# `npm/rules/flow/fix.mjs`

## Огляд

Файл є точкою входу (entry point) правила `flow` у каталозі правил пакета `@nitra/cursor`. Правило `flow` належить до категорії **pure-doc contract-правил**: воно не має програмних concern-ів і фактично валідує лише супровідний документ `.mdc` (Markdown-Contract) у тій самій теці.

Модуль виконує дві основні задачі:

1. Експортує функцію `run(ctx)`, яка делегує всю роботу стандартному пайплайну виконання правил (`runStandardRule`). Цей експорт використовується диспетчером CLI `@nitra/cursor` (наприклад, командою `npx @nitra/cursor fix flow`) та внутрішніми оркестраторами правил.
2. Підтримує **standalone-режим** — якщо файл запущено напряму як CLI-скрипт (через `bun rules/flow/fix.mjs`), він викликає універсальний CLI-раннер `runRuleCli` і завершує процес коректним exit-кодом, придатним для CI/IDE-інтеграцій.

Стандартний пайплайн правила, який запускає `runStandardRule`, виглядає так (порядок фіксований):
`applies` → `JS-concerns` → `policy` → `mdc-refs`. Оскільки в цьому правилі програмних concern-ів немає, реальна перевірка зводиться до етапів `applies`, `policy` та валідації посилань у `.mdc`-файлі.

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

Модуль є **ES-модулем** (`.mjs`), має такі експорти:

| Експорт | Тип                                            | Призначення                                                                                                     |
| ------- | ---------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `run`   | `function(ctx?: RuleContext): Promise<number>` | Іменований експорт; основна точка входу для виконання правила. Викликається диспетчером правил `@nitra/cursor`. |

Side-effect верхнього рівня (не експорт, але частина публічної поведінки модуля):

- Якщо `import.meta.url` відповідає стартовому файлу процесу (`isRunAsCli`), модуль запускає `runRuleCli(import.meta.dirname)` та викликає `process.exit(...)` із кодом, який повернув раннер.

Default-експорту **немає**.

## Функції

### `run(ctx)`

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

- **Призначення.** Запустити стандартизований пайплайн правила `flow` для поточної теки (`import.meta.dirname` — каталог, де лежить `fix.mjs`, тобто `npm/rules/flow/`).
- **Параметри:**
  - `ctx` _(необов'язковий, `RuleContext`)_ — контекст прогону правил. Тип `RuleContext` імпортується з `../../scripts/lib/run-standard-rule.mjs`. У контексті, зокрема, передаються спільні структури між правилами — наприклад, `walkCache` (кеш обходу файлової системи), щоб не повторювати дорогі сканування при послідовному запуску багатьох правил.
- **Повертає:** `Promise<number>`.
  - `0` — правило виконано успішно, порушень не виявлено.
  - `1` — знайдено порушення (наприклад, проблеми у відповідному `.mdc`-файлі).
- **Side effects:** жодних безпосередньо у функції; усі побічні ефекти (читання FS, виведення повідомлень, агрегація знахідок) інкапсульовані всередині `runStandardRule`.
- **Контракт пайплайну, який вмикає `runStandardRule`:**
  - `applies` — визначає, чи правило застосовне до поточного проєкту/контексту.
  - `JS-concerns` — програмні перевірки JS/MJS-коду (у цьому правилі — фактично відсутні, бо правило pure-doc).
  - `policy` — політики/обмеження, спільні для всіх правил.
  - `mdc-refs` — валідація посилань і структури супровідного `.mdc`-файлу.

### IIFE-блок CLI-режиму (top-level, не функція)

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

- **Призначення.** Дозволити запуск файлу як самостійного скрипта (`bun rules/flow/fix.mjs`), повністю еквівалентний `npx @nitra/cursor fix flow`.
- **Семантика умови.** `isRunAsCli(import.meta.url)` повертає `true`, якщо цей файл є стартовим модулем процесу (а не імпортованим іншим модулем). Це стандартний у пакеті паттерн dual-mode (бібліотека + CLI).
- **Параметри `runRuleCli`:** передається `import.meta.dirname` — абсолютний шлях до каталогу `npm/rules/flow/`. Раннер сам читає `meta.json`/`fix.mjs`/`.mdc` із цього каталогу.
- **Завершення.** `process.exit(...)` із цілочисельним exit-кодом, який повернув `runRuleCli`. Це важливо для CI/IDE: ненульовий код → провалена перевірка.
- **ESLint-винятки в коді:** рядок із `process.exit` навмисно вимикає правила `n/no-process-exit` та `unicorn/no-process-exit` через коментар `// eslint-disable-next-line ... -- standalone entry-point має повертати exit-code для CI/IDE`. Це задокументоване відхилення: для standalone CLI вихід кодом потрібен.

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

Усі залежності — **внутрішні**, з підкаталогу `scripts/lib/` пакета:

| Імпорт            | Шлях                                      | Що дає                                                                                                                                                 |
| ----------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `isRunAsCli`      | `../../scripts/lib/run-rule-cli.mjs`      | Предикат: чи `import.meta.url` модуля збігається зі стартовим файлом процесу (визначає standalone-запуск).                                             |
| `runRuleCli`      | `../../scripts/lib/run-rule-cli.mjs`      | Універсальний CLI-раннер для одного правила: парсить аргументи, готує контекст, викликає `run`, форматує вивід, повертає exit-код.                     |
| `runStandardRule` | `../../scripts/lib/run-standard-rule.mjs` | Стандартний пайплайн правил `applies → JS-concerns → policy → mdc-refs`; читає сусідній `.mdc`, виконує перевірки, агрегує результат у код повернення. |

Зовнішніх (npm-пакетних) залежностей файл не імпортує.

Опосередковано (через `runStandardRule`) використовуються:

- Сусідні файли у каталозі `npm/rules/flow/` — насамперед `.mdc` (Markdown-Contract правила) та `meta.json` (метадані правила), якщо такі присутні.
- Спільна інфраструктура з `npm/scripts/lib/` (логування, форматування результатів, утиліти FS-обходу).

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

### Сценарій 1. Виклик з диспетчера правил (типовий)

1. CLI `@nitra/cursor` (або інший оркестратор) визначає, що потрібно запустити правило `flow` у режимі `fix`.
2. Диспетчер імпортує `fix.mjs` як модуль і викликає `run(ctx)`, передаючи спільний `ctx` (наприклад, із заповненим `walkCache`).
3. `run` делегує виконання `runStandardRule(import.meta.dirname, ctx)`.
4. `runStandardRule` послідовно виконує етапи `applies → JS-concerns → policy → mdc-refs`.
5. Повертається `Promise<number>` — `0` або `1`.
6. Оркестратор агрегує коди повернення з усіх правил.

### Сценарій 2. Standalone CLI-запуск

Команда:

```bash
bun npm/rules/flow/fix.mjs
```

(еквівалентно `npx @nitra/cursor fix flow`).

1. Інтерпретатор виконує модуль як стартовий файл процесу.
2. Рядок `if (isRunAsCli(import.meta.url))` повертає `true`.
3. Викликається `await runRuleCli(import.meta.dirname)`:
   - Раннер парсить CLI-аргументи (флаги, шляхи).
   - Готує `ctx` для одиничного запуску.
   - Викликає `run` цього ж модуля (або еквівалентний внутрішній механізм через `runStandardRule`).
   - Форматує вивід (звіт про знахідки, кольори, summary).
4. `process.exit(<код>)` — процес завершується з кодом раннера: `0` за успіху, `1` за порушень.

### Сценарій 3. Імпорт у тестах / скриптах

Можливий імпорт `run` для unit-/інтеграційного тестування або для написання кастомних оркестраторів:

```js
import { run } from '@nitra/cursor/rules/flow/fix.mjs'

const code = await run() // або await run(customCtx)
if (code !== 0) {
  // обробити порушення
}
```

У цьому випадку top-level блок `if (isRunAsCli(...))` **не спрацьовує** (файл імпортовано, а не запущено напряму), отже `process.exit` не викликається — побічних ефектів на процес немає.

### Контракт повернення

| Код | Значення                                                                                         | Дія викликача                                    |
| --- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------ |
| `0` | Правило застосоване й порушень немає; або правило не застосовне (etap `applies` повернув false). | Продовжувати конвеєр.                            |
| `1` | Знайдено порушення (зазвичай — невідповідність `.mdc`-файлу контракту).                          | CI має зафейлити збірку; IDE — показати помилку. |

## Особливості реалізації / Нотатки

- **Pure-doc rule.** За коментарем у JSDoc функції `run`, правило `flow` не має програмних concern-ів. Це означає, що каталог `npm/rules/flow/` навмисно не містить (або містить лише мінімум) перевірок поверх JS-коду — основний контракт описано в `.mdc`-файлі, який і валідується через підетап `mdc-refs`.
- **Стандартизований шаблон.** Файл написаний за єдиним шаблоном для всіх правил пакета: тонкий wrapper над `runStandardRule` + опційний standalone CLI-блок. Це спрощує генерацію/підтримку правил і робить їх взаємозамінними для оркестратора.
- **`import.meta.dirname`** використовується замість `__dirname` (який недоступний в ES-модулях) і передається у `runStandardRule`/`runRuleCli` як локалізатор каталогу правила.
- **Top-level `await`** у блоці `process.exit(await runRuleCli(...))` дозволений, тому що модуль є ES-модулем (`.mjs`).
- **ESLint-disable з обґрунтуванням.** Вимкнення `n/no-process-exit` та `unicorn/no-process-exit` зроблено з явним коментарем-обґрунтуванням після `--`, що відповідає внутрішнім js-lint правилам пакета (вимога описувати причину disable).

## Rebuild Test (контрольна реконструкція)

Спираючись виключно на цю документацію, файл `fix.mjs` можна відновити так:

1. Створити ES-модуль `npm/rules/flow/fix.mjs`.
2. Імпортувати з `../../scripts/lib/run-rule-cli.mjs` іменовані експорти `isRunAsCli` та `runRuleCli`.
3. Імпортувати з `../../scripts/lib/run-standard-rule.mjs` іменований експорт `runStandardRule`.
4. Експортувати функцію `run(ctx)`, яка повертає результат виклику `runStandardRule(import.meta.dirname, ctx)`. Контракт: `Promise<number>` (`0` — OK, `1` — порушення). JSDoc описує пайплайн `applies → JS-concerns → policy → mdc-refs` та зазначає, що правило pure-doc.
5. Додати top-level блок: `if (isRunAsCli(import.meta.url)) process.exit(await runRuleCli(import.meta.dirname))`.
6. Поряд із `process.exit` поставити `eslint-disable-next-line` для `n/no-process-exit` та `unicorn/no-process-exit` з обґрунтуванням, що standalone entry-point має повертати exit-code для CI/IDE.

Такий файл буде функціонально еквівалентним оригіналу.
