# fix.mjs — точка входу правила `rego`

## Огляд

Файл `npm/rules/rego/fix.mjs` — це **entry-point правила `rego`** з кодової бази `@nitra/cursor`. Він є тонкою (thin) обгорткою навколо стандартного пайплайну виконання правила і виконує **дві ролі одночасно**:

1. **Library mode** — експортує функцію `run(ctx)`, яку CLI-оркестратор (`@nitra/cursor fix <id>`) викликає через динамічний `import`, передаючи спільний контекст прогону (наприклад, `walkCache`).
2. **Standalone mode** — якщо файл запущено напряму як CLI (наприклад, `bun npm/rules/rego/fix.mjs`), він виконує **повний еквівалент** команди `npx @nitra/cursor fix rego`: завантажує конфіг, застосовує whitelist, друкує summary та повертає процесові exit-code.

Внутрішня логіка правила (що саме перевіряється/виправляється у файлах `.rego`, OPA-політиках тощо) **не реалізована в цьому файлі** — вся послідовність кроків (`applies → JS-concerns → policy → mdc-refs`) інкапсульована у спільному хелпері `runStandardRule`. Сам `fix.mjs` лише делегує виконання, передаючи `import.meta.dirname` як точку відліку для конвенційного пошуку файлів-супутників правила (наприклад, `applies.mjs`, `check-*.mjs`, `policy/`, `mdc/`).

Така структура повторюється для кожного правила в `npm/rules/<id>/fix.mjs` — це шаблонний стандартний boilerplate, у якому індивідуальною є лише тека `<id>` (тут — `rego`).

## Експорти / API

| Назва | Тип                               | Опис                                                                                                                        |
| ----- | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `run` | `function(ctx?): Promise<number>` | Named-експорт. Library-точка входу для оркестратора. Запускає стандартний пайплайн правила `rego` у директорії цього файлу. |

Файл **не має default-експорту** і **не експортує** жодних інших символів. Side-effect на верхньому рівні модуля — перевірка `isRunAsCli(import.meta.url)`; якщо вона істинна, виконується `process.exit(await runRuleCli(...))`.

### Сигнатура `run`

```js
export function run(ctx)
```

- **Параметр `ctx`** — необов'язковий об'єкт типу `RuleContext` (тип імпортується через JSDoc із `../../scripts/lib/run-standard-rule.mjs`). У контексті, зокрема, передається `walkCache` — кеш обходу файлів, який дозволяє багаторазовим прогонам різних правил не дублювати дискові операції під час однієї сесії `@nitra/cursor fix`.
- **Повертає** — `Promise<number>`, де `0` означає **OK** (порушень не знайдено / усе виправлено), а `1` означає **наявність порушень** (failure). Це класична CLI-конвенція exit-code, узгоджена з типами в `runStandardRule`.
- **Прив'язка до директорії правила** — `import.meta.dirname` (нативне Node.js property для ESM, доступне у сучасних рантаймах) дає абсолютний шлях до теки, де лежить сам `fix.mjs`. Цей шлях передається у `runStandardRule` як «корінь правила», аби той знаходив супровідні файли (`applies.mjs`, `check-*.mjs`, `policy/*.rego`, `mdc/*` тощо) за конвенцією.

## Функції

### `run(ctx)`

- **Сигнатура:** `function run(ctx?: RuleContext): Promise<number>`
- **Параметри:**
  - `ctx` (опційний) — контекст прогону правила. Передається оркестратором (`runRuleCli` / `runMany`), коли правило запускається не самостійно, а в складі батча; містить, серед іншого, спільний `walkCache`. У standalone-режимі (див. нижче) `ctx` не передається — використовується дефолт, який сконструює `runStandardRule` самостійно.
- **Повертає:** `Promise<number>` — exit-code:
  - `0` — правило пройшло (`OK`).
  - `1` — правило виявило порушення (`violation`).
- **Side effects:**
  - Не має жодних побічних ефектів на рівні самої функції-обгортки.
  - Усі побічні ефекти (читання файлів, запис виправлень, друк у `stdout`/`stderr`, доступ до `walkCache`, мережа/диск) делеговані у `runStandardRule` і виконуються вже в його реалізації.
- **Чому така тонка обгортка?** Оркестратор `@nitra/cursor fix` під час батчового прогону **динамічно імпортує** `fix.mjs` кожного правила й викликає `run(ctx)`. Якби логіка пайплайну дублювалася в кожному `fix.mjs`, її було б неможливо змінювати централізовано — `runStandardRule` забезпечує єдину точку зміни поведінки для всіх правил.

### Standalone-блок (не функція, top-level код)

```js
if (isRunAsCli(import.meta.url)) {
  process.exit(await runRuleCli(import.meta.dirname))
}
```

- **Умова входу:** `isRunAsCli(import.meta.url)` повертає `true`, якщо модуль запущений як головний (а не імпортований іншим). Реалізація хелпера — у `npm/scripts/lib/run-rule-cli.mjs`.
- **Що виконує:** `runRuleCli(import.meta.dirname)` запускає **повний CLI** для цього правила — еквівалент `npx @nitra/cursor fix rego`. На відміну від `run(ctx)`, цей шлях додатково завантажує конфіг репозиторію, застосовує whitelist (ігнорування файлів/паттернів за конфігом), друкує summary прогону тощо.
- **`process.exit(...)`** — потрібен, щоб standalone-запуск повертав **коректний exit-code** у виклик shell/CI (інакше `bun` міг би завершитися з `0` навіть за провалу).
- **ESLint-винятки:** `n/no-process-exit` і `unicorn/no-process-exit` локально відключені коментарем — це свідома відмова на користь правильного CI/IDE-поведінки (standalone entry-point **зобов'язаний** повертати exit-code).
- **`await` на top-level:** використовується top-level `await`, що в ESM-модулях Node 20+ є штатною можливістю.

## Залежності

Файл імпортує лише **два внутрішніх модулі** (relative imports), зовнішніх npm-пакетів не використовує безпосередньо.

| Імпорт            | Звідки                                    | Призначення                                                                                                                        |
| ----------------- | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `isRunAsCli`      | `../../scripts/lib/run-rule-cli.mjs`      | Предикат: повертає `true`, якщо файл запущено напряму (а не імпортовано). Використовується у standalone-блоці.                     |
| `runRuleCli`      | `../../scripts/lib/run-rule-cli.mjs`      | Запуск **повного CLI** для одного правила (config-loading + whitelist + summary). Викликається в standalone-режимі.                |
| `runStandardRule` | `../../scripts/lib/run-standard-rule.mjs` | Запуск **стандартного пайплайну** правила: `applies → JS-concerns → policy → mdc-refs`. Викликається з library-функції `run(ctx)`. |

Додатково через JSDoc підтягується **тип** `RuleContext` (для типізації `ctx`) із того ж `../../scripts/lib/run-standard-rule.mjs`.

Інші **неявні залежності** (через `runStandardRule` / `runRuleCli`):

- файлова система — для пошуку файлів за конвенцією правила;
- сам `import.meta.dirname` — як спосіб самоідентифікації правила без хардкоду шляху.

## Потік виконання / Використання

### Сценарій 1: Library mode (виклик з оркестратора)

1. Користувач виконує `bun cursor fix` або `npx @nitra/cursor fix`.
2. Оркестратор знаходить усі активні правила (зокрема `rego`) і динамічно імпортує `npm/rules/rego/fix.mjs`.
3. Оркестратор викликає `run(ctx)`, де `ctx` містить, зокрема, `walkCache`.
4. `run(ctx)` повертає `runStandardRule(import.meta.dirname, ctx)`.
5. `runStandardRule` послідовно виконує стандартні етапи правила:
   - **applies** — визначає, до яких файлів застосовується правило (через супровідний `applies.mjs`);
   - **JS-concerns** — перевірки/виправлення, що стосуються JS-коду навколо правила;
   - **policy** — застосування полісі/політик (для `rego` це конкретні OPA-rego-фрагменти);
   - **mdc-refs** — звірка з посиланнями на правила в `.mdc`-документах.
6. Результат — `Promise<number>` (`0` або `1`) — повертається оркестратору, який агрегує підсумок по всіх правилах.

### Сценарій 2: Standalone mode (прямий запуск)

1. Розробник виконує, наприклад, `bun npm/rules/rego/fix.mjs` (або еквівалентно `node npm/rules/rego/fix.mjs`).
2. Виконується top-level код модуля; `isRunAsCli(import.meta.url)` повертає `true`.
3. Викликається `await runRuleCli(import.meta.dirname)` — це **повний CLI-флоу** для одного правила (включно з config-loading, whitelist та summary).
4. Результат — `number` (exit-code) — передається у `process.exit(...)`, який негайно завершує процес із цим кодом.
5. Shell / IDE / CI отримує exit-code: `0` (OK) або `1` (violations) — і може відповідно сигналізувати про результат.

### Як викликати `run` із зовнішнього коду (приклад)

```js
import { run } from './npm/rules/rego/fix.mjs'

const code = await run() // або: await run({ walkCache: myCache })
if (code !== 0) {
  // правило знайшло порушення
}
```

### Як викликати у standalone

```sh
bun npm/rules/rego/fix.mjs
echo $? # 0 — OK, 1 — violations
```

### Подвійна роль файлу

Дві ролі — `library` (іменований експорт `run`) та `standalone` (top-level CLI з `process.exit`) — навмисно поєднані в одному файлі: це дозволяє оркестратору не дублювати code path для одного й того самого правила, а розробнику — швидко прогнати окреме правило без запуску всього батча. Конвенція повторюється для всіх правил у `npm/rules/<id>/fix.mjs`, тож структура файлу `rego/fix.mjs` ідентична іншим правилам у каталозі — змінюється лише імпліцитна тека-«корінь» (`import.meta.dirname`).
