@nitra/cursor 1.11.3 → 1.11.6

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 (86) hide show
  1. package/CHANGELOG.md +32 -3
  2. package/bin/n-cursor.js +40 -6
  3. package/package.json +2 -1
  4. package/rules/abie/js/applies/check.mjs +4 -4
  5. package/rules/abie/js/env_dns/check.mjs +1 -1
  6. package/rules/abie/js/firebase_hosting/check.mjs +1 -1
  7. package/rules/abie/js/hc_pairing/check.mjs +3 -3
  8. package/rules/abie/js/ua_http_route/check.mjs +3 -3
  9. package/rules/abie/js/ua_node_selector/check.mjs +4 -2
  10. package/rules/abie/policy/base_deployment_preem/target.json +1 -5
  11. package/rules/abie/utils/enabled.mjs +1 -1
  12. package/rules/abie/utils/env-dns.mjs +4 -4
  13. package/rules/abie/utils/http-route.mjs +5 -5
  14. package/rules/abie/utils/k8s-tree.mjs +23 -15
  15. package/rules/abie/utils/kustomization-patches.mjs +20 -20
  16. package/rules/abie/utils/overlay-paths.mjs +8 -8
  17. package/rules/abie/utils/yaml.mjs +4 -4
  18. package/rules/adr/adr.mdc +2 -2
  19. package/rules/adr/js/hooks/check.mjs +5 -5
  20. package/rules/docker/docker.mdc +2 -2
  21. package/rules/docker/js/run.mjs +3 -2
  22. package/rules/docker/policy/package_json/package_json.rego +1 -1
  23. package/rules/ga/js/lint.mjs +3 -26
  24. package/rules/hasura/js/internal_urls/check.mjs +1 -1
  25. package/rules/js-bun-redis/js/imports/check.mjs +5 -1
  26. package/rules/js-run/js/runtime/check.mjs +4 -1
  27. package/rules/k8s/js/run.mjs +3 -2
  28. package/rules/k8s/k8s.mdc +2 -4
  29. package/rules/k8s/policy/base_manifest/target.json +1 -5
  30. package/rules/nginx-default-tpl/js/template/check.mjs +4 -2
  31. package/rules/npm-module/js/package_structure/check.mjs +8 -3
  32. package/rules/npm-module/npm-module.mdc +3 -3
  33. package/rules/rego/js/applies/check.mjs +2 -2
  34. package/rules/rego/js/lint.mjs +4 -1
  35. package/rules/rego/policy/package_json/package_json.rego +5 -3
  36. package/rules/rego/rego.mdc +3 -3
  37. package/rules/style-lint/js/tooling/check.mjs +1 -1
  38. package/rules/style-lint/style-lint.mdc +1 -1
  39. package/rules/tauri/js/tooling/check.mjs +3 -1
  40. package/rules/text/js/formatting/check.mjs +8 -24
  41. package/rules/text/js/lint.mjs +34 -0
  42. package/rules/text/js/run-shellcheck.mjs +2 -2
  43. package/rules/text/js/run-v8r.mjs +2 -2
  44. package/rules/text/text.mdc +5 -5
  45. package/schemas/v8r-catalog.json +6 -0
  46. package/scripts/auto-skills.mjs +3 -7
  47. package/scripts/utils/discover-checkable-rules.mjs +4 -3
  48. package/scripts/utils/resolve-target-files.mjs +1 -1
  49. package/scripts/utils/run-lint-step.mjs +33 -0
  50. package/scripts/utils/run-rule.mjs +5 -3
  51. package/skills/abie-clean/SKILL.md +13 -11
  52. package/skills/adr-normalize/SKILL.md +0 -1
  53. package/skills/fix/SKILL.md +3 -7
  54. package/rules/abie/policy/base_deployment_preem/base_deployment_preem_test.rego +0 -60
  55. package/rules/abie/policy/clean_merged_ignore_branches/clean_merged_ignore_branches_test.rego +0 -48
  56. package/rules/abie/policy/health_check_policy/health_check_policy_test.rego +0 -99
  57. package/rules/abie/policy/http_route_base/http_route_base_test.rego +0 -64
  58. package/rules/bun/policy/package_json/package_json_test.rego +0 -109
  59. package/rules/docker/policy/lint_docker_yml/lint_docker_yml_test.rego +0 -104
  60. package/rules/docker/policy/package_json/package_json_test.rego +0 -42
  61. package/rules/graphql/policy/vscode_extensions/vscode_extensions_test.rego +0 -34
  62. package/rules/image-avif/policy/package_json/package_json_test.rego +0 -69
  63. package/rules/js-lint/policy/package_json/package_json_test.rego +0 -130
  64. package/rules/js-run/policy/jsconfig/jsconfig_test.rego +0 -88
  65. package/rules/k8s/policy/base_kustomization/base_kustomization_test.rego +0 -73
  66. package/rules/k8s/policy/base_manifest/base_manifest_test.rego +0 -94
  67. package/rules/k8s/policy/gateway/gateway_test.rego +0 -122
  68. package/rules/k8s/policy/hasura_configmap/hasura_configmap_test.rego +0 -49
  69. package/rules/k8s/policy/hasura_httproute/hasura_httproute_test.rego +0 -148
  70. package/rules/k8s/policy/hpa_pdb/hpa_pdb_test.rego +0 -101
  71. package/rules/k8s/policy/kustomization/kustomization_test.rego +0 -128
  72. package/rules/k8s/policy/manifest/manifest_test.rego +0 -309
  73. package/rules/k8s/policy/svc_hl_yaml/svc_hl_yaml_test.rego +0 -42
  74. package/rules/k8s/policy/svc_yaml/svc_yaml_test.rego +0 -41
  75. package/rules/nginx-default-tpl/policy/vscode_extensions/vscode_extensions_test.rego +0 -30
  76. package/rules/nginx-default-tpl/policy/vscode_settings/vscode_settings_test.rego +0 -53
  77. package/rules/npm-module/policy/npm_package_json/npm_package_json_test.rego +0 -81
  78. package/rules/rego/policy/package_json/package_json_test.rego +0 -42
  79. package/rules/rego/policy/vscode_extensions/vscode_extensions_test.rego +0 -34
  80. package/rules/rego/policy/vscode_settings/vscode_settings_test.rego +0 -55
  81. package/rules/style-lint/policy/vscode_extensions/vscode_extensions_test.rego +0 -39
  82. package/rules/style-lint/policy/vscode_settings/vscode_settings_test.rego +0 -49
  83. package/rules/tauri/policy/vscode_extensions/vscode_extensions_test.rego +0 -44
  84. package/rules/text/policy/markdownlint/markdownlint_test.rego +0 -98
  85. package/rules/text/policy/vscode_extensions/vscode_extensions_test.rego +0 -51
  86. package/rules/text/policy/vscode_settings/vscode_settings_test.rego +0 -85
package/CHANGELOG.md CHANGED
@@ -4,6 +4,35 @@
4
4
 
5
5
  Формат — [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), нумерація — [SemVer](https://semver.org/lang/uk/).
6
6
 
7
+ ## [1.11.6] - 2026-05-15
8
+
9
+ ### Changed
10
+
11
+ - **`npm/rules/npm-module/npm-module.mdc`** — переформульовано вимогу про тести й фікстури. Раніше правило вимагало тримати їх **поза** будь-яким шляхом з `"files"` (канонічно — у `npm/tests/`). Тепер тести/фікстури можуть лежати **поруч з кодом** усередині `"files"`-шляхів, але `"files"` обовʼязково має містити **негативні glob-патерни**, що виключають їх із tarball (`!**/*.test.*`, `!**/*.spec.*`, `!**/test-helpers.*`, `!**/fixtures/**`, `!**/__tests__/**`, опційно `!**/*_test.rego`). Це краще відповідає реальному layout пакета (co-located test-файли у `rules/<id>/js/<concern>/`) і прибирає роз'їзд правила з фактичним `npm/package.json`. Версію `.mdc` піднято до `1.12`.
12
+ - **`npm/rules/npm-module/js/package_structure/check.mjs::checkNoTestsInPublishedFiles`** — текст fail-повідомлення тепер однозначно радить додати негативний glob у `"files"`, без альтернативи «винеси за межі шляхів з "files"». Логіка перевірки (walk positive ∖ negative + класифікація test-style) не змінилась — пере-кваліфіковано лише підказку для агента й людини.
13
+
14
+ ## [1.11.5] - 2026-05-15
15
+
16
+ ### Added
17
+
18
+ - **`npm/bin/n-cursor.js`** — підкоманди `lint-rego`, `lint-k8s`, `lint-docker`, `lint-text` (раніше був лише `lint-ga`). Споживчі `package.json` тепер можуть використовувати уніфіковану форму `n-cursor lint-X` замість прямих посилань на файли `bun ./npm/scripts/*.mjs`, які після phase 2 концерн-сплету переїхали у `npm/rules/<id>/js/`. `lint-text` — композитний: послідовно `cspell .` → `runShellcheckText()` → `bunx markdownlint-cli2 --fix "**/*.md" "**/*.mdc"` → `runV8rWithGlobs()`.
19
+ - **`npm/rules/text/js/lint.mjs`** — новий ентрі-модуль для канонічного `lint-text`.
20
+ - **`npm/scripts/utils/run-lint-step.mjs`** — спільний хелпер `runLintStep(title, cmd, args)` для CLI-обгорток `lint-<rule>` (раніше дублювалося у `rules/ga/js/lint.mjs` і новому `rules/text/js/lint.mjs` — jscpd-clone).
21
+
22
+ ### Changed
23
+
24
+ - **`npm/rules/k8s/js/run.mjs`**, **`npm/rules/docker/js/run.mjs`** — `main()` перейменовано і експортовано як `runLintK8s` / `runLintDocker` для виклику з CLI-маршрутизатора. `isRunAsCli()`-гілка прямого запуску збережена для зворотної сумісності.
25
+ - **`npm/rules/rego/js/lint.mjs`** — авто-виклик `runLintRego()` тепер обгорнуто `if (isRunAsCli())`, інакше імпорт модуля з CLI запускав би лінт як side-effect.
26
+ - **`npm/rules/rego/policy/package_json/`** — канонічне значення `scripts.lint-rego` змінено на `"n-cursor lint-rego"` (раніше `"bun ./npm/scripts/lint-rego.mjs"`). Аналогічно `npm/rules/docker/policy/package_json/` — на `"n-cursor lint-docker"`.
27
+ - **`npm/rules/text/js/formatting/check.mjs`** — `checkLintTextScript()` тепер вимагає рівно `"n-cursor lint-text"` замість попередньої складної валідації багатоступеневого ланцюжка (`cspell` → `run-shellcheck-text.mjs` → `markdownlint-cli2` → `run-v8r.mjs`). Канонічна форма — одне посилання на CLI, а зміст ланцюжка живе у `npm/rules/text/js/lint.mjs`.
28
+ - **`npm/package.json#files`** — додано негативний glob `"!**/*_test.rego"` (раніше були лише `*.test.mjs`, `test-helpers.mjs`, `fixtures/**`). 33 rego-юніт-тестових файли (`<policy>_test.rego`) більше не потрапляють у tarball — їх виконує лише `conftest verify` у dev-репо.
29
+
30
+ ## [1.11.4] - 2026-05-15
31
+
32
+ ### Fixed
33
+
34
+ - **`npm/rules/nginx-default-tpl/js/template/check.mjs`** — `findDefaultConfTemplatePaths` пропускає тестові `fixtures/` за будь-яким сегментом шляху, не лише `tests/fixtures/`. Після concern-split (фази 1-4) fixtures лежать у `rules/<rule>/js/<concern>/fixtures/`, і старий патерн пропускав їх повз → check шукав Dockerfile поруч із тестовим шаблоном і фалс-фейлив.
35
+
7
36
  ## [1.11.3] - 2026-05-15
8
37
 
9
38
  ### Fixed
@@ -55,7 +84,7 @@
55
84
  - **Інкрементальна міграція правил на `target.json`** (декларативні маніфести поруч із кожним `<concern>.rego`):
56
85
  - **Single-file правила** (11): `bun`, `text`, `style-lint`, `php`, `docker`, `npm-module`, `js-lint`, `image-compress`, `capacitor`, `hasura`, `adr` — `target.json` з `single` для кожного канонічного конфіг-файлу. Сумарно 27 нових маніфестів. `bun.bunfig`, `text.cspell`, `npm_module.npm_publish_yml` тощо тепер прогоняються через CLI `check <id>` без додаткового `bun run lint-conftest`.
57
86
  - **Walk-glob правила** (6): `js-mssql`, `js-bun-db`, `js-bun-redis`, `js-run` (package_json + configmap), `vue`, `image-avif` — `walkGlob: "**/package.json"` або відповідний патерн.
58
- - **k8s.* концерни** (8): `manifest`, `gateway`, `hpa_pdb`, `kustomization`, `svc_yaml`, `svc_hl_yaml`, `base_kustomization`, `base_manifest` — `walkGlob` по YAML під сегментом `k8s/`; `base_manifest` використовує негативний glob для виключення `kustomization.yaml`.
87
+ - **k8s.\* концерни** (8): `manifest`, `gateway`, `hpa_pdb`, `kustomization`, `svc_yaml`, `svc_hl_yaml`, `base_kustomization`, `base_manifest` — `walkGlob` по YAML під сегментом `k8s/`; `base_manifest` використовує негативний glob для виключення `kustomization.yaml`.
59
88
  - **abie концерни** (4): `clean_merged_ignore_branches` (single), `health_check_policy` (walkGlob `**/k8s/**/hc.yaml`), `http_route_base` (walkGlob `**/k8s/**/base/**/hr.yaml`), `base_deployment_preem` (walkGlob `**/k8s/**/base/**/*.{yaml,yml}` з виключенням `kustomization.yaml`).
60
89
  - **`capture-decisions.sh` тепер пише чернетки напряму в `docs/adr/<timestamp>-<sid>.md`** (раніше — у `docs/adr/_inbox/`). Сам каталог `_inbox/` більше не створюється, але `normalize-decisions.sh` бачить його рекурсивно — старі чернетки з `_inbox/` поступово розчищаються нормалізацією. Можна також одноразово `git mv docs/adr/_inbox/*.md docs/adr/` і прибрати порожній каталог.
61
90
  - **Правило `adr` (`npm/rules/adr/adr.mdc`)**: повне переписування під дві фази (capture + normalize). Видалено згадки `_inbox/`. Версія `version: '2.0'`.
@@ -77,14 +106,14 @@
77
106
 
78
107
  ### Fixed
79
108
 
80
- - `npm/package.json#files`: додано негативні glob-патерни `!**/*.test.mjs`, `!**/test-helpers.mjs`, `!**/fixtures/**`, щоб після переїзду тестів у `rules/<rule>/js/`, `scripts/`, `scripts/utils/` вони не потрапляли в опубліковану npm-таrball (вимагає правило `npm-module`).
109
+ - `npm/package.json#files`: додано негативні glob-патерни `!**/*.test.mjs`, `!**/test-helpers.mjs`, `!**/fixtures/**`, щоб після переїзду тестів у `rules/<rule>/js/`, `scripts/`, `scripts/utils/` вони не потрапляли в опубліковану npm-tarball (вимагає правило `npm-module`).
81
110
  - `npm/package.json#devDependencies`: додано `@nitra/cursor: ^1.9.22` (auto-fill від `ensure-nitra-cursor-dev-dependencies.mjs`).
82
111
 
83
112
  ## [1.9.22] - 2026-05-14
84
113
 
85
114
  ### Changed
86
115
 
87
- - **Rule-centric структура пакета.** Кожне правило тепер живе в одній директорії `npm/rules/{rule}/` з усіма своїми артефактами: `{rule}.mdc`, `auto.md` (умова автоактивації), `policy/` (rego-поліси), `js/` (check.mjs + опційні run/lint + co-located *.test.mjs + fixtures/). Видалив каталог правила — правило зникло без слідів у `bin/auto-rules.md`, `npm/policy/`, `npm/scripts/`. Дзеркальна структура для скілів у `npm/skills/{skill}/` (SKILL.md + auto.md + js/).
116
+ - **Rule-centric структура пакета.** Кожне правило тепер живе в одній директорії `npm/rules/{rule}/` з усіма своїми артефактами: `{rule}.mdc`, `auto.md` (умова автоактивації), `policy/` (rego-поліси), `js/` (check.mjs + опційні run/lint + co-located \*.test.mjs + fixtures/). Видалив каталог правила — правило зникло без слідів у `bin/auto-rules.md`, `npm/policy/`, `npm/scripts/`. Дзеркальна структура для скілів у `npm/skills/{skill}/` (SKILL.md + auto.md + js/).
88
117
  - **Тести співрозташовуються з джерелами.** ~50 файлів з `npm/tests/` переїхали в `npm/rules/{rule}/js/*.test.mjs` (тести правил), `npm/scripts/*.test.mjs` (тести інфраструктури), `npm/scripts/utils/*.test.mjs` (тести утиліт). `tests/helpers.mjs` → `scripts/utils/test-helpers.mjs`. `npm/tests/` залишається тільки для 3 крос-правильних інтеграційних тестів.
89
118
  - **`bin/n-cursor.js`**: `BUNDLED_MDC_DIR` → `BUNDLED_RULES_DIR`. `discoverBundledRuleNames` і `discoverCheckScripts` тепер обходять підкаталоги `rules/` замість файлів у `mdc/` чи `check-*.mjs` у `scripts/`. Резолвер check-скриптів: `rules/{rule}/js/check.mjs`. `readBundledRuleContent` читає `rules/{rule}/{rule}.mdc`.
90
119
  - **`scripts/utils/run-conftest-batch.mjs` та `scripts/lint-conftest.mjs`**: шляхи до rego-полісі — `rules/{rule}/policy/{name}/` (замість `policy/{rule}/{name}/`). Snake_case `policyDirRel` у JS-call sites замінено на kebab-case.
package/bin/n-cursor.js CHANGED
@@ -13,6 +13,12 @@
13
13
  * інакше викликає `check`); прописується автоматично в `.claude/settings.json`
14
14
  * `npx \@nitra/cursor lint-ga` — канонічний lint-ga (ga.mdc): preflight на `shellcheck` →
15
15
  * `bunx github-actionlint` → `uvx zizmor --offline --collect=workflows .`
16
+ * `npx \@nitra/cursor lint-rego` — канонічний lint-rego (conftest.mdc + rego.mdc):
17
+ * preflight на `opa`/`regal` → `opa check --strict` → `regal lint` → опц. `conftest verify`
18
+ * `npx \@nitra/cursor lint-k8s` — канонічний lint-k8s (k8s.mdc): `kubeconform` + `kubescape` по `…/k8s/*.yaml`
19
+ * `npx \@nitra/cursor lint-docker` — канонічний lint-docker (docker.mdc): `hadolint` по `Dockerfile`/`*.Dockerfile`
20
+ * `npx \@nitra/cursor lint-text` — канонічний lint-text (text.mdc): `cspell` → `shellcheck` (з auto-fix) →
21
+ * `markdownlint-cli2 --fix` → `v8r` (json/json5/yaml/yml/toml)
16
22
  *
17
23
  * Claude Code інтеграція: під час синку, окрім `.cursor/rules` і `.claude/commands` (з skills), CLI ще раз
18
24
  * синхронізує `.claude/settings.json` (hooks + permissions; merge — користувацькі поля зберігаються),
@@ -68,7 +74,11 @@ import { detectAutoSkills } from '../scripts/auto-skills.mjs'
68
74
  import { runStopHookCli } from '../scripts/claude-stop-hook.mjs'
69
75
  import { discoverCheckableRules } from '../scripts/utils/discover-checkable-rules.mjs'
70
76
  import { ensureNitraCursorInRootDevDependencies } from '../scripts/ensure-nitra-cursor-dev-dependencies.mjs'
77
+ import { runLintDocker } from '../rules/docker/js/run.mjs'
71
78
  import { runLintGaCli } from '../rules/ga/js/lint.mjs'
79
+ import { runLintK8s } from '../rules/k8s/js/run.mjs'
80
+ import { runLintRego } from '../rules/rego/js/lint.mjs'
81
+ import { runLintTextCli } from '../rules/text/js/lint.mjs'
72
82
  import { runRule } from '../scripts/utils/run-rule.mjs'
73
83
  import { syncClaudeConfig } from '../scripts/sync-claude-config.mjs'
74
84
  import { upgradeNitraCursorToLatestAndBunInstall } from '../scripts/upgrade-nitra-cursor-and-install.mjs'
@@ -88,7 +98,6 @@ const RULE_PREFIX = 'n-'
88
98
 
89
99
  const binDir = dirname(fileURLToPath(import.meta.url))
90
100
  const BUNDLED_RULES_DIR = join(binDir, '..', 'rules')
91
- const BUNDLED_SCRIPTS_DIR = join(binDir, '..', 'scripts')
92
101
  const BUNDLED_SKILLS_DIR = join(binDir, '..', 'skills')
93
102
  const BUNDLED_AGENTS_TEMPLATE_PATH = join(binDir, '..', AGENTS_TEMPLATE_FILE)
94
103
  /** Корінь установленого пакету (каталог з `rules/`, `github-actions/`, …) */
@@ -266,8 +275,9 @@ async function readConfig(paths = {}) {
266
275
  // правило, додане вручну (напр. `adr` без auto.md-умови), не активувало б залежні
267
276
  // скіли (`adr-normalize`).
268
277
  const disableRulesSet = new Set(disableRules)
269
- const effectiveRulesForSkills = [...new Set([...normalizeIdList(parsedConfig.rules), ...autoDetectedRules.rules])]
270
- .filter(id => !disableRulesSet.has(id))
278
+ const effectiveRulesForSkills = [
279
+ ...new Set([...normalizeIdList(parsedConfig.rules), ...autoDetectedRules.rules])
280
+ ].filter(id => !disableRulesSet.has(id))
271
281
  const autoDetectedSkills = detectAutoSkills({
272
282
  availableSkills,
273
283
  detectedRules: effectiveRulesForSkills,
@@ -999,9 +1009,9 @@ function logRemovedManagedItems(title, basePath, names) {
999
1009
  * (плюс legacy `rules/<id>/js/check.mjs` як концерн `legacy`) або policy-концерн у
1000
1010
  * `rules/<id>/policy/<concern>/target.json`. Делегує у `discoverCheckableRules` —
1001
1011
  * див. `scripts/utils/discover-checkable-rules.mjs`.
1002
- * @returns {Promise<import('../scripts/utils/discover-checkable-rules.mjs').CheckableRule[]>} опис правил у алфавітному порядку
1012
+ * @returns {import('../scripts/utils/discover-checkable-rules.mjs').CheckableRule[]} опис правил у алфавітному порядку
1003
1013
  */
1004
- async function discoverCheckScripts() {
1014
+ function discoverCheckScripts() {
1005
1015
  return discoverCheckableRules(BUNDLED_RULES_DIR)
1006
1016
  }
1007
1017
 
@@ -1347,6 +1357,30 @@ try {
1347
1357
 
1348
1358
  break
1349
1359
  }
1360
+ case 'lint-rego': {
1361
+ // Канонічний lint-rego: preflight opa/regal → opa check --strict → regal lint → conftest verify (опц.).
1362
+ process.exitCode = runLintRego()
1363
+
1364
+ break
1365
+ }
1366
+ case 'lint-k8s': {
1367
+ // Канонічний lint-k8s: kubeconform + kubescape по знайдених деревах `…/k8s/*.yaml`.
1368
+ process.exitCode = await runLintK8s()
1369
+
1370
+ break
1371
+ }
1372
+ case 'lint-docker': {
1373
+ // Канонічний lint-docker: hadolint по Dockerfile та *.Dockerfile (docker.mdc).
1374
+ process.exitCode = await runLintDocker()
1375
+
1376
+ break
1377
+ }
1378
+ case 'lint-text': {
1379
+ // Канонічний lint-text: cspell → run-shellcheck → markdownlint-cli2 --fix → run-v8r (text.mdc).
1380
+ process.exitCode = runLintTextCli()
1381
+
1382
+ break
1383
+ }
1350
1384
  case undefined:
1351
1385
  case '': {
1352
1386
  await runSync()
@@ -1356,7 +1390,7 @@ try {
1356
1390
  default: {
1357
1391
  console.error(`❌ Невідома команда: ${command}`)
1358
1392
  console.error(
1359
- ` Очікується: (без аргументів) синхронізація правил, check, rename-yaml-extensions, stop-hook, lint-ga`
1393
+ ` Очікується: (без аргументів) синхронізація правил, check, rename-yaml-extensions, stop-hook, lint-ga, lint-rego, lint-k8s, lint-docker, lint-text`
1360
1394
  )
1361
1395
  process.exitCode = 1
1362
1396
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "1.11.3",
3
+ "version": "1.11.6",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",
@@ -34,6 +34,7 @@
34
34
  "AGENTS.template.md",
35
35
  "CHANGELOG.md",
36
36
  "!**/*.test.mjs",
37
+ "!**/*_test.rego",
37
38
  "!**/test-helpers.mjs",
38
39
  "!**/fixtures/**"
39
40
  ],
@@ -8,16 +8,16 @@ import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mj
8
8
  import { isAbieRuleEnabled } from '../../utils/enabled.mjs'
9
9
 
10
10
  /**
11
- * @returns {Promise<boolean>}
11
+ * @returns {Promise<boolean>} `true` — правило застосовне; `false` — пропустити
12
12
  */
13
- export async function applies() {
13
+ export function applies() {
14
14
  return isAbieRuleEnabled(process.cwd())
15
15
  }
16
16
 
17
17
  /**
18
- * @returns {Promise<number>}
18
+ * @returns {number} exit-код (0 — OK, 1 — порушення)
19
19
  */
20
- export async function check() {
20
+ export function check() {
21
21
  const reporter = createCheckReporter()
22
22
  reporter.pass('Правило abie увімкнено — виконуємо перевірки')
23
23
  return reporter.getExitCode()
@@ -15,7 +15,7 @@ import { loadCursorIgnorePaths } from '../../../../scripts/utils/load-cursor-con
15
15
  import { abieEnvNameFromBasename, collectAbieEnvFiles, validateAbieEnvInternalUrls } from '../../utils/env-dns.mjs'
16
16
 
17
17
  /**
18
- * @returns {Promise<number>}
18
+ * @returns {Promise<number>} результат
19
19
  */
20
20
  export async function check() {
21
21
  const reporter = createCheckReporter()
@@ -12,7 +12,7 @@ import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mj
12
12
  const SKIP_TOP_DIR_NAMES = new Set(['.git', 'node_modules'])
13
13
 
14
14
  /**
15
- * @returns {Promise<number>}
15
+ * @returns {Promise<number>} результат
16
16
  */
17
17
  export async function check() {
18
18
  const reporter = createCheckReporter()
@@ -17,7 +17,7 @@ import { validateAbieHcModeline } from '../../utils/hc-yaml.mjs'
17
17
  import { collectDeploymentDirs, findK8sYamlFiles } from '../../utils/k8s-tree.mjs'
18
18
 
19
19
  /**
20
- * @returns {Promise<number>}
20
+ * @returns {Promise<number>} результат
21
21
  */
22
22
  export async function check() {
23
23
  const reporter = createCheckReporter()
@@ -50,8 +50,8 @@ export async function check() {
50
50
  continue
51
51
  }
52
52
  const modelineErr = validateAbieHcModeline(hcRaw, relHc)
53
- if (modelineErr !== null) fail(modelineErr)
54
- else pass(`${relHc}: modeline OK`)
53
+ if (modelineErr === null) pass(`${relHc}: modeline OK`)
54
+ else fail(modelineErr)
55
55
  }
56
56
 
57
57
  return reporter.getExitCode()
@@ -26,7 +26,7 @@ import {
26
26
  } from '../../utils/overlay-paths.mjs'
27
27
 
28
28
  /**
29
- * @returns {Promise<number>}
29
+ * @returns {Promise<number>} результат
30
30
  */
31
31
  export async function check() {
32
32
  const reporter = createCheckReporter()
@@ -78,8 +78,8 @@ export async function check() {
78
78
  }
79
79
  const combined = getCombinedNginxRunPatchTextFromKustomization(raw)
80
80
  const v = validateAbieNginxRunHttpRoutePatches(combined, 'ua', raw, sharedAnalysis.refCount)
81
- if (v !== null) fail(`${rel}: ${v}`)
82
- else pass(`${rel}: HTTPRoute patch (ua) відповідає abie.mdc`)
81
+ if (v === null) pass(`${rel}: HTTPRoute patch (ua) відповідає abie.mdc`)
82
+ else fail(`${rel}: ${v}`)
83
83
  }
84
84
 
85
85
  return reporter.getExitCode()
@@ -16,7 +16,7 @@ import { kustomizationHasAbieDeploymentNodeSelectorPatch } from '../../utils/kus
16
16
  import { abieOverlayK8sTreeHasDeployment, isUaKustomizationPath } from '../../utils/overlay-paths.mjs'
17
17
 
18
18
  /**
19
- * @returns {Promise<number>}
19
+ * @returns {Promise<number>} результат
20
20
  */
21
21
  export async function check() {
22
22
  const reporter = createCheckReporter()
@@ -55,7 +55,9 @@ export async function check() {
55
55
  continue
56
56
  }
57
57
  if (!kustomizationHasAbieDeploymentNodeSelectorPatch(raw, 'ua')) {
58
- fail(`${rel}: потрібен patch target kind Deployment: path /spec/template/spec/nodeSelector та preem: false (abie.mdc)`)
58
+ fail(
59
+ `${rel}: потрібен patch target kind Deployment: path /spec/template/spec/nodeSelector та preem: false (abie.mdc)`
60
+ )
59
61
  continue
60
62
  }
61
63
  pass(`${rel}: nodeSelector patch (ua) відповідає abie.mdc`)
@@ -1,10 +1,6 @@
1
1
  {
2
2
  "$schema": "https://unpkg.com/@nitra/cursor/schemas/target.json",
3
3
  "files": {
4
- "walkGlob": [
5
- "**/k8s/**/base/**/*.yaml",
6
- "**/k8s/**/base/**/*.yml",
7
- "!**/k8s/**/base/**/kustomization.yaml"
8
- ]
4
+ "walkGlob": ["**/k8s/**/base/**/*.yaml", "**/k8s/**/base/**/*.yml", "!**/k8s/**/base/**/kustomization.yaml"]
9
5
  }
10
6
  }
@@ -12,7 +12,7 @@ const CONFIG_FILE = '.n-cursor.json'
12
12
  /**
13
13
  * Чи увімкнено правило **abie** у `.n-cursor.json:rules`.
14
14
  * @param {string} root корінь репозиторію (cwd)
15
- * @returns {Promise<boolean>}
15
+ * @returns {Promise<boolean>} `true` — `rules` містить `abie`; `false` — інакше
16
16
  */
17
17
  export async function isAbieRuleEnabled(root) {
18
18
  const p = join(root, CONFIG_FILE)
@@ -23,8 +23,8 @@ const ABIE_ENV_CLUSTER_DNS_MAP = Object.freeze({
23
23
  /**
24
24
  * Дістає `dev` / `ua` з basename env-файлу abie.
25
25
  * Не-abie env-файли (`production.env`, `.env` без імені) → null.
26
- * @param {string} basenameOfEnvFile
27
- * @returns {('dev' | 'ua') | null}
26
+ * @param {string} basenameOfEnvFile опис.
27
+ * @returns {('dev' | 'ua') | null} результат
28
28
  */
29
29
  export function abieEnvNameFromBasename(basenameOfEnvFile) {
30
30
  const m = basenameOfEnvFile.match(ABIE_ENV_FILE_BASENAME_RE)
@@ -35,7 +35,7 @@ export function abieEnvNameFromBasename(basenameOfEnvFile) {
35
35
  * Сканує вміст env-файла, повертає помилки невідповідності кластерного DNS / namespace
36
36
  * для кожного internal URL (один URL у двох змінних = дві окремі помилки).
37
37
  * @param {string} content вміст env-файла (UTF-8)
38
- * @param {'dev' | 'ua'} envName
38
+ * @param {'dev' | 'ua'} envName опис.
39
39
  * @returns {string[]} порожній масив, якщо все OK
40
40
  */
41
41
  export function validateAbieEnvInternalUrls(content, envName) {
@@ -63,7 +63,7 @@ export function validateAbieEnvInternalUrls(content, envName) {
63
63
  * Збирає `*.env` файли, які є abie env (`dev.env`/`ua.env`, опц. з провідною крапкою).
64
64
  * @param {string} root корінь репозиторію
65
65
  * @param {string[]} ignorePaths абсолютні шляхи каталогів-виключень
66
- * @returns {Promise<string[]>}
66
+ * @returns {Promise<string[]>} результат
67
67
  */
68
68
  export async function collectAbieEnvFiles(root, ignorePaths) {
69
69
  /** @type {string[]} */
@@ -2,7 +2,7 @@
2
2
  * Cross-документна аналітика abie HTTPRoute: підрахунок `backendRefs` до спільних
3
3
  * сервісів (`auth-run-hl`, `file-link-hl`) у base-маніфестах пакета (поза overlay `ua`).
4
4
  * Використовується ua_http_route-концерном для синхронізації числа patch-ів namespace
5
- * у overlay із кількістю base-referencе.
5
+ * у overlay із кількістю base-reference.
6
6
  */
7
7
  import { relative } from 'node:path'
8
8
 
@@ -14,7 +14,7 @@ const ABIE_SHARED_CROSS_NS_BACKEND_SET = new Set(ABIE_SHARED_CROSS_NS_BACKEND_NA
14
14
 
15
15
  /**
16
16
  * Перевіряє один `backendRef`: якщо це спільний `-hl` сервіс, має бути `namespace: dev`.
17
- * @param {unknown} br
17
+ * @param {unknown} br опис.
18
18
  * @param {string} rel rel-шлях файла
19
19
  * @param {string[]} errors мутабельний список помилок
20
20
  * @returns {number} 1 — це shared backend, 0 — інакше
@@ -33,8 +33,8 @@ function checkSharedBackendRef(br, rel, errors) {
33
33
  /**
34
34
  * Збирає по HTTPRoute-документу кількість посилань на shared backends і порушення namespace.
35
35
  * @param {unknown} obj корінь YAML
36
- * @param {string} rel
37
- * @returns {{ refCount: number, errors: string[] }}
36
+ * @param {string} rel опис.
37
+ * @returns {{ refCount: number, errors: string[] }} статистика shared-backend посилань і порушення namespace
38
38
  */
39
39
  function httpRouteDocSharedCrossNsBackendStats(obj, rel) {
40
40
  /** @type {string[]} */
@@ -66,7 +66,7 @@ function httpRouteDocSharedCrossNsBackendStats(obj, rel) {
66
66
  * @param {string} root корінь репозиторію
67
67
  * @param {string} pkgAbs абсолютний шлях каталогу пакета
68
68
  * @param {string[]} yamlFilesAbs усі yaml під k8s
69
- * @returns {Promise<{ refCount: number, baseErrors: string[] }>}
69
+ * @returns {Promise<{ refCount: number, baseErrors: string[] }>} агрегована статистика й помилки base-шару
70
70
  */
71
71
  export async function analyzeAbieSharedBackendRefsInPackageK8s(root, pkgAbs, yamlFilesAbs) {
72
72
  const pkgRel = relative(root, pkgAbs).replaceAll('\\', '/') || pkgAbs
@@ -5,7 +5,6 @@
5
5
  *
6
6
  * Кеш — module-level singleton, ключований за `(root, ignorePaths)`. Перший виклик
7
7
  * платить за обхід; наступні концерни в межах того ж прогону отримують готове.
8
- * Для тестів — `resetAbieK8sTreeCache()` (інакше withTmpCwd-фікстури злипатимуться).
9
8
  */
10
9
  import { dirname, relative } from 'node:path'
11
10
 
@@ -20,20 +19,11 @@ const yamlCache = new Map()
20
19
  /** @type {Map<string, Promise<Set<string>>>} */
21
20
  const deploymentCache = new Map()
22
21
 
23
- /**
24
- * Скидає кеш — тести мусять викликати між фікстурами.
25
- * @returns {void}
26
- */
27
- export function resetAbieK8sTreeCache() {
28
- yamlCache.clear()
29
- deploymentCache.clear()
30
- }
31
-
32
22
  /**
33
23
  * Стабільний ключ кешу за (root, ignorePaths).
34
- * @param {string} root
35
- * @param {string[]} ignorePaths
36
- * @returns {string}
24
+ * @param {string} root опис.
25
+ * @param {string[]} ignorePaths опис.
26
+ * @returns {string} результат
37
27
  */
38
28
  function cacheKey(root, ignorePaths) {
39
29
  return `${root}|${[...ignorePaths].toSorted((a, b) => a.localeCompare(b)).join(':')}`
@@ -44,7 +34,7 @@ function cacheKey(root, ignorePaths) {
44
34
  * Каталог `.github/` свідомо пропускається (належить `ga.mdc`).
45
35
  * @param {string} root корінь репозиторію
46
36
  * @param {string[]} [ignorePaths] абсолютні шляхи каталогів-виключень
47
- * @returns {Promise<string[]>}
37
+ * @returns {Promise<string[]>} результат
48
38
  */
49
39
  export function findK8sYamlFiles(root, ignorePaths = []) {
50
40
  const key = cacheKey(root, ignorePaths)
@@ -77,7 +67,25 @@ export function findK8sYamlFiles(root, ignorePaths = []) {
77
67
  * @param {(msg: string) => void} [fail] репортер помилок парсингу (опц.)
78
68
  * @returns {Promise<Set<string>>} абсолютні шляхи директорій
79
69
  */
80
- export function collectDeploymentDirs(root, yamlAbs, fail = () => {}) {
70
+ /**
71
+ * No-op fail-handler за замовчуванням для `collectDeploymentDirs` — пошкоджені YAML
72
+ * під час cross-rule сканування мовчки пропускаються; формальний reporter передає сам caller.
73
+ * @param {string} _msg повідомлення про помилку (ігнорується)
74
+ */
75
+ const silentFail = _msg => {
76
+ // noop
77
+ }
78
+
79
+ /**
80
+ * Знаходить унікальні каталоги, що містять Deployment-маніфести серед переданих YAML-файлів.
81
+ * Парсить документи через `readAndParseYamlDocs` і фільтрує лише ті, що є Deployment.
82
+ * Кешує результат за ключем `root|<sorted yamlAbs>`, щоб повторні виклики не робили I/O.
83
+ * @param {string} root абсолютний корінь репо для побудови relative-шляхів у повідомленнях
84
+ * @param {string[]} yamlAbs абсолютні шляхи до YAML-файлів для перевірки
85
+ * @param {(msg: string) => void} [fail] callback на помилку парсингу (за замовчуванням noop)
86
+ * @returns {Promise<Set<string>>} проміс із сетом абсолютних каталогів, де знайдено Deployment
87
+ */
88
+ export function collectDeploymentDirs(root, yamlAbs, fail = silentFail) {
81
89
  const key = `${root}|${[...yamlAbs].toSorted((a, b) => a.localeCompare(b)).join(':')}`
82
90
  const cached = deploymentCache.get(key)
83
91
  if (cached) return cached
@@ -24,8 +24,8 @@ const ABIE_UA_HTTPROUTE_HOST_MARKERS = ['abie.app', 'vybeerai.com.ua', '*.abie.a
24
24
 
25
25
  /**
26
26
  * Чи patch-рядок містить очікуваний ua nodeSelector (preem: false).
27
- * @param {string} patchText
28
- * @returns {boolean}
27
+ * @param {string} patchText опис.
28
+ * @returns {boolean} результат
29
29
  */
30
30
  function jsonPatchTextHasUaDeploymentNodeSelector(patchText) {
31
31
  if (typeof patchText !== 'string' || patchText.trim() === '') return false
@@ -36,9 +36,9 @@ function jsonPatchTextHasUaDeploymentNodeSelector(patchText) {
36
36
 
37
37
  /**
38
38
  * Чи один елемент `patches` відповідає abie nodeSelector для `mode`.
39
- * @param {unknown} p
40
- * @param {'ua'} mode
41
- * @returns {boolean}
39
+ * @param {unknown} p опис.
40
+ * @param {'ua'} mode опис.
41
+ * @returns {boolean} результат
42
42
  */
43
43
  function inlineKustomizationPatchMatchesAbieMode(p, mode) {
44
44
  if (p === null || typeof p !== 'object' || Array.isArray(p)) return false
@@ -55,9 +55,9 @@ function inlineKustomizationPatchMatchesAbieMode(p, mode) {
55
55
 
56
56
  /**
57
57
  * Чи документ Kustomization містить відповідний inline patch на Deployment.
58
- * @param {import('yaml').Document} doc
59
- * @param {'ua'} mode
60
- * @returns {boolean}
58
+ * @param {import('yaml').Document} doc опис.
59
+ * @param {'ua'} mode опис.
60
+ * @returns {boolean} результат
61
61
  */
62
62
  function kustomizationDocumentHasAbieDeploymentNodeSelectorPatch(doc, mode) {
63
63
  if (doc.errors.length > 0) return false
@@ -76,8 +76,8 @@ function kustomizationDocumentHasAbieDeploymentNodeSelectorPatch(doc, mode) {
76
76
  /**
77
77
  * Чи `kustomization.yaml` містить валідні inline patch для Deployment nodeSelector (ua).
78
78
  * @param {string} raw повний текст файла
79
- * @param {'ua'} mode
80
- * @returns {boolean}
79
+ * @param {'ua'} mode опис.
80
+ * @returns {boolean} результат
81
81
  */
82
82
  export function kustomizationHasAbieDeploymentNodeSelectorPatch(raw, mode) {
83
83
  const body = stripBom(raw)
@@ -100,8 +100,8 @@ export function kustomizationHasAbieDeploymentNodeSelectorPatch(raw, mode) {
100
100
  // ── HTTPRoute (ua) ────────────────────────────────────────────────────────
101
101
 
102
102
  /**
103
- * @param {unknown} p
104
- * @returns {string | null}
103
+ * @param {unknown} p опис.
104
+ * @returns {string | null} результат
105
105
  */
106
106
  function extractHttpRoutePatchString(p) {
107
107
  if (p === null || typeof p !== 'object' || Array.isArray(p)) return null
@@ -116,8 +116,8 @@ function extractHttpRoutePatchString(p) {
116
116
 
117
117
  /**
118
118
  * Збирає inline `patch`-рядки для HTTPRoute (непорожній `target.name`) з одного Kustomization-документа.
119
- * @param {import('yaml').Document} doc
120
- * @returns {string[]}
119
+ * @param {import('yaml').Document} doc опис.
120
+ * @returns {string[]} результат
121
121
  */
122
122
  function collectAbieHttpRoutePatchStringsFromKustomizationDoc(doc) {
123
123
  if (doc.errors.length > 0) return []
@@ -137,7 +137,7 @@ function collectAbieHttpRoutePatchStringsFromKustomizationDoc(doc) {
137
137
  /**
138
138
  * Збирає всі inline JSON6902-фрагменти HTTPRoute (непорожній `target.name`) у kustomization.yaml.
139
139
  * @param {string} raw повний текст файла
140
- * @returns {string}
140
+ * @returns {string} результат
141
141
  */
142
142
  export function getCombinedNginxRunPatchTextFromKustomization(raw) {
143
143
  const body = stripBom(raw)
@@ -162,8 +162,8 @@ export function getCombinedNginxRunPatchTextFromKustomization(raw) {
162
162
  /**
163
163
  * Рахує операції JSON6902 з `path: /spec/rules/.../backendRefs/.../namespace` і `value: ua[-…]`.
164
164
  * @param {string} combined сукупний текст patch
165
- * @param {'ua'} mode
166
- * @returns {number}
165
+ * @param {'ua'} mode опис.
166
+ * @returns {number} результат
167
167
  */
168
168
  function countAbieHttpRouteBackendRefNamespacePatchesInCombined(combined, mode) {
169
169
  if (mode !== 'ua') return 0
@@ -175,7 +175,7 @@ function countAbieHttpRouteBackendRefNamespacePatchesInCombined(combined, mode)
175
175
  /**
176
176
  * Перевіряє сукупний текст patch(ів) HTTPRoute на відповідність abie.mdc.
177
177
  * @param {string} combined сукупний текст patch
178
- * @param {'ua'} mode
178
+ * @param {'ua'} mode опис.
179
179
  * @param {string} [_fullKustomizationRaw] зберігається для API-сумісності, не використовується
180
180
  * @param {number} [sharedCrossNsBackendRefCount] кількість `auth-run-hl`/`file-link-hl` у base HTTPRoute
181
181
  * @returns {string | null} повідомлення про помилку або null
@@ -215,8 +215,8 @@ export function validateAbieNginxRunHttpRoutePatches(
215
215
  /**
216
216
  * Чи kustomization містить валідні patch для HTTPRoute (ua).
217
217
  * @param {string} raw повний текст kustomization.yaml
218
- * @param {'ua'} mode
219
- * @returns {boolean}
218
+ * @param {'ua'} mode опис.
219
+ * @returns {boolean} результат
220
220
  */
221
221
  export function kustomizationHasAbieNginxRunHttpRoutePatch(raw, mode) {
222
222
  const combined = getCombinedNginxRunPatchTextFromKustomization(raw)
@@ -16,8 +16,8 @@ const TRAILING_SLASH_RE = /\/$/u
16
16
 
17
17
  /**
18
18
  * Чи `rel` — це `…/ua/kustomization.yaml` (abie overlay).
19
- * @param {string} rel посі від кореня репозиторію
20
- * @returns {boolean}
19
+ * @param {string} rel posix-шлях від кореня репозиторію
20
+ * @returns {boolean} результат
21
21
  */
22
22
  export function isUaKustomizationPath(rel) {
23
23
  const norm = rel.replaceAll('\\', '/')
@@ -28,7 +28,7 @@ export function isUaKustomizationPath(rel) {
28
28
  * Каталог пакета (батько `k8s/`) для overlay `…/k8s/ua/kustomization.yaml`.
29
29
  * @param {string} root корінь репозиторію
30
30
  * @param {string} kustomizationAbs абсолютний шлях до ua kustomization
31
- * @returns {string | null}
31
+ * @returns {string | null} результат
32
32
  */
33
33
  export function abiePackageDirFromK8sOverlay(root, kustomizationAbs) {
34
34
  const rel = relative(root, kustomizationAbs).replaceAll('\\', '/') || kustomizationAbs
@@ -41,7 +41,7 @@ export function abiePackageDirFromK8sOverlay(root, kustomizationAbs) {
41
41
  * застосовується лише до Vite-пакетів.
42
42
  * @param {string} root корінь репозиторію
43
43
  * @param {string} kustomizationAbs абсолютний шлях до ua kustomization
44
- * @returns {boolean}
44
+ * @returns {boolean} результат
45
45
  */
46
46
  export function abieOverlayRequiresHttpRouteByVite(root, kustomizationAbs) {
47
47
  const pkg = abiePackageDirFromK8sOverlay(root, kustomizationAbs)
@@ -58,7 +58,7 @@ export function abieOverlayRequiresHttpRouteByVite(root, kustomizationAbs) {
58
58
  * @param {Set<string>} deploymentDirs абсолютні каталоги з Deployment
59
59
  * @param {string} root корінь репозиторію
60
60
  * @param {string} kustomizationAbs абсолютний шлях до ua kustomization
61
- * @returns {boolean}
61
+ * @returns {boolean} результат
62
62
  */
63
63
  export function abieOverlayK8sTreeHasDeployment(deploymentDirs, root, kustomizationAbs) {
64
64
  const pkg = abiePackageDirFromK8sOverlay(root, kustomizationAbs)
@@ -73,8 +73,8 @@ export function abieOverlayK8sTreeHasDeployment(deploymentDirs, root, kustomizat
73
73
 
74
74
  /**
75
75
  * Чи rel-шлях `…/k8s/base/…` (base-шар abie, не overlay).
76
- * @param {string} rel
77
- * @returns {boolean}
76
+ * @param {string} rel опис.
77
+ * @returns {boolean} результат
78
78
  */
79
79
  export function isAbieK8sBaseYamlPath(rel) {
80
80
  const norm = rel.replaceAll('\\', '/')
@@ -85,7 +85,7 @@ export function isAbieK8sBaseYamlPath(rel) {
85
85
  * Чи yaml належить до `<pkgRel>/k8s/**` поза `ua/` піддеревом (base-шар abie).
86
86
  * @param {string} relFromRoot шлях від кореня
87
87
  * @param {string} pkgRelFromRoot каталог пакета від кореня
88
- * @returns {boolean}
88
+ * @returns {boolean} результат
89
89
  */
90
90
  export function isK8sYamlInAbiePackageExcludingUaOverlay(relFromRoot, pkgRelFromRoot) {
91
91
  const normRel = relFromRoot.replaceAll('\\', '/')
@@ -12,7 +12,7 @@ export const LINE_SPLIT_RE = /\r?\n/u
12
12
  /**
13
13
  * Прибирає BOM на початку файлу.
14
14
  * @param {string} s вміст
15
- * @returns {string}
15
+ * @returns {string} результат
16
16
  */
17
17
  export function stripBom(s) {
18
18
  return s.startsWith('') ? s.slice(1) : s
@@ -21,7 +21,7 @@ export function stripBom(s) {
21
21
  /**
22
22
  * Чи YAML-документ — це `kind: Deployment`.
23
23
  * @param {unknown} obj корінь YAML-документа
24
- * @returns {boolean}
24
+ * @returns {boolean} результат
25
25
  */
26
26
  export function isDeploymentDoc(obj) {
27
27
  return (
@@ -46,8 +46,8 @@ export const silentFail = _msg => {
46
46
  * викликає `failFn` і повертає `null`.
47
47
  * @param {string} abs абсолютний шлях
48
48
  * @param {string} rel відносний (для повідомлень)
49
- * @param {(msg: string) => void} failFn
50
- * @returns {Promise<import('yaml').Document[] | null>}
49
+ * @param {(msg: string) => void} failFn опис.
50
+ * @returns {Promise<import('yaml').Document[] | null>} результат
51
51
  */
52
52
  export async function readAndParseYamlDocs(abs, rel, failFn) {
53
53
  let raw