---
type: JS Module
title: native.mjs
resource: npm/scripts/lib/native.mjs
docgen:
  crc: 49c1a451
  model: omlx/gemma-4-e4b-it-OptiQ-4bit
  tier: local-min
  score: 70
---

## Огляд

Loader napi-аддона `rules-core` (`crates/rules-napi` → `rules-core`) —
за зразком `llm-lib/lib/internal/native.mjs` (T2 фази 1,
`docs/specs/2026-07-30-rules-v2-rust-core-migration.md`).

Порядок пошуку (залежить від оточення — див. [`isSourceTree`]):
  1. N_RULES_NATIVE_ADDON — явний override шляху до аддона (dev / CI / тести).
  2. **Лише у вихідному дереві** (`<repoRoot>/crates/rules-napi/Cargo.toml`
     існує — тобто dev-машина або CI цього репо): локальна збірка
     `<repoRoot>/target/release|debug/` (сирий cdylib з
     `cargo build -p rules-napi`) та вивід `napi build` у `crates/rules-napi/`.
  3. Platform-підпакет `@7n/rules-<platform>-<arch>` (napi-артефакт
     `rules-napi.<triple>.node`).
  4. Той самий fallback на локальну збірку для НЕ-вихідного дерева
     (продакшен без підпакета) — поведінка така сама, як до фіксу.
  5. Інакше — зрозуміла помилка з підказкою `cargo build --release -p rules-napi`.

ЧОМУ порядок різний для вихідного дерева і проду (регресія 2026-08-03):
до фіксу підпакет стояв перед локальною збіркою БЕЗУМОВНО, тож у репо
(dev і CI) `cargo build -p rules-napi` збирав аддон, який loader потім НЕ
брав — вантажився **опублікований** `@7n/rules-<key>` із `node_modules`.
Будь-який тест нової native-поверхні перевіряв попередню збірку, а зелений
результат нічого не доводив. У CI це ще й недетерміноване: platform-пакет
потрапляє в `bun.lock` лише тоді, коли lock востаннє регенерували на тій
самій платформі (пор. `git show 581082ef:bun.lock` — `@7n/rules-linux-x64`
був у lock, тобто ubuntu-runner тестував registry-аддон 1.76.0).
Зворотний безумовний порядок («target завжди перший») теж хибний — у
користувача підпакет є **єдиним** авторитетним артефактом, запіненим
lockstep до версії `@7n/rules` і звіреним за `contractVersion()`; сторонній
`target/release/librules_napi.*`, що випадково опинився поруч зі
встановленим пакетом, не має його перебивати. Тому дискримінатор — не
евристика (`CI`, `NODE_ENV`), а факт наявності вихідних файлів аддона поруч
із loader-ом.

Аддон завантажується через `process.dlopen` — працює і для `.node`, і для
сирих cdylib (`.dylib`/`.so`/`.dll`), і під bun (не лише node). Результат
кешується (одне завантаження на процес). Без JS-fallback на неоголошеній
платформі — hard error, свідома межа v1 (darwin-arm64, linux-x64, win32-x64),
Р1 спеки + П3 (`docs/specs/2026-07-30-rules-v2-rust-core-migration.md`,
рішення О `docs/specs/2026-07-31-plugin-contract-v3-wasm-component.md` §3.4a).

Додатково (відмінність від `llm-lib`-loader-а): після dlopen звіряється
`addon.contractVersion()` з [`EXPECTED_CONTRACT_VERSION`] — розбіжність
означає несумісний DTO-контракт `rules-core` ⇄ `rules-napi` (Р10 спеки,
enforcement-точка за зразком `requiresPluginApi`). Звірка — один раз, при
першому завантаженні.

## Публічний API

- EXPECTED_CONTRACT_VERSION — Очікувана версія JSON DTO-контракту `rules-core` ⇄ `rules-napi` (Р10 спеки).
- resolveNativeAddon — Резолвить шлях до napi-аддона `rules-core`.
- loadNative — Кешований доступ до аддона (одне завантаження на процес). Після dlopen
звіряє `addon.contractVersion()` з [`EXPECTED_CONTRACT_VERSION`] — до
кешування, тож розбіжність кидає щоразу (не залипає в невдалому стані).

## Сценарії використання

- `npm/scripts/lib/tests/native.test.mjs` (resolveNativeAddon (порядок пошуку); resolveNativeAddon (вихідне дерево vs прод)) — N_RULES_NATIVE_ADDON має найвищий пріоритет; platform-підпакет: резолвиться @7n/rules-<key> з napi-суфіксом; linux-x64 мапиться на суфікс linux-x64-gnu; win32-x64 мапиться на суфікс win32-x64-msvc; dev-fallback: release-cdylib перемагає debug; ще 12

## Гарантії поведінки

- Власних операцій запису (ФС/БД) у файлі немає; виклики імпортованих модулів можуть писати.
- Кешує результати в межах одного прогону.
