@nitra/cursor 5.3.4 → 5.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/.claude-template/settings.template.json +2 -2
  2. package/.pi-template/extensions/n-cursor-adr/docs/index.md +13 -24
  3. package/CHANGELOG.md +11 -0
  4. package/bin/n-cursor.js +43 -22
  5. package/lib/docs/models.md +29 -18
  6. package/lib/docs/omlx-trace.md +51 -0
  7. package/lib/docs/omlx.md +31 -15
  8. package/lib/omlx.mjs +2 -5
  9. package/package.json +1 -1
  10. package/rules/abie/docs/fix.md +17 -11
  11. package/rules/adr/docs/fix.md +25 -140
  12. package/rules/bun/docs/fix.md +18 -151
  13. package/rules/capacitor/docs/fix.md +16 -13
  14. package/rules/capacitor/js/docs/platforms.md +31 -43
  15. package/rules/changelog/docs/fix.md +25 -169
  16. package/rules/ci4/docs/fix.md +11 -14
  17. package/rules/doc-files/doc-files.mdc +60 -0
  18. package/rules/doc-files/docs/fix.md +31 -0
  19. package/rules/doc-files/fix.mjs +19 -0
  20. package/{skills → rules}/doc-files/js/docgen-extract.mjs +42 -19
  21. package/{skills → rules}/doc-files/js/docgen-ignore.mjs +2 -1
  22. package/{skills → rules}/doc-files/js/docgen-scan.mjs +9 -1
  23. package/{skills → rules}/doc-files/js/docs/docgen-crc.md +1 -1
  24. package/{skills → rules}/doc-files/js/docs/docgen-extract-anchors.md +1 -1
  25. package/{skills → rules}/doc-files/js/docs/docgen-extract.md +2 -2
  26. package/{skills → rules}/doc-files/js/docs/docgen-files-batch.md +1 -1
  27. package/{skills → rules}/doc-files/js/docs/docgen-gen.md +1 -1
  28. package/{skills → rules}/doc-files/js/docs/docgen-ignore.md +4 -4
  29. package/rules/doc-files/js/docs/docgen-prompts.md +39 -0
  30. package/rules/doc-files/js/docs/docgen-scan.md +54 -0
  31. package/rules/doc-files/js/docs/lint.md +36 -0
  32. package/rules/doc-files/js/docs/units-js.md +31 -0
  33. package/rules/doc-files/js/docs/units-rs.md +35 -0
  34. package/rules/doc-files/js/docs/units.md +30 -0
  35. package/rules/doc-files/js/lint.mjs +96 -0
  36. package/{skills → rules}/doc-files/js/units-rs.mjs +37 -17
  37. package/rules/doc-files/lint/docs/lint.md +37 -0
  38. package/rules/doc-files/lint/lint.mjs +105 -0
  39. package/rules/doc-files/meta.json +1 -0
  40. package/rules/docker/docs/fix.md +21 -161
  41. package/rules/efes/docs/fix.md +23 -194
  42. package/rules/feedback/docs/fix.md +10 -8
  43. package/rules/ga/docs/fix.md +10 -5
  44. package/rules/graphql/docs/fix.md +23 -119
  45. package/rules/hasura/docs/fix.md +19 -5
  46. package/rules/hasura/js/docs/internal_urls.md +34 -307
  47. package/rules/image-avif/docs/fix.md +16 -127
  48. package/rules/image-compress/docs/fix.md +20 -141
  49. package/rules/image-compress/js/docs/package_setup.md +22 -182
  50. package/rules/js-bun-db/docs/fix.md +23 -139
  51. package/rules/js-bun-db/js/docs/safety.md +33 -221
  52. package/rules/js-bun-redis/docs/fix.md +25 -114
  53. package/rules/js-bun-redis/js/docs/imports.md +18 -166
  54. package/rules/js-lint/docs/fix.md +30 -108
  55. package/rules/js-lint/js/docs/lint-findings.md +37 -17
  56. package/rules/js-lint/js/docs/lint.md +22 -238
  57. package/rules/js-lint/js/docs/tooling.md +34 -331
  58. package/rules/js-lint-ci/docs/fix.md +16 -149
  59. package/rules/js-lint-ci/js/docs/lint.md +16 -136
  60. package/rules/js-mssql/docs/fix.md +18 -123
  61. package/rules/js-mssql/js/docs/deps.md +28 -251
  62. package/rules/js-run/docs/fix.md +23 -138
  63. package/rules/js-run/js/docs/runtime.md +24 -378
  64. package/rules/k8s/docs/fix.md +18 -123
  65. package/rules/nginx-default-tpl/docs/fix.md +22 -118
  66. package/rules/nginx-default-tpl/js/docs/template.md +38 -360
  67. package/rules/npm-module/docs/fix.md +27 -89
  68. package/rules/npm-module/js/docs/header_doc_pointer.md +15 -15
  69. package/rules/npm-module/js/docs/package_structure.md +36 -258
  70. package/rules/npm-module/js/docs/rule_meta.md +25 -127
  71. package/rules/npm-module/js/docs/skill_meta.md +18 -180
  72. package/rules/php/docs/fix.md +21 -98
  73. package/rules/php/js/docs/tooling.md +20 -143
  74. package/rules/python/docs/fix.md +25 -157
  75. package/rules/python/js/docs/applies.md +20 -98
  76. package/rules/python/js/docs/tooling.md +27 -144
  77. package/rules/rego/docs/fix.md +24 -112
  78. package/rules/rego/js/docs/applies.md +20 -164
  79. package/rules/rego/js/docs/lint.md +15 -110
  80. package/rules/release/docs/fix.md +16 -114
  81. package/rules/rust/docs/fix.md +24 -119
  82. package/rules/rust/js/docs/applies.md +20 -129
  83. package/rules/security/docs/fix.md +21 -78
  84. package/rules/security/js/docs/sample_secret.md +23 -182
  85. package/rules/security/js/docs/trufflehog.md +19 -128
  86. package/rules/style-lint/docs/fix.md +16 -150
  87. package/rules/style-lint/js/docs/lint.md +21 -172
  88. package/rules/style-lint/js/docs/tooling.md +19 -184
  89. package/rules/tauri/docs/fix.md +26 -152
  90. package/rules/tauri/js/docs/cargo_mutants_config.md +21 -159
  91. package/rules/tauri/js/docs/tooling.md +20 -217
  92. package/rules/test/docs/fix.md +19 -127
  93. package/rules/test/js/data/stryker_config/docs/stryker.config.baseline.md +15 -127
  94. package/rules/test/js/data/stryker_config/docs/stryker.config.vue.baseline.md +17 -153
  95. package/rules/test/js/docs/cargo_mutants_config.md +24 -164
  96. package/rules/test/js/docs/location.md +24 -126
  97. package/rules/test/js/docs/no-process-chdir.md +20 -151
  98. package/rules/test/js/docs/no-relative-fs-path.md +24 -261
  99. package/rules/test/js/docs/stryker_config.md +48 -148
  100. package/rules/test/js/docs/vitest-config-pool-forks.md +21 -164
  101. package/rules/text/docs/fix.md +25 -113
  102. package/rules/text/js/docs/forbidden-prettier.md +21 -132
  103. package/rules/text/js/docs/formatting.md +60 -251
  104. package/rules/text/js/docs/lint.md +17 -114
  105. package/rules/vue/docs/fix.md +25 -118
  106. package/rules/vue/js/docs/packages.md +25 -323
  107. package/rules/worktree/docs/fix.md +31 -150
  108. package/scripts/coverage-classify/docs/index.md +23 -209
  109. package/scripts/coverage-classify/docs/verdict-schema.md +14 -159
  110. package/scripts/dispatcher/docs/trace.md +35 -0
  111. package/scripts/docs/auto-rules.md +37 -361
  112. package/scripts/docs/lint-cli.md +12 -13
  113. package/scripts/docs/post-tool-use-fix.md +16 -15
  114. package/scripts/docs/skills-cli.md +26 -23
  115. package/scripts/docs/sync-claude-config.md +94 -34
  116. package/scripts/docs/worktree-cli.md +11 -34
  117. package/scripts/lib/docs/assert-project-root.md +14 -16
  118. package/scripts/lib/docs/changed-files.md +24 -139
  119. package/scripts/lib/docs/discover-check-rules-from-cursor.md +14 -146
  120. package/scripts/lib/docs/rule-predicates.md +20 -17
  121. package/scripts/lib/docs/run-rule-cli.md +14 -18
  122. package/scripts/lib/docs/run-rule.md +13 -20
  123. package/scripts/lib/docs/run-standard-rule.md +12 -15
  124. package/scripts/lib/docs/sync-gitignore-worktree.md +15 -18
  125. package/scripts/lib/rule-predicates.mjs +1 -1
  126. package/scripts/sync-claude-config.mjs +4 -1
  127. package/scripts/utils/docs/with-lock.md +19 -12
  128. package/scripts/utils/with-lock.mjs +4 -2
  129. package/skills/doc-aggregate/SKILL.md +2 -2
  130. package/skills/doc-aggregate/js/docgen-ignore.mjs +6 -6
  131. package/skills/doc-aggregate/js/docs/docgen-ignore.md +1 -1
  132. package/skills/doc-aggregate/js/docs/docgen-scan.md +78 -0
  133. package/skills/doc-files/.changes/260612-0031.md +5 -0
  134. package/skills/doc-files/.changes/260612-0036.md +5 -0
  135. package/skills/doc-files/.changes/260612-0114.md +5 -0
  136. package/skills/doc-files/SKILL.md +6 -6
  137. package/skills/fix/js/docs/llm-worker.md +17 -15
  138. package/skills/fix/js/docs/orchestrator.md +30 -23
  139. package/skills/fix/js/docs/t0.md +26 -16
  140. package/skills/start-check/js/docs/check.md +26 -22
  141. package/skills/taze/js/docs/diff.md +44 -20
  142. package/skills/doc-files/js/docs/docgen-prompts.md +0 -32
  143. package/skills/doc-files/js/docs/docgen-scan.md +0 -25
  144. package/skills/doc-files/js/docs/units-rs.md +0 -35
  145. /package/{skills → rules}/doc-files/js/docgen-crc.mjs +0 -0
  146. /package/{skills → rules}/doc-files/js/docgen-extract-anchors.mjs +0 -0
  147. /package/{skills → rules}/doc-files/js/docgen-files-batch.mjs +0 -0
  148. /package/{skills → rules}/doc-files/js/docgen-gen.mjs +0 -0
  149. /package/{skills → rules}/doc-files/js/docgen-prompts.mjs +0 -0
  150. /package/{skills → rules}/doc-files/js/units-js.mjs +0 -0
  151. /package/{skills → rules}/doc-files/js/units.mjs +0 -0
@@ -1,133 +1,37 @@
1
1
  ---
2
2
  docgen:
3
3
  source: npm/rules/nginx-default-tpl/fix.mjs
4
- crc: 12fc1644
4
+ crc: 38cf876b
5
+ score: 100
5
6
  ---
6
7
 
7
- # `fix.mjs` — точка входу правила `nginx-default-tpl`
8
+ # fix.mjs
8
9
 
9
10
  ## Огляд
10
11
 
11
- Модуль `npm/rules/nginx-default-tpl/fix.mjs` є **точкою входу** (entry-point) для правила `nginx-default-tpl` у CLI-інструменті `@nitra/cursor`. Файл реалізує патерн «двох ролей» (dual-role module) для одного rule-файлу:
12
+ Виконує застосування політики безпеки до посилань mdc.
12
13
 
13
- 1. **Library mode** експортує функцію `run(ctx)`, яку викликає зовнішня orchestration-логіка (наприклад, batch-прогін усіх правил через `n-cursor fix`).
14
- 2. **Standalone mode** — якщо файл запущено напряму через `bun rules/nginx-default-tpl/fix.mjs`, то відпрацьовує повний CLI-цикл (завантаження конфігурації, whitelist, summary) і завершує процес з відповідним exit-кодом для CI/IDE.
14
+ Виконує повний еквівалент команди `npx @nitra/cursor fix <id>`, включаючи завантаження конфігурації, перевірку дозволених елементів та підбиття підсумку.
15
15
 
16
- Сам файл **не містить специфічної логіки правила** — він є тонкою обгорткою (thin wrapper) над `runStandardRule`, яка делегує виконання стандартному пайплайну: `applies → JS-concerns → policy → mdc-refs`. Конкретна перевірена/виправлювана поведінка правила `nginx-default-tpl` описана в сусідніх артефактах теки (`check-*.mjs`, `.mdc`, конфіги), а цей файл забезпечує лише уніфікований інтерфейс запуску.
16
+ ## Поведінка
17
17
 
18
- ## Експорти / API
18
+ 1. Запуск правила.
19
+ * Приймає контекст прогону.
20
+ * Виконує застосування JS-занепокоєних до політики до посилань mdc.
21
+ * Повертає результат.
22
+ 2. Запуск правила у режимі CLI.
23
+ * Викликається через оркестрацію CLI.
24
+ * Виконує повний еквівалент команди `npx @nitra/cursor fix <id>`.
25
+ * Виконує завантаження конфігурації, перевірку дозволених елементів та підбиття підсумку.
26
+ * Встановлює код виходу процесу на основі результату.
19
27
 
20
- | Експорт | Тип | Призначення |
21
- | ------- | --------------------------------- | --------------------------------------------------------------------------------------------- |
22
- | `run` | `function(ctx?): Promise<number>` | Library-API: виконує стандартний пайплайн правила; повертає exit-код (0 — OK, 1 — порушення). |
28
+ ## Публічний API
23
29
 
24
- Файл також має **side-effect top-level await** на рівні модуля: при запуску як CLI він викликає `process.exit(await runRuleCli(...))`. Жодних інших іменованих експортів, default-експорту чи реекспортів немає.
30
+ run запускає правило: applies JS-concerns policy mdc-refs (через runStandardRule).
31
+ Library mode — викликається CLI orchestration через `import + run`.
25
32
 
26
- ## Функції
33
+ ## Гарантії поведінки
27
34
 
28
- ### `run(ctx)`
29
-
30
- ```js
31
- /**
32
- *
33
- */
34
- export function run(ctx) {
35
- return runStandardRule(import.meta.dirname, ctx)
36
- }
37
- ```
38
-
39
- - **Сигнатура:** `run(ctx?: RuleContext): Promise<number>`
40
- - **Параметри:**
41
- - `ctx` _(необов’язковий)_ — об’єкт контексту прогону типу `RuleContext` з `../../scripts/lib/run-standard-rule.mjs`. Зазвичай містить кеші (наприклад, `walkCache` — спільний результат обходу файлової системи між кількома правилами), щоб не повторювати дорогі операції під час batch-прогону. Якщо `ctx` не передано — `runStandardRule` створює власне ізольоване оточення.
42
- - **Повертає:** `Promise<number>`
43
- - `0` — правило не знайшло порушень (або всі порушення були автоматично виправлені).
44
- - `1` — правило знайшло порушення, які потрібно ескалувати у CI / IDE.
45
- - **Side effects:**
46
- - Запускає стандартний пайплайн `runStandardRule`, який залежно від реалізації може **читати файли** проєкту, **писати fix-патчі**, **друкувати summary** у stdout/stderr.
47
- - Сам по собі `run` процес **не завершує** (`process.exit` викликається лише в standalone-гілці нижче).
48
- - **Як обчислюється цільова тека:** перший аргумент `import.meta.dirname` — це абсолютний шлях до теки правила (`.../npm/rules/nginx-default-tpl/`). `runStandardRule` за цим шляхом резолвить supporting-артефакти: `meta.json`, `check-*.mjs`, MDC-документ, applies-конфіг тощо. Тому **категорично не можна** замінювати `import.meta.dirname` на CWD або жорстко закодований шлях — це зламає інкапсуляцію правила в монорепо.
49
-
50
- ### Top-level CLI-блок (не функція, а імперативний side-effect)
51
-
52
- ```js
53
- if (isRunAsCli(import.meta.url)) {
54
- process.exit(await runRuleCli(import.meta.dirname))
55
- }
56
- ```
57
-
58
- - **Умова `isRunAsCli(import.meta.url)`** — детектор того, чи модуль є entry-point (`node fix.mjs` / `bun fix.mjs`), а не імпорт-залежністю. Реалізація утиліти стандартизована в `scripts/lib/run-rule-cli.mjs` (зазвичай порівнює `import.meta.url` з `process.argv[1]`).
59
- - **`runRuleCli(import.meta.dirname)`** виконує **повний CLI-еквівалент** команди `npx @nitra/cursor fix nginx-default-tpl`:
60
- - завантаження конфігурації проєкту,
61
- - застосування whitelist / overrides,
62
- - друк summary-репорта.
63
- - **`process.exit(...)`** з результатом `runRuleCli` (number) повертає **exit-code в shell**, щоб CI/IDE могли інтерпретувати «червоний» / «зелений» прогін. Лінт-директиви `n/no-process-exit` і `unicorn/no-process-exit` свідомо вимкнені рядковим коментарем — це задокументоване виключення для **standalone entry-point**, де exit-code є частиною контракту.
64
- - **Top-level `await`** дозволений у ESM-модулях (`.mjs`) і використовується для уникнення обгортки `(async () => { … })()`.
65
-
66
- ## Залежності
67
-
68
- ### Внутрішні (relative imports)
69
-
70
- | Імпорт | Із | Що використовується |
71
- | ----------------- | ----------------------------------------- | ---------------------------------------------------------------------------------------------- |
72
- | `isRunAsCli` | `../../scripts/lib/run-rule-cli.mjs` | Детектор entry-point — повертає `true`, якщо поточний модуль викликаний напряму CLI-рантаймом. |
73
- | `runRuleCli` | `../../scripts/lib/run-rule-cli.mjs` | Standalone-orchestration: повний CLI-цикл одного правила (config + whitelist + summary). |
74
- | `runStandardRule` | `../../scripts/lib/run-standard-rule.mjs` | Library-pipeline правила: `applies → JS-concerns → policy → mdc-refs`. |
75
-
76
- Шляхи відносні до `npm/rules/nginx-default-tpl/`; `../../scripts/lib/` резолвиться у `npm/scripts/lib/`.
77
-
78
- ### Зовнішні / runtime
79
-
80
- - **Bun / Node.js ESM** — для `import.meta.dirname` потрібен рантайм з підтримкою `import.meta.dirname` (Node ≥ 20.11 або сучасний Bun). На старіших Node цей геттер повертає `undefined`, що зламає резолв шляхів.
81
- - **`process.exit`** — глобал Node/Bun рантайму; використовується лише у CLI-гілці.
82
-
83
- ### Типи (JSDoc)
84
-
85
- `@param {import('../../scripts/lib/run-standard-rule.mjs').RuleContext}` — тип контексту імпортується через JSDoc-тип-імпорт із того ж модуля `runStandardRule`. Це **не runtime-залежність** — лише підказка для TS/IDE.
86
-
87
- ## Потік виконання / Використання
88
-
89
- ### Сценарій 1 — Library mode (batch run)
90
-
91
- ```js
92
- // Викликається з npm/scripts/run-all-rules.mjs або інших orchestrators
93
- import { run } from '@nitra/cursor/rules/nginx-default-tpl/fix.mjs'
94
-
95
- const exitCode = await run({ walkCache })
96
- if (exitCode !== 0) violatedRules.push('nginx-default-tpl')
97
- ```
98
-
99
- - Orchestrator передає спільний `walkCache` (чи інший контекст), щоб уникнути повторного обходу файлової системи між правилами.
100
- - `run` повертає число — orchestrator сам вирішує, чи робити `process.exit` і коли.
101
-
102
- ### Сценарій 2 — Standalone (debug / IDE / CI per-rule)
103
-
104
- ```bash
105
- bun npm/rules/nginx-default-tpl/fix.mjs
106
- # еквівалент:
107
- npx @nitra/cursor fix nginx-default-tpl
108
- ```
109
-
110
- - Bun завантажує файл, бачить `isRunAsCli(...) === true`, входить у CLI-гілку.
111
- - `runRuleCli` сам викликає **той самий** `runStandardRule` під капотом (через `run`-експорт або прямо), але додає завантаження конфігу й whitelist-фільтри.
112
- - Процес завершується з відповідним exit-кодом — `0` для CI «зелений», `1` для «червоний».
113
-
114
- ### Логічна послідовність всередині `runStandardRule`
115
-
116
- Згідно з JSDoc-коментарем у `run`, стандартний пайплайн послідовно виконує чотири фази:
117
-
118
- 1. **`applies`** — визначення множини файлів, до яких застосовне правило (за glob-патернами з конфігу правила).
119
- 2. **`JS-concerns`** — JS/TS-специфічні перевірки (синтаксис, AST, imports), якщо релевантно для правила.
120
- 3. **`policy`** — застосування policy-логіки правила (для `nginx-default-tpl` — перевірка/виправлення дефолт-шаблону nginx-конфігурації).
121
- 4. **`mdc-refs`** — крос-валідація з `.mdc`-документом правила (наявність refs, узгодженість прикладів тощо).
122
-
123
- Деталі та точна семантика фаз залежать від реалізації `runStandardRule` й артефактів самого правила (`check-*.mjs`, `mdc`-файл).
124
-
125
- ### Контракти й нюанси
126
-
127
- - **Ідемпотентність:** не гарантована на рівні цього wrapper-а — залежить від `runStandardRule`. Зазвичай fix-режим записує патчі на диск і повторний виклик уже повертає `0`.
128
- - **Concurrency:** глобальне правило монорепо забороняє паралельний запуск кількох правил/лінтерів одночасно (див. CLAUDE.md «Лінт і ESLint (без паралельних запусків)»). У library-режимі orchestrator повинен серіалізувати виклики.
129
- - **Захищеність від помилок:** wrapper не має власних `try/catch` — будь-який throw з `runStandardRule` чи `runRuleCli` поширюється нагору. У standalone-режимі неперехоплений throw призведе до ненульового exit-коду рантайму (без явного `process.exit(1)`).
130
-
131
- ## Rebuild Test
132
-
133
- Документ описує **public-контракт** файлу (експорт `run`, його сигнатуру/повернення/побічні ефекти, CLI-гілку з `process.exit`, відносні залежності й послідовність фаз `applies → JS-concerns → policy → mdc-refs`). Цього достатньо для відновлення семантично-еквівалентного wrapper-файлу: створити `.mjs` модуль з ESM-імпортами трьох утиліт, експортувати `run(ctx)`, який повертає `runStandardRule(import.meta.dirname, ctx)`, і додати CLI-блок з `isRunAsCli` + `process.exit(await runRuleCli(import.meta.dirname))`. Сама бізнес-логіка правила `nginx-default-tpl` тут не міститься — вона делегована стандартному пайплайну й артефактам теки.
35
+ - Read-only: файл не виконує операцій запису у файлову систему.
36
+ - Кешує результати в межах одного прогону.
37
+ - Не звертається до мережі.
@@ -1,378 +1,56 @@
1
- # template.mjs — перевірка nginx-шаблону (правило `nginx-default-tpl`)
2
-
3
- ## Огляд
4
-
5
- Модуль реалізує JS-частину правила `nginx-default-tpl.mdc`. Він шукає у проєкті файли `default.conf.template` (типовий nginx-шаблон, який рендериться `envsubst` під час старту контейнера) і пов'язані з ними артефакти, після чого:
6
-
7
- 1. виконує міграції старих/невалідних артефактів у канонічний вигляд:
8
- - перейменовує застаріле `default.tpl.conf` у `default.conf.template` (або перезаписує контентом, якщо ціль уже є, після чого видаляє джерело);
9
- - замінює невалідну директиву `error_log off;` на канонічну `error_log /dev/null crit;`;
10
- 2. перевіряє вміст кожного `default.conf.template` на наявність обов'язкових директив (порт `8080`, `/healthz`, `gzip_static`, `try_files`, `server_tokens off`, `sendfile_max_chunk` тощо) і **відсутність** будь-яких `*_pass` директив (`proxy_pass`, `fastcgi_pass`, `grpc_pass`, `uwsgi_pass` тощо — бекенд-логіка має бути винесена в HTTPRoute на рівні k8s);
11
- 3. перевіряє, що поруч із шаблоном є щонайменше один `*.ini` (values для середовища), а кожен ключ з ini використовується в шаблоні як `$KEY` (контракт `envsubst`);
12
- 4. перевіряє, що в будь-якому `Dockerfile` / `Containerfile` репозиторію є крок стиснення статики `find … /usr/share/nginx/html … gzip -k` і виклик `envsubst` з `default.conf.template`;
13
- 5. делегує валідацію `.vscode/extensions.json` і `.vscode/settings.json` rego-пакетам `nginx_default_tpl.vscode_extensions` / `nginx_default_tpl.vscode_settings` через `runConftestBatch`.
14
-
15
- Перевірка є **умовною**: якщо в дереві (після міграції) немає жодного `default.conf.template`, увесь крок пропускається з exit-кодом `0`. Приклад HTTPRoute з правила залишений лише для рев'ю — функція `httpRouteMatchesNginxDefaultTpl` присутня в експорті як інструмент для тестів і потенційного майбутнього вузького застосування, але в `check()` зараз **не** викликається (через різнорідність схем маршрутизації в продукті).
16
-
17
- ### Канонічні константи
18
-
19
- Літерали правила, винесені в регулярні вирази й рядки модуля:
20
-
21
- - порт listen: `8080`;
22
- - кореневий каталог статики: `/usr/share/nginx/html`;
23
- - директива заміни невалідного логу: `error_log /dev/null crit;`;
24
- - розширення для стиснення: `*.{js,css}` (через `GZIP_EXTENSION_RE`).
25
-
26
- ## Експорти / API
27
-
28
- Усі експорти — іменовані; default-експорту немає.
29
-
30
- | Символ | Тип | Призначення |
31
- | -------------------------------------------------- | -------------- | -------------------------------------------------------------------------------------- |
32
- | `findDefaultConfTemplatePaths(root, ignorePaths?)` | async function | Збирає абсолютні шляхи всіх `default.conf.template` у дереві (виключаючи `fixtures/`). |
33
- | `migrateDefaultTplConfFiles(root, ignorePaths?)` | async function | Мігрує застарілі `default.tpl.conf` у `default.conf.template`. |
34
- | `migrateErrorLogOffDirective(root, ignorePaths?)` | async function | Замінює `error_log off;` на `error_log /dev/null crit;` у всіх знайдених шаблонах. |
35
- | `parseIniVariableNames(iniText)` | function | Витягує імена ключів з тексту `.ini` (рядки виду `KEY=value`). |
36
- | `nginxTemplateViolations(content)` | function | Повертає перше порушення канону шаблону або `null`. |
37
- | `httpRouteMatchesNginxDefaultTpl(manifest)` | function | Перевіряє структуру `HTTPRoute` (зараз не використовується в `check()`). |
38
- | `iniKeysMissingInTemplate(keys, template)` | function | Повертає повідомлення про першу ключ-змінну з `*.ini`, якої немає в шаблоні як `$KEY`. |
39
- | `check(cwd?)` | async function | Головна точка входу правила; повертає `0`/`1` як код виходу. |
40
-
41
- Внутрішні (не експортовані) хелпери:
42
-
43
- - `dockerfileHasGzipStaticPipeline(dockerfileContent)` — детектор стиснення статики в Dockerfile;
44
- - `dockerfileHasEnvsSubstTemplate(dockerfileContent)` — детектор кроку `envsubst` із шаблоном;
45
- - `checkTemplateFile(abs, root, passFn, failFn)` — перевірка одного шаблону й сусідніх `*.ini`;
46
- - `checkDockerfiles(root, ignorePaths, passFn, failFn)` — перевірка Dockerfile'ів проєкту;
47
- - `checkVscodeNginx(passFn, failFn, cwd)` — делегування у rego-перевірки VSCode-конфігів.
48
-
49
- ## Функції
50
-
51
- ### `findDefaultConfTemplatePaths(root, ignorePaths = [])`
52
-
53
- **Сигнатура:** `async (root: string, ignorePaths?: string[]) => Promise<string[]>`
54
-
55
- **Параметри:**
56
-
57
- - `root` — абсолютний корінь обходу (зазвичай `process.cwd()` репозиторію);
58
- - `ignorePaths` — список абсолютних шляхів каталогів, повністю виключених з обходу (зчитується з `.cursorignore` / `.gitignore` через `loadCursorIgnorePaths`).
59
-
60
- **Повертає:** відсортований (`localeCompare`) масив абсолютних шляхів до файлів з іменем `default.conf.template`.
61
-
62
- **Поведінка:** обходить дерево через `walkDir`, додає тільки шляхи з базовим іменем `default.conf.template`. Будь-який шлях, у якому хоч один сегмент дорівнює `fixtures`, пропускається — це покриває як кореневі `tests/fixtures/`, так і co-located `rules/<rule>/js/<concern>/fixtures/`. Перед порівнянням бекслеші у відносному шляху нормалізуються в `/` (Windows-safe).
63
-
64
- **Side effects:** виключно читання дерева ФС через `walkDir`.
65
-
66
- ---
67
-
68
- ### `migrateDefaultTplConfFiles(root, ignorePaths = [])`
69
-
70
- **Сигнатура:** `async (root: string, ignorePaths?: string[]) => Promise<{ renamed: string[], overwritten: string[] }>`
71
-
72
- **Параметри:** аналогічні `findDefaultConfTemplatePaths`.
73
-
74
- **Повертає:** об'єкт з двома масивами відносних (від `root`) шляхів колишніх `default.tpl.conf`:
75
-
76
- - `renamed` — файл був просто перейменований у `default.conf.template`;
77
- - `overwritten` — поруч уже існував `default.conf.template`, тому його вміст замінено вмістом `default.tpl.conf`, а сам `default.tpl.conf` видалено.
78
-
79
- **Поведінка:**
80
-
81
- 1. збирає всі `default.tpl.conf` через `walkDir` і сортує їх для детермінованого порядку обробки;
82
- 2. для кожного шляху обчислює цільовий `default.conf.template` у тому ж каталозі (`dirname` + `join`);
83
- 3. якщо ціль існує (`existsSync`) — читає вміст `default.tpl.conf`, записує його в `default.conf.template` (`writeFile` з `utf8`), видаляє джерело (`unlink`);
84
- 4. інакше виконує `rename(old → new)`.
85
-
86
- **Side effects:** мутація ФС — записи, перейменування, видалення файлів. Усі шляхи в звіті нормалізовані `/`-роздільником.
87
-
88
- ---
89
-
90
- ### `migrateErrorLogOffDirective(root, ignorePaths = [])`
91
-
92
- **Сигнатура:** `async (root: string, ignorePaths?: string[]) => Promise<string[]>`
93
-
94
- **Параметри:** аналогічні попереднім міграційним функціям.
95
-
96
- **Повертає:** відносні шляхи шаблонів, у яких було виконано заміну (для звіту `pass`).
97
-
98
- **Поведінка:** для кожного `default.conf.template`, знайденого `findDefaultConfTemplatePaths`, читає вміст, виконує заміну `ERROR_LOG_OFF_RE → ERROR_LOG_CANONICAL` (тобто `error_log\s+off\s*;` → `error_log /dev/null crit;`). Якщо текст не змінився — файл не переписується (раннє `continue`); інакше — `writeFile` з `utf8`.
99
-
100
- **Мотивація:** `error_log off;` у nginx **не** валідна директива — `off` трактується як ім'я файлу (`/etc/nginx/off`), і під `readOnlyRootFilesystem` контейнер падає. `/dev/null` — writable character device і коректно «вимикає» лог.
101
-
102
- **Side effects:** перезапис вмісту шаблонів на місці.
103
-
104
- ---
105
-
106
- ### `parseIniVariableNames(iniText)`
107
-
108
- **Сигнатура:** `(iniText: string) => string[]`
109
-
110
- **Параметри:**
111
-
112
- - `iniText` — повний текст INI-файлу.
113
-
114
- **Повертає:** масив імен ключів у порядку появи (дублікати не дедуплікуються; вони з'являться стільки разів, скільки разів зустрілися).
115
-
116
- **Поведінка:** розбиває вхід за `LINE_SPLIT_RE` (`\r?\n`), для кожного рядка обрізає пробіли (`trim()`), пропускає порожні й коментарі (`#`, `;`), решту матчить регексом `INI_KEY_RE = /^([A-Za-z_]\w*)\s*=/` і додає першу групу. Секції (`[section]`) ігноруються природним чином, бо не матчаться регексом.
117
-
118
- **Side effects:** немає, чиста функція.
119
-
120
- ---
121
-
122
- ### `nginxTemplateViolations(content)`
123
-
124
- **Сигнатура:** `(content: string) => string | null`
125
-
126
- **Параметри:**
127
-
128
- - `content` — повний текст `default.conf.template`.
129
-
130
- **Повертає:** рядок з першим знайденим порушенням або `null`, якщо шаблон валідний.
131
-
132
- **Поведінка:** проходить упорядкований список правил і повертає повідомлення першого, чий предикат `ok(content)` повернув `false`. Порядок матеріалізовано в коді; перелік правил, що перевіряються (у порядку виконання):
133
-
134
- 1. наявність `server_tokens off`;
135
- 2. наявність `port_in_redirect off`;
136
- 3. наявність `client_max_body_size 0`;
137
- 4. наявність `client_body_buffer_size 512M`;
138
- 5. наявність `listen 8080`;
139
- 6. наявність `server_name _`;
140
- 7. наявність `access_log off`;
141
- 8. наявність `error_log /dev/null crit` (саме така форма; `error_log off` не валідний);
142
- 9. наявність `root /usr/share/nginx/html`;
143
- 10. `location /healthz` повертає healthy (підрядок `healthy` **або** `return\s+200`);
144
- 11. `location` для статики без gzip — мають бути присутні підрядки `gif|jpe?g|png|ico|woff2|xlsx`, `31536000` та `alias /usr/share/nginx/html/`;
145
- 12. `location` для текстової статики з gzip — підрядок `svg|js|css|ttf|map|xml|webmanifest|wasm`;
146
- 13. `gzip_static on` зустрічається **щонайменше двічі** (для двох location-блоків зі стисненням);
147
- 14. використання `$PUBLIC_PATH` у будь-якому location;
148
- 15. наявність `sendfile on`, `sendfile_max_chunk 512k`, `tcp_nopush on`;
149
- 16. наявність `try_files $uri $uri/ /index.html =404`.
150
-
151
- Після всіх «позитивних» правил додатково перевіряється негативне: якщо регекс `PROXY_LIKE_RE` (`proxy_pass|proxy_redirect|proxy_set_header|proxy_http_version|fastcgi_pass|grpc_pass|uwsgi_pass`) **матчить** вміст, повертається повідомлення про `*_pass` із вказівкою винести логіку в HTTPRoute. У коді є директива `// cspell:ignore fastcgi uwsgi`, щоб ці назви не лаялися спелчекером.
152
-
153
- **Side effects:** немає.
154
-
155
- ---
156
-
157
- ### `httpRouteMatchesNginxDefaultTpl(manifest)`
158
-
159
- **Сигнатура:** `(manifest: unknown) => boolean`
160
-
161
- **Параметри:**
162
-
163
- - `manifest` — десеріалізований корінь YAML-документа (об'єкт, мапа, або довільне значення з парсера).
164
-
165
- **Повертає:** `true`, якщо структура збігається з еталонним прикладом `HTTPRoute` з `nginx-default-tpl.mdc`; інакше `false`.
166
-
167
- **Поведінка:** ретельно гардить кожен шар від `null`/`undefined`/масивів/не-об'єктів і перевіряє:
168
-
169
- - `kind === 'HTTPRoute'`;
170
- - `spec` — об'єкт, не масив;
171
- - `spec.rules` — масив довжиною ≥ 2;
172
- - у `rules[0]`:
173
- - `matches` містить елемент з `path.type === 'Exact'`;
174
- - `filters` містить елемент з `type === 'RequestRedirect'`, `requestRedirect.scheme === 'https'`, `requestRedirect.path.type === 'ReplaceFullPath'` і `statusCode` `301` (як число або рядок);
175
- - у `rules[1]`:
176
- - `matches` містить елемент з `path.type === 'PathPrefix'`;
177
- - `backendRefs` містить елемент з `port` `8080` (як число або рядок).
178
-
179
- Підсумок: `hasExact && hasRedirect && hasPrefix && has8080`.
180
-
181
- **Side effects:** немає. Зараз не викликається з `check()` (зарезервована для тестів і майбутніх перевірок).
182
-
183
1
  ---
184
-
185
- ### `iniKeysMissingInTemplate(keys, template)`
186
-
187
- **Сигнатура:** `(keys: string[], template: string) => string | null`
188
-
189
- **Параметри:**
190
-
191
- - `keys` — масив імен змінних (зазвичай результат `parseIniVariableNames`);
192
- - `template` — повний текст `default.conf.template`.
193
-
194
- **Повертає:** повідомлення про першу змінну, для якої в шаблоні немає підрядка `$KEY`; або `null`, якщо всі ключі присутні.
195
-
196
- **Поведінка:** простий послідовний перебір `keys` з перевіркою `template.includes('$' + key)`. Це контракт `envsubst`: якщо ключ є у values-ini, він має бути використаний у шаблоні (або вилучений з ini).
197
-
198
- **Side effects:** немає.
199
-
2
+ docgen:
3
+ source: npm/rules/nginx-default-tpl/js/template.mjs
4
+ crc: 2d912e28
5
+ score: 90
200
6
  ---
201
7
 
202
- ### `dockerfileHasGzipStaticPipeline(dockerfileContent)` (внутрішня)
203
-
204
- **Сигнатура:** `(dockerfileContent: string) => boolean`
205
-
206
- **Параметри:**
207
-
208
- - `dockerfileContent` — повний текст одного Dockerfile/Containerfile.
209
-
210
- **Повертає:** `true`, якщо в тексті одночасно присутні: `find` (через `FIND_CMD_RE`), підрядок `/usr/share/nginx/html`, `gzip` (через `GZIP_CMD_RE`), прапор `-k` і регекс розширень `*.(js|css)` (через `GZIP_EXTENSION_RE`).
211
-
212
- **Side effects:** немає.
213
-
214
- ---
215
-
216
- ### `dockerfileHasEnvsSubstTemplate(dockerfileContent)` (внутрішня)
217
-
218
- **Сигнатура:** `(dockerfileContent: string) => boolean`
219
-
220
- **Параметри:**
221
-
222
- - `dockerfileContent` — повний текст Dockerfile/Containerfile.
223
-
224
- **Повертає:** `true`, якщо текст містить одночасно `envsubst` і `default.conf.template`.
225
-
226
- **Side effects:** немає.
227
-
228
- ---
229
-
230
- ### `checkTemplateFile(abs, root, passFn, failFn)` (внутрішня, async)
231
-
232
- **Сигнатура:** `async (abs: string, root: string, passFn: (msg: string) => void, failFn: (msg: string) => void) => Promise<void>`
233
-
234
- **Параметри:**
8
+ # template.mjs
235
9
 
236
- - `abs` — абсолютний шлях до `default.conf.template`;
237
- - `root` — корінь репозиторію (для відносних повідомлень);
238
- - `passFn`, `failFn` — колбеки `createCheckReporter`.
239
-
240
- **Поведінка:**
241
-
242
- 1. читає шаблон, викликає `nginxTemplateViolations`, рапортує `pass` або `fail` зі стислим описом порушення;
243
- 2. читає сусідній каталог (`readdir(dirname(abs))`); якщо `readdir` падає (наприклад, нет прав), список INI-файлів вважається порожнім (`try/catch`);
244
- 3. з усіх записів обирає тільки ті, що закінчуються на `.ini`;
245
- 4. якщо INI немає — викликає `failFn` з підказкою додати `values-*.ini` і повертається;
246
- 5. інакше — рапортує `pass` про знайдені ini (з кількістю), і для кожного:
247
- - читає текст (`readFile utf8`); якщо `readFile` падає — рапортує `fail` з повідомленням помилки (`Error.message` або `String(error)` для не-`Error`);
248
- - інакше парсить імена через `parseIniVariableNames`, передає у `iniKeysMissingInTemplate(keys, content)`; якщо є невикористаний ключ — `fail` з відносним шляхом ini.
249
-
250
- **Side effects:** читання файлів і каталогів, виклик колбеків репортера.
251
-
252
- ---
253
-
254
- ### `checkDockerfiles(root, ignorePaths, passFn, failFn)` (внутрішня, async)
255
-
256
- **Сигнатура:** `async (root: string, ignorePaths: string[], passFn, failFn) => Promise<void>`
257
-
258
- **Поведінка:**
259
-
260
- 1. шукає Dockerfile'и через `findDockerfilePaths(root, ignorePaths)` (імпорт з `../../docker/js/lint.mjs`);
261
- 2. якщо їх немає — `fail` з підказкою (бо `default.conf.template` уже знайдено);
262
- 3. читає вміст усіх Dockerfile'ів `Promise.all`;
263
- 4. якщо **хоч один** має gzip-pipeline (`dockerfileHasGzipStaticPipeline`) — `pass`, інакше `fail`;
264
- 5. якщо **хоч один** має envsubst+template (`dockerfileHasEnvsSubstTemplate`) — `pass`, інакше `fail`.
265
-
266
- **Side effects:** читання Dockerfile'ів, виклик колбеків.
267
-
268
- ---
269
-
270
- ### `checkVscodeNginx(passFn, failFn, cwd)` (внутрішня, sync)
271
-
272
- **Сигнатура:** `(passFn, failFn, cwd: string) => void`
273
-
274
- **Поведінка:** має дві однотипні гілки для `.vscode/extensions.json` і `.vscode/settings.json`:
275
-
276
- - якщо файл існує — викликає `runConftestBatch` із відповідним `policyDirRel` (`nginx-default-tpl/vscode_extensions` або `…/vscode_settings`) і `namespace` (`nginx_default_tpl.vscode_extensions` / `…vscode_settings`), передає масив з одного файлу. Якщо `violations` порожній — `pass`; інакше для кожного `v` — `failFn(v.message)`;
277
- - якщо файлу немає — `failFn` з підказкою з `nginx-default-tpl.mdc`.
278
-
279
- Для `settings.json` при відсутності файлу функція робить ранній `return`, не виконуючи `runConftestBatch` другий раз.
280
-
281
- **Side effects:** виконання `conftest` як зовнішнього процесу (через `runConftestBatch`), виклик колбеків.
282
-
283
- ---
284
-
285
- ### `check(cwd = process.cwd())`
286
-
287
- **Сигнатура:** `async (cwd?: string) => Promise<number>`
288
-
289
- **Параметри:**
290
-
291
- - `cwd` — корінь репозиторію; за замовчанням `process.cwd()`.
292
-
293
- **Повертає:** код виходу від `reporter.getExitCode()`: `0`, якщо `failFn` жодного разу не викликався (тільки `pass`); `1` — якщо була хоч одна помилка.
294
-
295
- **Поведінка (потік виконання):**
296
-
297
- 1. створює репортер `createCheckReporter()` і деструктурує `{ pass, fail }`;
298
- 2. читає список ігнорованих шляхів через `loadCursorIgnorePaths(root)`;
299
- 3. виконує міграцію `default.tpl.conf` → `default.conf.template` (`migrateDefaultTplConfFiles`) і репортує `pass` за кожним перейменованим/перезаписаним файлом;
300
- 4. виконує заміну `error_log off;` (`migrateErrorLogOffDirective`) і репортує `pass` за кожним виправленим шаблоном;
301
- 5. шукає `default.conf.template` (`findDefaultConfTemplatePaths`); якщо їх нуль — `pass` про пропуск і ранній `return reporter.getExitCode()`;
302
- 6. репортує `pass` з кількістю знайдених шаблонів;
303
- 7. послідовно (через `for…of` з `await`) перевіряє кожен шаблон через `checkTemplateFile`;
304
- 8. перевіряє Dockerfile'и через `checkDockerfiles`;
305
- 9. перевіряє VSCode-конфіги через `checkVscodeNginx` (синхронно);
306
- 10. повертає `reporter.getExitCode()`.
307
-
308
- **Side effects:** виклик усіх міграцій (мутація ФС), читання файлів і каталогів, виконання `conftest` через `runConftestBatch`, запис у репортер.
309
-
310
- ## Залежності
311
-
312
- ### Стандартна бібліотека Node.js
313
-
314
- - `node:fs` — `existsSync` (синхронна перевірка існування файлу для міграції й VSCode-конфігів);
315
- - `node:fs/promises` — `readdir`, `readFile`, `rename`, `unlink`, `writeFile`;
316
- - `node:path` — `basename`, `dirname`, `join`, `relative`.
10
+ ## Огляд
317
11
 
318
- ### Внутрішні модулі правил
12
+ Надає повну перевірку проєкту, включаючи перевірку шаблонів, Dockerfile та конфігурацій VSCode.
319
13
 
320
- - `../../docker/js/lint.mjs` — `findDockerfilePaths(root, ignorePaths)` — пошук Dockerfile'ів у дереві (повторне використання логіки правила `n-docker`);
321
- - `../../../scripts/lib/check-reporter.mjs` — `createCheckReporter()` — стандартний репортер pass/fail з агрегованим exit-кодом;
322
- - `../../../scripts/lib/load-cursor-config.mjs` — `loadCursorIgnorePaths(root)` — список абсолютних ігнорованих шляхів з `.cursor` конфігу;
323
- - `../../../scripts/lib/run-conftest-batch.mjs` — `runConftestBatch({ policyDirRel, namespace, files })` — синхронний запуск `conftest` для пакетної перевірки rego;
324
- - `../../../scripts/utils/walkDir.mjs` — `walkDir(root, visitor, ignorePaths)` — асинхронний обхід дерева.
14
+ ## Поведінка
325
15
 
326
- ### Регулярні вирази та константи модуля
16
+ findDefaultConfTemplatePaths
17
+ Збирає абсолютні шляхи до default.conf.template
327
18
 
328
- - `LINE_SPLIT_RE = /\r?\n/u` — універсальний роздільник рядків для INI;
329
- - `INI_KEY_RE = /^([A-Za-z_]\w*)\s*=/u` розпізнавання ключа `KEY=` в INI;
330
- - `RETURN_200_RE = /return\s+200/u` — індикатор успішного `/healthz`;
331
- - `GZIP_STATIC_ON_RE = /gzip_static\s+on/gu` — підрахунок входжень для перевірки «щонайменше 2»;
332
- - `PROXY_LIKE_RE = /\b(proxy_pass|proxy_redirect|proxy_set_header|proxy_http_version|fastcgi_pass|grpc_pass|uwsgi_pass)\b/u` — заборонені директиви бекенду;
333
- - `FIND_CMD_RE = /\bfind\b/u`, `GZIP_CMD_RE = /\bgzip\b/u` — детектори команд у Dockerfile;
334
- - `GZIP_EXTENSION_RE = /\*\.(?:js|css)/u` — детектор маски розширень gzip-кроку;
335
- - `ERROR_LOG_OFF_RE = /error_log\s+off\s*;/gu` — глобальний регекс для заміни;
336
- - `ERROR_LOG_CANONICAL = 'error_log /dev/null crit;'` — канонічна заміна.
19
+ migrateDefaultTplConfFiles
20
+ Перейменовує або перезаписує default.tpl.conf у default.conf.template
337
21
 
338
- ## Потік виконання / Використання
22
+ migrateErrorLogOffDirective
23
+ Замінює невалідну директиву error_log off; на error_log /dev/null crit;
339
24
 
340
- Модуль використовується як одна з перевірок репозитарного лінтера `n-cursor`/правил `.mdc`. Типовий виклик з контекстного скрипту правила:
25
+ parseIniVariableNames
26
+ Витягує імена змінних з рядків у форматі KEY=value
341
27
 
342
- ```mjs
343
- import { check } from './template.mjs'
28
+ nginxTemplateViolations
29
+ Перевіряє вміст шаблону на відповідність вимогам nginx-default-tpl.mdc
344
30
 
345
- const exitCode = await check(process.cwd())
346
- process.exit(exitCode)
347
- ```
31
+ httpRouteMatchesNginxDefaultTpl
32
+ Перевіряє відповідність структури HTTPRoute до прикладу у nginx-default-tpl.mdc
348
33
 
349
- Послідовність дій `check(cwd)`:
34
+ iniKeysMissingInTemplate
35
+ Перевіряє, чи використовуються всі імена змінних з ini у шаблоні
350
36
 
351
- 1. **Підготовка:** створення репортера, завантаження ігнор-списку.
352
- 2. **Міграції (мутативна фаза, до перевірок):**
353
- - застаріле `default.tpl.conf` → `default.conf.template` (rename або overwrite + unlink);
354
- - невалідне `error_log off;` → `error_log /dev/null crit;`.
355
- 3. **Збір шаблонів:** `findDefaultConfTemplatePaths` (виключає сегменти `fixtures/`).
356
- 4. **Швидкий вихід:** якщо шаблонів нуль — функція повертає `0` як «нічого перевіряти».
357
- 5. **Перевірка кожного шаблону:**
358
- - валідація директив через `nginxTemplateViolations` (на перше порушення);
359
- - пошук сусідніх `*.ini`, парс ключів, перевірка `$KEY` у шаблоні через `iniKeysMissingInTemplate`.
360
- 6. **Перевірка Dockerfile'ів:** наявність gzip-кроку для статики й кроку `envsubst` з шаблоном.
361
- 7. **Перевірка VSCode-конфігів:** делегування у rego через `runConftestBatch`.
362
- 8. **Підсумок:** `reporter.getExitCode()` — `0` або `1`.
37
+ check
38
+ Виконує повну валідацію проєкту включаючи перевірку шаблонів, Dockerfile та конфігурацій VSCode
363
39
 
364
- ### Контракт з користувачами модуля
40
+ ## Публічний API
365
41
 
366
- - Усі публічні функції є чистими або «атомарними» (одна відповідальність), що дозволяє точково використовувати їх у тестах (наприклад, `parseIniVariableNames`, `nginxTemplateViolations`, `iniKeysMissingInTemplate`, `httpRouteMatchesNginxDefaultTpl`).
367
- - Шляхи в звітах нормалізовані до `/`-роздільника й відносні до `root`.
368
- - Сортування результатів (`findDefaultConfTemplatePaths`, `migrateDefaultTplConfFiles` через `oldPaths.sort`) гарантує детермінований порядок виводу повідомлень — це важливо для стабільності тестів.
369
- - Фази «міграції» виконуються **до** валідації, тому застарілі артефакти не призводять до false-negative; натомість користувач бачить `pass` про факт міграції.
370
- - Шлях VSCode-перевірки залежить від `existsSync` у момент виклику, тобто новостворені файли (у тій самій сесії процесу) будуть враховані.
42
+ findDefaultConfTemplatePaths Збирає повні шляхи до **default.conf.template** у репозиторії; виключає будь-який сегмент `fixtures/` (включаючи `tests/fixtures/` та ко-локальні шляхи `rules/<rule>/js/<concern>/fixtures/`).
43
+ migrateDefaultTplConfFiles Знаходить **default.tpl.conf** у дереві від `root`. Якщо **default.conf.template** відсутній, перейменовує **default.tpl.conf**; якщо він присутній, перезаписує **default.conf.template** вмістом **default.tpl.conf** та видаляє **default.tpl.conf**.
44
+ migrateErrorLogOffDirective Замінює невалідну директиву `error_log off;` на `error_log /dev/null crit;` у всіх **default.conf.template** від `root**. `error_log off;` трактується як ім'я файлу (`/etc/nginx/off`), що призводить до помилки `readOnlyRootFilesystem`. `/dev/null` — це записуваний пристрій.
45
+ parseIniVariableNames Витягує імена змінних з файлів ini (рядки у форматі KEY=value, без коментарів і порожніх).
46
+ nginxTemplateViolations Перевіряє вміст **default.conf.template** на відповідність вимогам **nginx-default-tpl.mdc**.
47
+ httpRouteMatchesNginxDefaultTpl — Перевіряє, чи відповідає **HTTPRoute** патерну Exact→RequestRedirect + PathPrefix→backendRefs.
48
+ iniKeysMissingInTemplate — Перевіряє, чи входять усі імена ключів з ini до шаблону у форматі `$KEY` (використовуючи envsubst).
49
+ check — Перевіряє відповідність проєкту правилам **nginx-default-tpl.mdc**.
371
50
 
372
- ### Особливості й нюанси
51
+ ## Гарантії поведінки
373
52
 
374
- - `nginxTemplateViolations` повертає **перше** порушення, а не повний список це свідома стратегія fail-fast (мінімізація шуму в репортері).
375
- - `httpRouteMatchesNginxDefaultTpl` приймає тип `unknown`, бо очікує безпосередньо результат YAML-парсера; усі гарди — позитивні (object && !Array && !null && !undefined).
376
- - `parseIniVariableNames` **не** дедуплікує ключі: дубль у ini призведе до повторного перебору в `iniKeysMissingInTemplate`, проте через `template.includes` обидва результати будуть однакові — це не впливає на коректність.
377
- - `checkTemplateFile` обережно обробляє помилки `readdir` (поглинає винятки, продовжує без INI), але помилки `readFile` для ini рапортує як `fail` із текстом помилки.
378
- - `migrateErrorLogOffDirective` не запускає файлову операцію, якщо вміст не змінився — оптимізація для ідемпотентних запусків.
53
+ - Перехоплює помилки і не пропускає винятків назовні (fail-safe).
54
+ - За невдачі повертає значення помилки (`false`/`null`/`Err`) замість генерування винятку чи паніки.
55
+ - Кешує результати в межах одного прогону.
56
+ - Не звертається до мережі.