@nitra/cursor 1.13.76 → 1.13.82

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 (47) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/package.json +1 -1
  3. package/rules/abie/abie.mdc +11 -3
  4. package/rules/abie/policy/base_deployment_preem/base_deployment_preem.rego +9 -11
  5. package/rules/abie/policy/health_check_policy/health_check_policy.rego +9 -10
  6. package/rules/abie/policy/http_route_base/http_route_base.rego +7 -8
  7. package/rules/changelog/fix/consistency/check.mjs +16 -16
  8. package/{scripts/utils → rules/changelog/fix/consistency}/package-manifest.mjs +1 -1
  9. package/rules/docker/docker.mdc +3 -3
  10. package/rules/docker/fix/lint/check.mjs +3 -3
  11. package/{scripts/utils → rules/docker/fix/lint}/docker-hadolint.mjs +3 -3
  12. package/rules/docker/lint/lint.mjs +2 -2
  13. package/rules/ga/lint/lint.mjs +4 -1
  14. package/rules/graphql/fix/tooling/check.mjs +1 -1
  15. package/{scripts/utils → rules/graphql/fix/tooling}/graphql-gql-scan.mjs +47 -12
  16. package/rules/hasura/fix/internal_urls/check.mjs +1 -1
  17. package/{scripts/utils → rules/js-bun-db/fix/safety}/bun-sql-scan.mjs +1 -1
  18. package/rules/js-bun-db/fix/safety/check.mjs +1 -1
  19. package/rules/js-lint/fix/tooling/check.mjs +3 -15
  20. package/{scripts/utils → rules/js-lint/fix/tooling}/rebuild-oxlint-canonical.mjs +1 -1
  21. package/rules/js-lint/js-lint.mdc +2 -2
  22. package/rules/js-mssql/fix/deps/check.mjs +1 -1
  23. package/{scripts/utils → rules/js-mssql/fix/deps}/mssql-pool-scan.mjs +1 -1
  24. package/{scripts/utils → rules/js-run/fix/runtime}/bunyan-imports.mjs +1 -1
  25. package/{scripts/utils → rules/js-run/fix/runtime}/check-env-scan.mjs +1 -1
  26. package/rules/js-run/fix/runtime/check.mjs +5 -5
  27. package/{scripts/utils → rules/js-run/fix/runtime}/conn-file-rules.mjs +1 -1
  28. package/{scripts/utils → rules/js-run/fix/runtime}/conn-imports-scan.mjs +1 -1
  29. package/{scripts/utils → rules/js-run/fix/runtime}/promise-settimeout-scan.mjs +1 -1
  30. package/rules/k8s/k8s.mdc +1 -1
  31. package/rules/npm-module/fix/package_structure/check.mjs +11 -1
  32. package/rules/test/auto.md +1 -0
  33. package/rules/test/fix/location/check.mjs +77 -0
  34. package/rules/test/test.mdc +60 -0
  35. package/rules/vue/fix/packages/check.mjs +2 -2
  36. package/rules/vue/vue.mdc +1 -1
  37. package/scripts/auto-rules.mjs +3 -3
  38. package/scripts/utils/ast-scan-utils.mjs +3 -2
  39. package/scripts/utils/with-lock.mjs +120 -0
  40. package/scripts/utils/workspaces.mjs +1 -1
  41. package/scripts/utils/worktree-fingerprint.mjs +30 -0
  42. /package/{scripts/utils → rules/docker/fix/lint}/docker-mirror.mjs +0 -0
  43. /package/{scripts/utils → rules/js-lint/fix/tooling}/knip-canonical.json +0 -0
  44. /package/{scripts/utils → rules/js-lint/fix/tooling}/oxlint-canonical-skeleton.json +0 -0
  45. /package/{scripts/utils → rules/js-lint/fix/tooling}/oxlint-canonical.json +0 -0
  46. /package/{scripts/utils → rules/js-lint/fix/tooling}/oxlint-rules.tsv +0 -0
  47. /package/{scripts/utils → rules/vue/fix/packages}/vue-forbidden-imports.mjs +0 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,76 @@
4
4
 
5
5
  Формат — [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), нумерація — [SemVer](https://semver.org/lang/uk/).
6
6
 
7
+ ## [1.13.82] - 2026-05-23
8
+
9
+ ### Changed
10
+
11
+ - **`rules/test`: виняток для `*_test.rego` файлів — лишаються поряд із полісі (OPA/Conftest community-конвенція)**:
12
+ - **Rego unit-тести (`*_test.rego`) лежать у тому самому каталозі, що й `<name>.rego`** — за загальноприйнятим патерном OPA/Conftest. `package <name>` (полісі) ↔ `package <name>_test` (тест) семантично зв'язані через `package`-декларації, а не локацію файлу; `conftest verify -p <dir>` рекурсивний, тож знаходить тест незалежно від місця, але спільнота тримає їх поруч (бачимо у OPA examples, Gatekeeper library, Datree, Styra DAS bundles). Це **легітимне відхилення** від внутрішньої JS-конвенції «`tests/` всюди» на користь OPA-ідіоми.
13
+ - `rules/test/test.mdc` v1.1 — додано **окрему секцію про виняток** для Rego: «`*_test.rego` лишаються поряд із полісі, бо це загальноприйнятий OPA/Conftest community-патерн» (з прикладом структури `policy/<concern>/{<name>.rego, <name>_test.rego, target.json}`).
14
+ - `rules/test/fix/location/check.mjs` — перевіряє **лише `*.test.mjs`**, `*_test.rego` свідомо виключено з область перевірки (зафіксовано у docstring).
15
+ - Додано test-case у `rules/test/fix/location/tests/check.test.mjs`: `*_test.rego` поряд із полісі НЕ є порушенням.
16
+ - **Відкат переміщення `*_test.rego`**: 69 файлів, які раніше було помилково перенесено у `policy/<concern>/tests/<name>_test.rego`, повернуто у `policy/<concern>/<name>_test.rego` через `git mv`. Порожні `tests/` піддиректорії під `policy/` видалено.
17
+ - **`npx @nitra/cursor check test`** охоплює лише JS-тести: «✅ Всі 77 файлів *.test.mjs у каталозі tests/». Rego-тести продовжують перевірятись через `conftest verify` у правилі `rego`.
18
+
19
+ ## [1.13.81] - 2026-05-23
20
+
21
+ ### Fixed
22
+
23
+ - **`npm-module.package_structure`: carve-out для rule-name сегмента** у `classifyPublishedFileAsTest`. Раніше для шляху `rules/<X>/...` сегмент `<X>` піддавався TEST_DIR_NAMES-перевірці, що давало false positive на правилах із id, що збігається з test-style ім'ям (`test`, `tests`, `fixtures` тощо). Тепер сегмент індекс 1 (ім'я правила, коли індекс 0 — `rules`) пропускається; глибші сегменти (`rules/<r>/fix/<c>/tests/`) продовжують перевірятись.
24
+
25
+ ### Added
26
+
27
+ - **Нове правило `test` (`npm/rules/test/`)** — програмний канон розміщення тестів (ADR `docs/adr/20260523-154806-...`):
28
+ - `test.mdc` — конвенція «`*.test.mjs` живуть у `tests/` поряд із джерелом», з описом спецвипадків (root `tests/`, fixtures у `tests/__fixtures__/` і `tests/fixtures/`, test-helpers як shared-infra).
29
+ - `fix/location/check.mjs` — обхід дерева `walkDir`'ом (зі стандартним skip-листом + `.n-cursor.json:ignore`); для кожного `*.test.mjs` басенейм батьківської директорії має бути `tests`, інакше fail з вказівкою куди перенести.
30
+ - `fix/location/tests/check.test.mjs` — 6 тестів самого правила (eats own dogfood).
31
+ - `auto.md` — auto-enable умова: «якщо у проекті є хоча б один файл `*.test.mjs`».
32
+ - Додано `"test"` у `.n-cursor.json:rules` репо `@nitra/cursor`.
33
+ - Додано `"ignore": [".claude/worktrees"]` у `.n-cursor.json` — щоб правило не звітувало про знімки в git worktrees.
34
+
35
+ ### Changed
36
+
37
+ - **Тести переміщено з-поряд-із-файлом у `dir/tests/` піддиректорію** (ADR `docs/adr/20260523-154806-...`):
38
+ - **73 sibling-тести** у `rules/...` і `scripts/...`: для кожного `dir/X.test.mjs` → `dir/tests/X.test.mjs` із автоматичним оновленням relative imports.
39
+ - **3 integration-тести у `npm/tests/`** — без змін (вже відповідали конвенції).
40
+ - **`npm/scripts/utils/__fixtures__/`** → `npm/scripts/utils/tests/__fixtures__/`.
41
+ - **`npm/rules/nginx-default-tpl/fix/template/fixtures/`** → `.../tests/fixtures/`; посилання в `npm/tests/check-rule-fixtures.test.mjs` оновлено.
42
+ - Ручні фіксапи 4 тестів із HERE/`..` path patterns (sync-setup-bun-deps-action, inline-template-links, rules/adr/fix/hooks, rules/abie/utils/enabled) — додано додатковий `..`, бо тести стали на рівень глибше.
43
+ - `package.json#files` негативні globs (`!**/*.test.mjs`, `!**/__fixtures__/**`, `!**/fixtures/**`) працюють рекурсивно — без змін.
44
+ - **77 тестів** проходять у новому layout (76 існуючих + 1 нового правила): `bun test` 843 pass / 2 fail (обидва — pre-existing `with-lock` issues, не пов'язані).
45
+ - `npx @nitra/cursor check test` → `✅ Всі 77 файлів *.test.mjs у каталозі tests/ (test.mdc)`.
46
+
47
+ ## [1.13.79] - 2026-05-23
48
+
49
+ ### Changed
50
+
51
+ - **Перенесення single-rule сканерів і canonical-конфігів з `npm/scripts/utils/` у `npm/rules/<rule>/fix/<sub>/`** (узгоджено з конвенцією `rules/ga/fix/workflows/`, `rules/nginx-default-tpl/fix/template/` тощо; ADR `docs/adr/20260523-114913-...`, який supersede `20260523-112217-...`):
52
+ - **js-lint** (`rules/js-lint/fix/tooling/`): `knip-canonical.json`, `oxlint-canonical.json`, `oxlint-canonical-skeleton.json`, `oxlint-rules.tsv`, `rebuild-oxlint-canonical.mjs`. Константи `OXLINT_CANONICAL_JSON_PATH` / `KNIP_CANONICAL_JSON_PATH` у `check.mjs` стали локальними (без 4-річневих `..`).
53
+ - **js-run** (`rules/js-run/fix/runtime/`): `bunyan-imports.mjs` (+test), `check-env-scan.mjs`, `conn-file-rules.mjs` (+test), `conn-imports-scan.mjs` (+test), `promise-settimeout-scan.mjs` (+test).
54
+ - **docker** (`rules/docker/fix/lint/`): `docker-hadolint.mjs` (+test), `docker-mirror.mjs`. `rules/docker/lint/lint.mjs` тепер імпортує з `../fix/lint/docker-hadolint.mjs`.
55
+ - **js-bun-db** (`rules/js-bun-db/fix/safety/`): `bun-sql-scan.mjs`.
56
+ - **js-mssql** (`rules/js-mssql/fix/deps/`): `mssql-pool-scan.mjs`.
57
+ - **changelog** (`rules/changelog/fix/consistency/`): `package-manifest.mjs` (+test).
58
+ - **vue** (`rules/vue/fix/packages/`): `vue-forbidden-imports.mjs` (+test).
59
+ - **graphql** (`rules/graphql/fix/tooling/`): `graphql-gql-scan.mjs`. Cross-rule імпорту немає: `extractVueScriptBlocks`, локалізована `contentForGqlScan` і власні `isGqlScanSourceFile` / `shouldSkipFileForGqlScan` (з власною source-regex і skip-list `.d.ts` / `auto-imports.d.ts` / `components.d.ts`) дубльовані всередині `graphql-gql-scan.mjs` — правила залишаються самодостатніми.
60
+ - **`scripts/auto-rules.mjs`** оновлено: імпорти переадресовано на нові локації трьох сканерів (`bun-sql-scan`, `graphql-gql-scan`, `vue-forbidden-imports`).
61
+ - **`.mdc`-документація** оновлена: `rules/js-lint/js-lint.mdc`, `rules/docker/docker.mdc`, `rules/vue/vue.mdc`, `.cursor/rules/n-js-lint.mdc`, `.cursor/rules/n-vue.mdc` — посилання на нові шляхи canonical-файлів і сканерів.
62
+
63
+ ## [1.13.78] - 2026-05-23
64
+
65
+ ### Changed
66
+
67
+ - **abie / k8s / hasura — актуалізація посилань на неіснуючий `check-abie.mjs`:** після реструктуризації `rules/abie/` на `fix/<concern>/check.mjs` (+ Rego-пакети у `policy/`) монолітного `check-abie.mjs` більше немає; застарілі посилання в активних `.mdc`/`.rego`/`.mjs` (поза історичним `CHANGELOG.md`) оновлено:
68
+ - `npm/rules/abie/abie.mdc` — три згадки замінено: пер-документна перевірка HTTPRoute base hostnames → Rego `abie.http_route_base`; env-DNS-скан → `fix/env_dns/check.mjs`; cross-file/FS-логіку розбито за концернами (`hc_pairing/`, `ua_http_route/`, `ua_node_selector/`, `env_dns/`, `firebase_hosting/`) з поясненням, що `targetRef.name -hl` cross-check обчислюється з `hcp.metadata.name` у Rego.
69
+ - `npm/rules/abie/policy/{http_route_base,base_deployment_preem,health_check_policy}/*.rego` — у шапках замінено `npm/scripts/check-abie.mjs` на актуальні джерела: cross-file gating через `policy/<pkg>/target.json` (glob), rule-level applies-гейт у `fix/applies/check.mjs`, FS-парність HCP↔Deployment у `fix/hc_pairing/check.mjs`. Прибрано згадки видалених JS-функцій (`validateAbieHcPolicy`, `deploymentDocumentHasAbieBasePreemNodeSelector`).
70
+ - `npm/rules/k8s/k8s.mdc` — рядок про `targetRef -hl` для abie-проєктів вказує на Rego-пакет `abie.health_check_policy` + `abie.mdc` (замість `check-abie.mjs`).
71
+ - `npm/rules/hasura/fix/internal_urls/check.mjs` — у JSDoc згадку `check-abie` замінено на нейтральне «abie-перевірки».
72
+
73
+ ### Added
74
+
75
+ - **`with-lock`:** атомарний `mkdirSync`-лок + SHA-256 fingerprint-дедуп для важких команд; пілот — `lint-ga` автоматично серіалізує паралельні запуски та пропускає дублікати при незміненому робочому дереві (TTL 10 хв). Нові модулі: `scripts/utils/worktree-fingerprint.mjs`, `scripts/utils/with-lock.mjs`.
76
+
7
77
  ## [1.13.76] - 2026-05-22
8
78
 
9
79
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "1.13.76",
3
+ "version": "1.13.82",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",
@@ -36,7 +36,7 @@ spec:
36
36
 
37
37
  ### HTTPRoute: спільні сервіси **`auth-run-hl`**, **`file-link-hl`**
38
38
 
39
- У **HTTPRoute** у шляху з **`…/k8s/base/…`** у **`spec.hostnames`** дозволені лише **`aiml.live`**, **`*.aiml.live`** та інші піддомени **aiml.live** (перевірка в **`check-abie.mjs`**).
39
+ У **HTTPRoute** у шляху з **`…/k8s/base/…`** у **`spec.hostnames`** дозволені лише **`aiml.live`**, **`*.aiml.live`** та інші піддомени **aiml.live** (перевірка Rego-пакет **`abie.http_route_base`**, див. розділ нижче).
40
40
 
41
41
  Ці **Service** (headless **`-hl`**) живуть у **базовому** неймспейсі **`dev`**. У маніфесті **HTTPRoute** під **`k8s`** (шар без **`ua/`** — наприклад **`…/k8s/base/hr.yaml`**) для кожного **`backendRefs`** до такого сервісу явно вкажи **`namespace: dev`** і порт **8080**:
42
42
 
@@ -133,7 +133,7 @@ KVCMS_URL=http://kvcms-hl.ua-apruv.svc.abie-ua.internal:8080
133
133
 
134
134
  `<namespace>` (наприклад `dev-apruv` / `ua-apruv`) — `metadata.name` цільового namespace після kustomize-overlay для відповідного середовища; `<service>` — `metadata.name` headless Service (`-hl`) того сервісу, до якого йде URL.
135
135
 
136
- **Перевірка `check-abie.mjs`** сканує всі `*.env` файли, basename яких збігається з `dev.env` / `ua.env` (з провідною крапкою чи без), знаходить **усі** internal URL (`http://<svc>.<ns>.svc.<dns>` — як для Hasura-ендпоінта, так і для KVCMS чи будь-якого іншого) і вимагає, щоб для кожного:
136
+ **Перевірка `fix/env_dns/check.mjs`** сканує всі `*.env` файли, basename яких збігається з `dev.env` / `ua.env` (з провідною крапкою чи без), знаходить **усі** internal URL (`http://<svc>.<ns>.svc.<dns>` — як для Hasura-ендпоінта, так і для KVCMS чи будь-якого іншого) і вимагає, щоб для кожного:
137
137
 
138
138
  - DNS-суфікс відповідав env: `abie-dev.internal` / `abie-ua.internal`;
139
139
  - namespace починався з `dev-` / `ua-` відповідно.
@@ -170,4 +170,12 @@ bun add -d @nitra/abie-docs
170
170
  - **`clean_merged_ignore_branches/`** → `abie.clean_merged_ignore_branches` — у workflow `.github/workflows/clean-merged-branch.yml` крок з `uses: phpdocker-io/github-actions-delete-abandoned-branches` має `with.ignore_branches`, що містить токени `dev,ua` (case-insensitive). **Цільові файли:** `.github/workflows/clean-merged-branch.yml`.
171
171
  - **`package_json_docs/`** → `abie.package_json_docs` — у кореневому `package.json` `devDependencies` має містити `@nitra/abie-docs` (presence-only, версію не фіксуємо). **Цільові файли:** `package.json`.
172
172
 
173
- Cross-file логіка (парність HCP↔Deployment у каталозі, обчислений `<deployment.name>-hl` для `targetRef.name`, валідація ua-overlay JSON6902 patches на HTTPRoute, env→cluster DNS, аналіз cross-namespace backendRefs у пакетах) лишається у **`check-abie.mjs`** — Rego не читає файлову систему й не робить cross-document резолюцію.
173
+ Cross-file / FS-логіка лишається у JS-частинах (`fix/<concern>/check.mjs`) — Rego не читає файлову систему й не робить cross-document резолюцію:
174
+
175
+ - парність HCP↔Deployment у каталозі та modeline `hc.yaml` — `fix/hc_pairing/check.mjs`;
176
+ - валідація ua-overlay JSON6902 patches на HTTPRoute + аналіз cross-namespace `backendRefs` у пакетах — `fix/ua_http_route/check.mjs`;
177
+ - ua-overlay JSON6902 patch на `Deployment.nodeSelector` (`preem: false`) — `fix/ua_node_selector/check.mjs`;
178
+ - env→cluster DNS (`*.dev.env` / `*.ua.env`) — `fix/env_dns/check.mjs`;
179
+ - скан артефактів Firebase Hosting у підкаталогах першого рівня — `fix/firebase_hosting/check.mjs`.
180
+
181
+ Точна звірка `targetRef.name` HealthCheckPolicy з суфіксом `-hl` обчислюється з `hcp.metadata.name` і живе у Rego (`abie.health_check_policy`); за конвенцією `hcp.metadata.name` дорівнює `<deployment.name>`, тому окремий cross-file lookup до маніфесту Deployment не потрібен.
@@ -1,19 +1,17 @@
1
- # Порт перевірки `deploymentDocumentHasAbieBasePreemNodeSelector` з
2
- # `npm/scripts/check-abie.mjs` (abie.mdc): кожен `Deployment` у файлах під
3
- # `…/k8s/.../base/…` має `spec.template.spec.nodeSelector.preem` зі
4
- # значенням, що вважається істинним (boolean `true` або рядок `"true"`
5
- # без урахування регістру). Overlay ua далі підміняє селектор
6
- # JSON6902-патчем на `preem: false`.
1
+ # Перевірка (abie.mdc): кожен `Deployment` у файлах під `…/k8s/.../base/…` має
2
+ # `spec.template.spec.nodeSelector.preem` зі значенням, що вважається істинним
3
+ # (boolean `true` або рядок `"true"` без урахування регістру). Overlay ua далі
4
+ # підміняє селектор JSON6902-патчем на `preem: false`
5
+ # (див. `fix/ua_node_selector/check.mjs`).
7
6
  #
8
7
  # Запуск (локально, лише для одного base-YAML з Deployment):
9
8
  # conftest test path/to/k8s/base/deployment.yaml \
10
- # -p npm/policy/abie/base_deployment_preem \
9
+ # -p npm/rules/abie/policy/base_deployment_preem \
11
10
  # --namespace abie.base_deployment_preem
12
11
  #
13
- # JS відбирає файли під `…/k8s/.../base/…` (через `isAbieK8sBaseYamlPath`) і
14
- # викликає conftest з цією намеспейс. JS authoritative (`check-abie.mjs`:
15
- # `deploymentDocumentHasAbieBasePreemNodeSelector` + `ensureAbieBaseDeploymentPreemNodeSelector`).
16
- # Cross-file gating (правило `abie` у `.n-cursor.json`, шлях файла) — у JS.
12
+ # Cross-file gating: шлях `…/k8s/.../base/…` фільтрується через
13
+ # `policy/base_deployment_preem/target.json` (glob). Rule-level applies-гейт
14
+ # `fix/applies/check.mjs` (поле `rules` у `.n-cursor.json`).
17
15
  #
18
16
  # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
19
17
  # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
@@ -1,9 +1,8 @@
1
- # Порт структурної перевірки `HealthCheckPolicy` з
2
- # `npm/scripts/check-abie.mjs` (abie.mdc).
1
+ # Структурна перевірка `HealthCheckPolicy` (abie.mdc).
3
2
  #
4
3
  # Запуск (локально):
5
4
  # conftest test path/to/k8s/.../hc.yaml \
6
- # -p npm/policy/abie/health_check_policy \
5
+ # -p npm/rules/abie/policy/health_check_policy \
7
6
  # --namespace abie.health_check_policy
8
7
  #
9
8
  # Перевіряє для `kind: HealthCheckPolicy`:
@@ -14,12 +13,13 @@
14
13
  # починається з `/`;
15
14
  # - `spec.default.config.httpHealthCheck.port: 8080`;
16
15
  # - `spec.targetRef.kind: Service`;
17
- # - `spec.targetRef.name` має суфікс `-hl` (headless backend).
16
+ # - `spec.targetRef.name` `<hcp.metadata.name>-hl` (exact, з нормалізацією
17
+ # суфікса).
18
18
  #
19
- # Cross-file gating (правило `abie` у `.n-cursor.json`, парність з Deployment-каталогу,
20
- # точна звірка `targetRef.name` з обчисленим `<deployment.name>-hl`) — у JS
21
- # (`check-abie.mjs`: `validateAbieHcPolicy`, `checkHcYamlFiles`). JS authoritative;
22
- # ця Rego — швидкий gate для одиничного YAML (наприклад через IDE).
19
+ # Cross-file gating: glob по `hc.yaml` у k8s-дереві у
20
+ # `policy/health_check_policy/target.json`. FS-парність HCP↔Deployment та
21
+ # modeline `hc.yaml` `fix/hc_pairing/check.mjs`. Rule-level applies-гейт —
22
+ # `fix/applies/check.mjs`.
23
23
  #
24
24
  # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
25
25
  # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
@@ -122,8 +122,7 @@ deny contains msg if {
122
122
  }
123
123
 
124
124
  # Нормалізація: якщо `metadata.name` уже закінчується на `-hl` — використовуємо
125
- # як є; інакше додаємо суфікс. Узгоджено з `validateAbieHcPolicy`
126
- # у `check-abie.mjs`.
125
+ # як є; інакше додаємо суфікс.
127
126
  expected_target_ref_name(name) := name if {
128
127
  endswith(name, "-hl")
129
128
  } else := concat("", [name, "-hl"])
@@ -1,15 +1,14 @@
1
- # Порт перевірки `HTTPRoute` у шарі `…/k8s/.../base/...` з
2
- # `npm/scripts/check-abie.mjs` (abie.mdc): дозволені лише hostnames з домену
3
- # `aiml.live` (включно з піддоменами та `*.aiml.live`).
1
+ # Перевірка `HTTPRoute` у шарі `…/k8s/.../base/...` (abie.mdc): дозволені лише
2
+ # hostnames з домену `aiml.live` (включно з піддоменами та `*.aiml.live`).
4
3
  #
5
4
  # Запуск (локально):
6
- # conftest test path/to/k8s/base/hr.yaml -p npm/policy/abie \
5
+ # conftest test path/to/k8s/base/hr.yaml \
6
+ # -p npm/rules/abie/policy/http_route_base \
7
7
  # --namespace abie.http_route_base
8
8
  #
9
- # Cross-file gating (саме шлях `…/base/…` визначає, чи застосовувати правило)
10
- # у JS: conftest викликаємо лише на YAML-ах з base/. Тут — лише валідація вмісту
11
- # `spec.hostnames`. JS authoritative (`check-abie.mjs`) — ця Rego гейт для
12
- # одиничного YAML.
9
+ # Cross-file gating (саме шлях `…/k8s/.../base/...` визначає, чи застосовувати
10
+ # правило) задає glob у `policy/http_route_base/target.json`. Тут — лише
11
+ # валідація вмісту `spec.hostnames`. Rule-level applies-гейт `fix/applies/check.mjs`.
13
12
  #
14
13
  # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
15
14
  # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
@@ -28,7 +28,7 @@ import {
28
28
  manifestFilePath,
29
29
  parsePyprojectFields,
30
30
  readPackageManifest
31
- } from '../../../../scripts/utils/package-manifest.mjs'
31
+ } from './package-manifest.mjs'
32
32
 
33
33
  const execFileAsync = promisify(execFile)
34
34
 
@@ -247,7 +247,7 @@ async function workspaceHasRelevantChangesAgainstBase(baseRef, ws, subWorkspaces
247
247
  /**
248
248
  * Версія з маніфесту на `baseRef`.
249
249
  * @param {string} baseRef параметр
250
- * @param {import('../../../../scripts/utils/package-manifest.mjs').PackageManifest} manifest параметр
250
+ * @param {import('./package-manifest.mjs').PackageManifest} manifest параметр
251
251
  * @returns {Promise<string | null>} результат
252
252
  */
253
253
  async function readBaseVersion(baseRef, manifest) {
@@ -308,8 +308,8 @@ async function defaultGetPublishedPyPiVersion(name) {
308
308
  }
309
309
 
310
310
  /**
311
- * @param {import('../../../../scripts/utils/package-manifest.mjs').PackageManifest} manifest параметр
312
- * @param {(name: string, kind?: import('../../../../scripts/utils/package-manifest.mjs').PackageKind) => Promise<string | null>} getPublishedVersion параметр
311
+ * @param {import('./package-manifest.mjs').PackageManifest} manifest параметр
312
+ * @param {(name: string, kind?: import('./package-manifest.mjs').PackageKind) => Promise<string | null>} getPublishedVersion параметр
313
313
  * @returns {Promise<string | null>} результат
314
314
  */
315
315
  function resolvePublishedVersion(manifest, getPublishedVersion) {
@@ -319,7 +319,7 @@ function resolvePublishedVersion(manifest, getPublishedVersion) {
319
319
 
320
320
  /**
321
321
  * @param {string} name пакет
322
- * @param {import('../../../../scripts/utils/package-manifest.mjs').PackageKind} [kind] тип пакета
322
+ * @param {import('./package-manifest.mjs').PackageKind} [kind] тип пакета
323
323
  * @returns {Promise<string | null>} опублікована версія або null
324
324
  */
325
325
  function defaultGetPublishedVersion(name, kind = 'npm') {
@@ -330,14 +330,14 @@ function defaultGetPublishedVersion(name, kind = 'npm') {
330
330
  }
331
331
 
332
332
  /**
333
- * @returns {(name: string, kind?: import('../../../../scripts/utils/package-manifest.mjs').PackageKind) => Promise<string | null>} стандартний резолвер
333
+ * @returns {(name: string, kind?: import('./package-manifest.mjs').PackageKind) => Promise<string | null>} стандартний резолвер
334
334
  */
335
335
  function createDefaultGetPublishedVersion() {
336
336
  return defaultGetPublishedVersion
337
337
  }
338
338
 
339
339
  /**
340
- * @param {import('../../../../scripts/utils/package-manifest.mjs').PackageManifest} manifest параметр
340
+ * @param {import('./package-manifest.mjs').PackageManifest} manifest параметр
341
341
  * @param {(msg: string) => void} pass параметр
342
342
  * @param {(msg: string) => void} fail параметр
343
343
  */
@@ -375,7 +375,7 @@ async function verifyChangelogEntry(ws, version, pass, fail) {
375
375
  }
376
376
 
377
377
  /**
378
- * @param {import('../../../../scripts/utils/package-manifest.mjs').PackageManifest} manifest параметр
378
+ * @param {import('./package-manifest.mjs').PackageManifest} manifest параметр
379
379
  * @returns {string} результат
380
380
  */
381
381
  function workspaceLabel(manifest) {
@@ -383,7 +383,7 @@ function workspaceLabel(manifest) {
383
383
  }
384
384
 
385
385
  /**
386
- * @param {import('../../../../scripts/utils/package-manifest.mjs').PackageManifest} manifest параметр
386
+ * @param {import('./package-manifest.mjs').PackageManifest} manifest параметр
387
387
  * @param {string} Vcurrent параметр
388
388
  * @param {string[]} subWorkspaces параметр
389
389
  * @param {(msg: string) => void} pass параметр
@@ -438,9 +438,9 @@ async function checkPublishedWorkspacePendingGitChanges(manifest, Vcurrent, subW
438
438
  }
439
439
 
440
440
  /**
441
- * @param {import('../../../../scripts/utils/package-manifest.mjs').PackageManifest} manifest параметр
441
+ * @param {import('./package-manifest.mjs').PackageManifest} manifest параметр
442
442
  * @param {string[]} subWorkspaces параметр
443
- * @param {(name: string, kind?: import('../../../../scripts/utils/package-manifest.mjs').PackageKind) => Promise<string | null>} getPublishedVersion параметр
443
+ * @param {(name: string, kind?: import('./package-manifest.mjs').PackageKind) => Promise<string | null>} getPublishedVersion параметр
444
444
  * @param {(msg: string) => void} pass параметр
445
445
  * @param {(msg: string) => void} fail параметр
446
446
  * @returns {Promise<void>} результат
@@ -475,7 +475,7 @@ async function checkPublishedWorkspace(manifest, subWorkspaces, getPublishedVers
475
475
 
476
476
  /**
477
477
  * @param {string} comparisonRef ref/SHA для `git diff` / `git show`
478
- * @param {import('../../../../scripts/utils/package-manifest.mjs').PackageManifest} manifest параметр
478
+ * @param {import('./package-manifest.mjs').PackageManifest} manifest параметр
479
479
  * @param {string} baseLabel параметр
480
480
  * @param {(msg: string) => void} pass параметр
481
481
  * @param {(msg: string) => void} fail параметр
@@ -507,7 +507,7 @@ async function checkLocalOnlyChangedWorkspace(comparisonRef, manifest, baseLabel
507
507
  }
508
508
 
509
509
  /**
510
- * @param {import('../../../../scripts/utils/package-manifest.mjs').PackageManifest[]} localOnly параметр
510
+ * @param {import('./package-manifest.mjs').PackageManifest[]} localOnly параметр
511
511
  * @param {string[]} subWorkspaces параметр
512
512
  * @param {(msg: string) => void} pass параметр
513
513
  * @param {(msg: string) => void} fail параметр
@@ -543,7 +543,7 @@ async function runLocalOnlyChecks(localOnly, subWorkspaces, pass, fail) {
543
543
 
544
544
  /**
545
545
  * @param {object} [opts] опції перевірки
546
- * @param {(name: string, kind?: import('../../../../scripts/utils/package-manifest.mjs').PackageKind) => Promise<string | null>} [opts.getPublishedVersion] перевизначення npm/PyPI у тестах
546
+ * @param {(name: string, kind?: import('./package-manifest.mjs').PackageKind) => Promise<string | null>} [opts.getPublishedVersion] перевизначення npm/PyPI у тестах
547
547
  * @returns {Promise<number>} exit-код перевірки
548
548
  */
549
549
  export async function check(opts = {}) {
@@ -558,11 +558,11 @@ export async function check(opts = {}) {
558
558
  const isMonorepoRoot = subWorkspaces.length > 0
559
559
 
560
560
  /**
561
- @type {import('../../../../scripts/utils/package-manifest.mjs').PackageManifest[]}
561
+ @type {import('./package-manifest.mjs').PackageManifest[]}
562
562
  */
563
563
  const published = []
564
564
  /**
565
- @type {import('../../../../scripts/utils/package-manifest.mjs').PackageManifest[]}
565
+ @type {import('./package-manifest.mjs').PackageManifest[]}
566
566
  */
567
567
  const localOnly = []
568
568
 
@@ -8,7 +8,7 @@ import { dirname, join, relative } from 'node:path'
8
8
 
9
9
  import { parse as parseToml } from 'smol-toml'
10
10
 
11
- import { getMonorepoPackageRootDirs, isIgnoredWorkspaceRoot } from './workspaces.mjs'
11
+ import { getMonorepoPackageRootDirs, isIgnoredWorkspaceRoot } from '../../../../scripts/utils/workspaces.mjs'
12
12
 
13
13
  /**
14
14
  * @typedef {'npm' | 'python'} PackageKind
@@ -7,7 +7,7 @@ alwaysApply: false
7
7
 
8
8
  # Docker — hadolint
9
9
 
10
- Для образів з Docker Hub — **`oven/bun`**, **`alpine`**, **`nginxinc/nginx-unprivileged`**, **`node`** — у **`FROM`** треба вказувати дзеркало GCR, а не pull напряму з Hub: **`mirror.gcr.io/oven/bun`**, **`mirror.gcr.io/library/alpine`**, **`mirror.gcr.io/nginxinc/nginx-unprivileged`**, **`mirror.gcr.io/library/node`**. Перевіряє **`check-docker.mjs`**, деталі в **`npm/scripts/utils/docker-mirror.mjs`**.
10
+ Для образів з Docker Hub — **`oven/bun`**, **`alpine`**, **`nginxinc/nginx-unprivileged`**, **`node`** — у **`FROM`** треба вказувати дзеркало GCR, а не pull напряму з Hub: **`mirror.gcr.io/oven/bun`**, **`mirror.gcr.io/library/alpine`**, **`mirror.gcr.io/nginxinc/nginx-unprivileged`**, **`mirror.gcr.io/library/node`**. Перевіряє **`check-docker.mjs`**, деталі в **`npm/rules/docker/fix/lint/docker-mirror.mjs`**.
11
11
 
12
12
  Також Dockerfile/Containerfile **має бути multistage build**: окремий build stage (залежності/компіляція) і окремий runtime stage. У фінальному stage дозволені лише мінімальні базові образи:
13
13
 
@@ -99,7 +99,7 @@ CLI **`hadolint`** приймає лише **явні шляхи** (`[DOCKERFILE
99
99
 
100
100
  **Область lint-docker (вужча, ніж `check docker`):** лише файли з іменем **`Dockerfile`** та **`*.Dockerfile`** (суфікс **`.dockerfile`** без урахування регістру, наприклад **`api.Dockerfile`**). Файли **`Dockerfile.prod`**, **`Containerfile`** тощо **не** входять у **`lint-docker`**; їх ловить **`check docker`** (`check-docker.mjs`).
101
101
 
102
- Обхід: **`walkDir`** з тими самими пропусками каталогів, що й **`check-docker.mjs`**. Виклик **`hadolint`**: **`PATH`**, інакше **`docker run`** — спільна логіка **`npm/scripts/utils/docker-hadolint.mjs`**.
102
+ Обхід: **`walkDir`** з тими самими пропусками каталогів, що й **`check-docker.mjs`**. Виклик **`hadolint`**: **`PATH`**, інакше **`docker run`** — спільна логіка **`npm/rules/docker/fix/lint/docker-hadolint.mjs`**.
103
103
 
104
104
  - Канон `package.json#scripts.lint-docker`: [package.json.snippet.json](./policy/package_json/template/package.json.snippet.json)
105
105
 
@@ -109,7 +109,7 @@ CLI **`hadolint`** приймає лише **явні шляхи** (`[DOCKERFILE
109
109
 
110
110
  - Канон: [lint-docker.yml.snippet.yml](./policy/lint_docker_yml/template/lint-docker.yml.snippet.yml)
111
111
 
112
- Узгоджуй версію hadolint **v2.12.0** з **`HADOLINT_IMAGE`** у **`npm/scripts/utils/docker-hadolint.mjs`**.
112
+ Узгоджуй версію hadolint **v2.12.0** з **`HADOLINT_IMAGE`** у **`npm/rules/docker/fix/lint/docker-hadolint.mjs`**.
113
113
 
114
114
  Кореневий скрипт **`lint`** (див. **`n-bun.mdc`**) **обов'язково** містить **`bun run lint-docker`**, коли в проєкті підключено правило **`docker`**.
115
115
 
@@ -2,7 +2,7 @@
2
2
  * Запускає hadolint для Dockerfile / Containerfile у всьому репозиторії (див. docker.mdc).
3
3
  *
4
4
  * Додатково переконуються, що образи `oven/bun`, `alpine`, `nginx`, `node` з Docker Hub
5
- * вказуються через `mirror.gcr.io` (див. `utils/docker-mirror.mjs`).
5
+ * вказуються через `mirror.gcr.io` (див. `./docker-mirror.mjs`).
6
6
  *
7
7
  * Також перевіряє, що Dockerfile/Containerfile має **multistage build** і що фінальний stage
8
8
  * використовує дозволений runtime-образ (див. docker.mdc):
@@ -30,8 +30,8 @@
30
30
  import { readFile } from 'node:fs/promises'
31
31
  import { basename } from 'node:path'
32
32
 
33
- import { getMirrorGcrHint, getFromImageToken } from '../../../../scripts/utils/docker-mirror.mjs'
34
- import { lintDockerfileWithHadolint, posixRel } from '../../../../scripts/utils/docker-hadolint.mjs'
33
+ import { getMirrorGcrHint, getFromImageToken } from './docker-mirror.mjs'
34
+ import { lintDockerfileWithHadolint, posixRel } from './docker-hadolint.mjs'
35
35
  import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
36
36
  import { loadCursorIgnorePaths } from '../../../../scripts/utils/load-cursor-config.mjs'
37
37
  import { walkDir } from '../../../../scripts/utils/walkDir.mjs'
@@ -2,13 +2,13 @@
2
2
  * Спільна логіка виклику hadolint для шляхів до Dockerfile (див. docker.mdc).
3
3
  *
4
4
  * Відносні шляхи з прямими слешами для контейнера; спочатку hadolint з PATH,
5
- * інакше docker run з образом HADOLINT_IMAGE. Використовується check-docker.mjs та
6
- * run-docker.mjs.
5
+ * інакше docker run з образом HADOLINT_IMAGE. Використовується `./check.mjs`
6
+ * (check-docker) та `../../lint/lint.mjs` (run-docker).
7
7
  */
8
8
  import { spawnSync } from 'node:child_process'
9
9
  import { relative, sep } from 'node:path'
10
10
 
11
- import { resolveCmd } from './resolve-cmd.mjs'
11
+ import { resolveCmd } from '../../../../scripts/utils/resolve-cmd.mjs'
12
12
 
13
13
  /** Тег образу для резервного запуску (узгоджуй з docker.mdc). */
14
14
  export const HADOLINT_IMAGE = 'hadolint/hadolint:v2.12.0'
@@ -5,12 +5,12 @@
5
5
  * check docker, не обробляються Dockerfile.*, Containerfile тощо — лише канонічне ім’я
6
6
  * Dockerfile та варіанти виду app.Dockerfile (регістр суфікса не важливий).
7
7
  *
8
- * Виклик hadolint — через utils/docker-hadolint.mjs (PATH або docker run).
8
+ * Виклик hadolint — через ../fix/lint/docker-hadolint.mjs (PATH або docker run).
9
9
  */
10
10
  import { basename } from 'node:path'
11
11
 
12
12
  import { isRunAsCli } from '../../../scripts/cli-entry.mjs'
13
- import { lintDockerfileWithHadolint, posixRel } from '../../../scripts/utils/docker-hadolint.mjs'
13
+ import { lintDockerfileWithHadolint, posixRel } from '../fix/lint/docker-hadolint.mjs'
14
14
  import { createCheckReporter } from '../../../scripts/utils/check-reporter.mjs'
15
15
  import { loadCursorIgnorePaths } from '../../../scripts/utils/load-cursor-config.mjs'
16
16
  import { walkDir } from '../../../scripts/utils/walkDir.mjs'
@@ -29,6 +29,7 @@ import { platform } from 'node:process'
29
29
  import { check as checkGa } from '../fix/workflows/check.mjs'
30
30
  import { resolveCmd } from '../../../scripts/utils/resolve-cmd.mjs'
31
31
  import { runLintStep } from '../../../scripts/utils/run-lint-step.mjs'
32
+ import { withLock } from '../../../scripts/utils/with-lock.mjs'
32
33
 
33
34
  /**
34
35
  * Опис залежності preflight-ом: бінарник, для чого потрібен, і команди встановлення.
@@ -147,7 +148,7 @@ function preflight(dep) {
147
148
  * Першу помилку від actionlint/zizmor/check повертаємо як код виходу; наступні кроки не запускаються.
148
149
  * @returns {Promise<number>} 0 — все OK, інакше — код першого кроку, що впав
149
150
  */
150
- export async function runLintGaCli() {
151
+ async function runLintGaSteps() {
151
152
  let preflightOk = true
152
153
  for (const dep of [SHELLCHECK_PREFLIGHT, UV_PREFLIGHT, CONFTEST_PREFLIGHT]) {
153
154
  if (!preflight(dep)) preflightOk = false
@@ -163,3 +164,5 @@ export async function runLintGaCli() {
163
164
  console.log('\n▶ check-ga (rego-полісі npm/policy/ga/ + JS cross-file перевірки)')
164
165
  return await checkGa()
165
166
  }
167
+
168
+ export const runLintGaCli = () => withLock('lint-ga', runLintGaSteps)
@@ -15,7 +15,7 @@ import {
15
15
  isGqlScanSourceFile,
16
16
  shouldSkipFileForGqlScan,
17
17
  sourceFileHasGqlTaggedTemplate
18
- } from '../../../../scripts/utils/graphql-gql-scan.mjs'
18
+ } from './graphql-gql-scan.mjs'
19
19
  import { loadCursorIgnorePaths } from '../../../../scripts/utils/load-cursor-config.mjs'
20
20
  import { runConftestBatch } from '../../../../scripts/utils/run-conftest-batch.mjs'
21
21
  import { walkDir } from '../../../../scripts/utils/walkDir.mjs'
@@ -1,18 +1,45 @@
1
1
  /**
2
2
  * Пошук tagged template **`gql\`…\``** у джерелах для правила graphql.mdc.
3
3
  *
4
- * Для **`.vue`** береться лише вміст `<script>` / `<script setup>` (спільна логіка з **vue-forbidden-imports**).
4
+ * Для **`.vue`** береться лише вміст `<script>` / `<script setup>` (паралельна реалізація екстрактора;
5
+ * аналог у `rules/vue/fix/packages/vue-forbidden-imports.mjs` — модулі не діляться кодом, щоб уникати
6
+ * cross-rule імпортів і тримати кожне правило самодостатнім).
5
7
  * Семантику визначає **oxc-parser** (`program`): рекурсивний обхід AST, збіг лише для **Identifier** з іменем **`gql`** як тега шаблону.
6
8
  */
7
9
  import { parseSync } from 'oxc-parser'
8
10
 
9
- import {
10
- contentForVueImportScan,
11
- isVueImportScanSourceFile,
12
- shouldSkipFileForVueImportScan
13
- } from './vue-forbidden-imports.mjs'
14
-
15
11
  const VUE_EXTENSION_RE = /\.vue$/u
12
+ const SOURCE_FILE_RE = /\.(vue|[cm]?[jt]sx?)$/u
13
+ const VUE_SCRIPT_BLOCK_RE = /<script\b[^>]*>([\s\S]*?)<\/script>/gi
14
+
15
+ /**
16
+ * Витягує з SFC лише код усередині `<script>` блоків, ігноруючи `<template>` і `<style>`.
17
+ * @param {string} sfc вміст .vue файлу
18
+ * @returns {string} конкатенований текст усіх блоків `<script>` (через подвійний переклад рядка)
19
+ */
20
+ function extractVueScriptBlocks(sfc) {
21
+ const chunks = []
22
+ VUE_SCRIPT_BLOCK_RE.lastIndex = 0
23
+ let m = VUE_SCRIPT_BLOCK_RE.exec(sfc)
24
+ while (m) {
25
+ chunks.push(m[1])
26
+ m = VUE_SCRIPT_BLOCK_RE.exec(sfc)
27
+ }
28
+ return chunks.join('\n\n')
29
+ }
30
+
31
+ /**
32
+ * Підбирає текст для сканування: для .vue — лише script-блоки, інакше — увесь вміст.
33
+ * @param {string} content сирий вміст файлу
34
+ * @param {string} filePath відносний шлях (для вибору режиму)
35
+ * @returns {string} текст для `parseSync`
36
+ */
37
+ function contentForGqlScan(content, filePath) {
38
+ if (filePath.endsWith('.vue')) {
39
+ return extractVueScriptBlocks(content)
40
+ }
41
+ return content
42
+ }
16
43
 
17
44
  /**
18
45
  * Мова для Oxc за шляхом файлу (розширення).
@@ -81,7 +108,7 @@ function astContainsGqlTag(node) {
81
108
  * @returns {boolean} true, якщо знайдено `gql`…``
82
109
  */
83
110
  export function sourceFileHasGqlTaggedTemplate(content, relativePath) {
84
- const scan = contentForVueImportScan(content, relativePath)
111
+ const scan = contentForGqlScan(content, relativePath)
85
112
  const pathForLang = virtualPathForParse(relativePath)
86
113
  const lang = langFromPath(pathForLang)
87
114
  try {
@@ -96,19 +123,27 @@ export function sourceFileHasGqlTaggedTemplate(content, relativePath) {
96
123
  }
97
124
 
98
125
  /**
99
- * Чи підлягає файл скануванню за розширенням (узгоджено з vue-import scan).
126
+ * Чи підлягає файл скануванню за розширенням (`.vue`, `.[cm]?[jt]sx?`).
100
127
  * @param {string} relativePath відносний шлях
101
128
  * @returns {boolean} true якщо файл підлягає скануванню
102
129
  */
103
130
  export function isGqlScanSourceFile(relativePath) {
104
- return isVueImportScanSourceFile(relativePath)
131
+ return SOURCE_FILE_RE.test(relativePath)
105
132
  }
106
133
 
107
134
  /**
108
- * Чи пропустити файл (декларації, auto-imports) — ті самі критерії, що для vue-import scan.
135
+ * Чи пропустити файл (декларації, auto-imports/components.d.ts) — типові generated-файли,
136
+ * у яких немає авторського коду з gql-тегами.
109
137
  * @param {string} relativePosix шлях з posix-слешами
110
138
  * @returns {boolean} true якщо файл потрібно пропустити
111
139
  */
112
140
  export function shouldSkipFileForGqlScan(relativePosix) {
113
- return shouldSkipFileForVueImportScan(relativePosix)
141
+ const base = relativePosix.split('/').pop() || ''
142
+ if (base === 'auto-imports.d.ts' || base === 'components.d.ts') {
143
+ return true
144
+ }
145
+ if (relativePosix.endsWith('.d.ts')) {
146
+ return true
147
+ }
148
+ return false
114
149
  }
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Запускається лише якщо в кореневому `package.json` поле `repository`
7
7
  * вказує на `https://github.com/nitra/...` або `https://github.com/abinbevefes/...`
8
- * (інші репозиторії пропускаються без помилок — як у check-abie).
8
+ * (інші репозиторії пропускаються без помилок — як у abie-перевірках).
9
9
  *
10
10
  * Очікуваний формат URL — кластерний DNS-суфікс `<cluster>.internal`:
11
11
  * - GKE / GCP: `http://<service>.<namespace>.svc.<cluster>.internal:<port>`
@@ -27,7 +27,7 @@ import {
27
27
  parseProgramOrNull,
28
28
  templateQuasisText,
29
29
  walkAstWithAncestors
30
- } from './ast-scan-utils.mjs'
30
+ } from '../../../../scripts/utils/ast-scan-utils.mjs'
31
31
 
32
32
  const SOURCE_FILE_RE = /\.([cm]?[jt]sx?)$/u
33
33
  const BUN_SQL_IMPORT_RE = /\bimport\s*\{[\s\S]*?\b(sql|SQL)\b[\s\S]*?\}\s*from\s*["']bun["']/u
@@ -48,7 +48,7 @@ import {
48
48
  isBunSqlScanSourceFile,
49
49
  textHasBunSqlImport,
50
50
  textHasPgLibImport
51
- } from '../../../../scripts/utils/bun-sql-scan.mjs'
51
+ } from './bun-sql-scan.mjs'
52
52
  import { findAllPackageJsonPaths } from '../../../../scripts/utils/find-package-json-paths.mjs'
53
53
  import { loadCursorIgnorePaths } from '../../../../scripts/utils/load-cursor-config.mjs'
54
54
  import { walkDir } from '../../../../scripts/utils/walkDir.mjs'