@nitra/cursor 12.8.6 → 12.8.7

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 (187) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/package.json +1 -1
  3. package/rules/adr/js/hooks.mdc +32 -0
  4. package/rules/adr/js/madr_format.mdc +96 -0
  5. package/rules/adr/js/settings_policy.mdc +34 -0
  6. package/rules/adr/main.mdc +13 -95
  7. package/rules/bun/js/bunfig.mdc +12 -0
  8. package/rules/bun/js/layout.mdc +60 -0
  9. package/rules/bun/js/lint.mdc +9 -0
  10. package/rules/bun/js/package_json.mdc +19 -0
  11. package/rules/bun/main.mdc +9 -61
  12. package/rules/capacitor/js/ios_spm.mdc +69 -0
  13. package/rules/capacitor/js/version.mdc +29 -0
  14. package/rules/capacitor/main.mdc +8 -22
  15. package/rules/changelog/js/agent-workflow.mdc +15 -0
  16. package/rules/changelog/js/changelog-format.mdc +33 -0
  17. package/rules/changelog/js/comparison-models.mdc +40 -0
  18. package/rules/changelog/main.mdc +4 -98
  19. package/rules/ci4/js/marksman_config.mdc +31 -0
  20. package/rules/ci4/js/vscode_extensions.mdc +33 -0
  21. package/rules/ci4/main.mdc +14 -14
  22. package/rules/docker/js/compile.mdc +44 -0
  23. package/rules/docker/js/hadolint.mdc +50 -0
  24. package/rules/docker/js/mirror.mdc +13 -0
  25. package/rules/docker/js/multistage.mdc +13 -0
  26. package/rules/docker/js/native-addon.mdc +43 -0
  27. package/rules/docker/js/nginx-tag.mdc +7 -0
  28. package/rules/docker/js/nginx-user.mdc +37 -0
  29. package/rules/docker/js/non-root.mdc +39 -0
  30. package/rules/docker/main.mdc +15 -196
  31. package/rules/ga/js/lint_toolchain.mdc +15 -0
  32. package/rules/ga/js/required_workflows.mdc +35 -0
  33. package/rules/ga/js/vscode.mdc +17 -0
  34. package/rules/ga/js/workflow_common.mdc +108 -0
  35. package/rules/ga/js/workflows.mdc +32 -0
  36. package/rules/ga/js/zizmor.mdc +7 -0
  37. package/rules/ga/main.mdc +17 -125
  38. package/rules/graphql/js/tooling.mdc +13 -0
  39. package/rules/graphql/js/vscode_extensions.mdc +13 -0
  40. package/rules/graphql/main.mdc +3 -22
  41. package/rules/hasura/js/internal_urls.mdc +27 -0
  42. package/rules/hasura/js/migrations.mdc +13 -0
  43. package/rules/hasura/js/svc_hl.mdc +17 -0
  44. package/rules/hasura/main.mdc +8 -30
  45. package/rules/image-avif/js/avif_generation.mdc +26 -0
  46. package/rules/image-avif/js/package_json_optout.mdc +21 -0
  47. package/rules/image-avif/main.mdc +7 -34
  48. package/rules/image-compress/js/package_json.mdc +7 -0
  49. package/rules/image-compress/js/package_setup.mdc +13 -0
  50. package/rules/image-compress/main.mdc +4 -12
  51. package/rules/js/docs/index.md +3 -3
  52. package/rules/js/js/dep-policy.mdc +17 -0
  53. package/rules/js/js/eslint-config.mdc +28 -0
  54. package/rules/js/js/extensions.mdc +8 -0
  55. package/rules/js/js/file-extensions.mdc +12 -0
  56. package/rules/js/js/for-in.mdc +26 -0
  57. package/rules/js/js/jscpd.mdc +42 -0
  58. package/rules/js/js/knip.mdc +15 -0
  59. package/rules/js/js/lint-js-workflow.mdc +58 -0
  60. package/rules/js/js/oxlintrc.mdc +20 -0
  61. package/rules/js/js/package-json.mdc +31 -0
  62. package/rules/js/js/tests.mdc +9 -0
  63. package/rules/js/js/utils-lib-structure.mdc +15 -0
  64. package/rules/js/main.mdc +21 -214
  65. package/rules/js-bun-db/js/bun-sql-migration.mdc +15 -0
  66. package/rules/js-bun-db/js/connection.mdc +42 -0
  67. package/rules/js-bun-db/js/pg-format-identifiers.mdc +102 -0
  68. package/rules/js-bun-db/js/pg-format-shim.mdc +99 -0
  69. package/rules/js-bun-db/js/pg-leftover.mdc +27 -0
  70. package/rules/js-bun-db/js/pg-listen-notify.mdc +51 -0
  71. package/rules/js-bun-db/js/query-safety.mdc +117 -0
  72. package/rules/js-bun-db/js/sql-array.mdc +88 -0
  73. package/rules/js-bun-db/js/unsafe.mdc +65 -0
  74. package/rules/js-bun-db/main.mdc +15 -605
  75. package/rules/js-bun-redis/js/imports.mdc +47 -0
  76. package/rules/js-bun-redis/js/package_json.mdc +44 -0
  77. package/rules/js-bun-redis/main.mdc +3 -11
  78. package/rules/js-mssql/js/mssql-in-list.mdc +38 -0
  79. package/rules/js-mssql/js/mssql-pool.mdc +56 -0
  80. package/rules/js-mssql/js/mssql-query-template.mdc +33 -0
  81. package/rules/js-mssql/js/mssql-tvp.mdc +75 -0
  82. package/rules/js-mssql/js/mssql-version.mdc +7 -0
  83. package/rules/js-mssql/main.mdc +10 -198
  84. package/rules/js-run/js/check-env.mdc +35 -0
  85. package/rules/js-run/js/conn-aliases.mdc +109 -0
  86. package/rules/js-run/js/jsconfig.mdc +20 -0
  87. package/rules/js-run/js/otel-configmap.mdc +6 -0
  88. package/rules/js-run/js/pino.mdc +6 -0
  89. package/rules/js-run/js/project-structure.mdc +11 -0
  90. package/rules/js-run/js/runtime.mdc +14 -0
  91. package/rules/js-run/js/scope.mdc +11 -0
  92. package/rules/js-run/js/settimeout.mdc +11 -0
  93. package/rules/js-run/js/temporal.mdc +5 -0
  94. package/rules/js-run/main.mdc +16 -218
  95. package/rules/k8s/js/configmap.mdc +41 -0
  96. package/rules/k8s/js/deployment_resources.mdc +49 -0
  97. package/rules/k8s/js/hasura_httproute.mdc +91 -0
  98. package/rules/k8s/js/hpa_apiversion.mdc +27 -0
  99. package/rules/k8s/js/ingress_gateway.mdc +16 -0
  100. package/rules/k8s/js/kustomize_structure.mdc +144 -0
  101. package/rules/k8s/js/lint_k8s.mdc +72 -0
  102. package/rules/k8s/js/multidoc_yaml.mdc +5 -0
  103. package/rules/k8s/js/network_policy.mdc +136 -0
  104. package/rules/k8s/js/schema_modeline.mdc +57 -0
  105. package/rules/k8s/js/service.mdc +44 -0
  106. package/rules/k8s/js/topology_hpa_pdb.mdc +181 -0
  107. package/rules/k8s/main.mdc +30 -843
  108. package/rules/nginx-default-tpl/js/dockerfile.mdc +36 -0
  109. package/rules/nginx-default-tpl/js/http-route.mdc +41 -0
  110. package/rules/nginx-default-tpl/js/ini-keys.mdc +21 -0
  111. package/rules/nginx-default-tpl/js/template-structure.mdc +86 -0
  112. package/rules/nginx-default-tpl/js/vscode.mdc +37 -0
  113. package/rules/nginx-default-tpl/main.mdc +6 -112
  114. package/rules/npm-module/js/docs/index.md +5 -5
  115. package/rules/npm-module/js/docs/rule_meta.md +6 -6
  116. package/rules/npm-module/js/docs/skill_meta.md +8 -8
  117. package/rules/npm-module/js/header_doc_pointer.mdc +18 -0
  118. package/rules/npm-module/js/package_structure.mdc +62 -0
  119. package/rules/npm-module/js/rule_meta.mdc +11 -0
  120. package/rules/npm-module/js/skill_meta.mdc +11 -0
  121. package/rules/npm-module/main.mdc +10 -55
  122. package/rules/php/js/lint_php_yml.mdc +12 -0
  123. package/rules/php/js/tooling.mdc +66 -0
  124. package/rules/php/main.mdc +7 -66
  125. package/rules/python/js/lint_python_yml.mdc +23 -0
  126. package/rules/python/js/pyproject_toml.mdc +32 -0
  127. package/rules/python/js/tooling.mdc +23 -0
  128. package/rules/python/main.mdc +9 -33
  129. package/rules/rego/js/rego-lint.mdc +31 -0
  130. package/rules/rego/js/vscode_extensions.mdc +11 -0
  131. package/rules/rego/js/vscode_settings.mdc +13 -0
  132. package/rules/rego/main.mdc +8 -24
  133. package/rules/rust/js/coverage.mdc +28 -0
  134. package/rules/rust/js/lint.mdc +22 -0
  135. package/rules/rust/js/tauri_composition.mdc +8 -0
  136. package/rules/rust/js/vscode_extensions.mdc +12 -0
  137. package/rules/rust/main.mdc +8 -38
  138. package/rules/security/js/rego_policies.mdc +15 -0
  139. package/rules/security/js/sample_secret.mdc +19 -0
  140. package/rules/security/js/trufflehog.mdc +21 -0
  141. package/rules/security/main.mdc +7 -35
  142. package/rules/style/js/admin-table.mdc +88 -0
  143. package/rules/style/js/colors.mdc +21 -0
  144. package/rules/style/js/gap.mdc +22 -0
  145. package/rules/style/js/quasar-fixes.mdc +32 -0
  146. package/rules/style/js/quasar.mdc +7 -0
  147. package/rules/style/js/tooling.mdc +85 -0
  148. package/rules/style/main.mdc +13 -253
  149. package/rules/tauri/js/cargo_mutants_config.mdc +39 -0
  150. package/rules/tauri/js/tool_surface.mdc +21 -0
  151. package/rules/tauri/js/tooling.mdc +25 -0
  152. package/rules/tauri/main.mdc +8 -78
  153. package/rules/test/js/cargo_mutants_config.mdc +18 -0
  154. package/rules/test/js/docs/index.md +7 -7
  155. package/rules/test/js/location.mdc +52 -0
  156. package/rules/test/js/no-console-store-restore.mdc +11 -0
  157. package/rules/test/js/no-process-chdir.mdc +15 -0
  158. package/rules/test/js/no-relative-fs-path.mdc +22 -0
  159. package/rules/test/js/sandbox-aware-test.mdc +28 -0
  160. package/rules/test/js/stryker_config.mdc +26 -0
  161. package/rules/test/js/vitest-config-pool-forks.mdc +33 -0
  162. package/rules/test/main.mdc +18 -184
  163. package/rules/text/js/ci-lint-text.mdc +15 -0
  164. package/rules/text/js/cspell.mdc +81 -0
  165. package/rules/text/js/dotenv-linter.mdc +16 -0
  166. package/rules/text/js/forbidden-prettier.mdc +13 -0
  167. package/rules/text/js/markdownlint.mdc +25 -0
  168. package/rules/text/js/oxfmt.mdc +35 -0
  169. package/rules/text/js/package-json.mdc +26 -0
  170. package/rules/text/js/shellcheck.mdc +18 -0
  171. package/rules/text/js/v8r.mdc +23 -0
  172. package/rules/text/js/vscode.mdc +86 -0
  173. package/rules/text/main.mdc +20 -237
  174. package/rules/vue/js/composition-api.mdc +82 -0
  175. package/rules/vue/js/nheader-layout.mdc +171 -0
  176. package/rules/vue/js/node-imports.mdc +25 -0
  177. package/rules/vue/js/quasar-ui.mdc +32 -0
  178. package/rules/vue/js/structure.mdc +101 -0
  179. package/rules/vue/js/testing.mdc +32 -0
  180. package/rules/vue/js/tfm-translations.mdc +26 -0
  181. package/rules/vue/js/vite-config.mdc +126 -0
  182. package/rules/vue/js/vite-env.mdc +55 -0
  183. package/rules/vue/js/vue-imports.mdc +25 -0
  184. package/rules/vue/main.mdc +16 -640
  185. package/scripts/docs/index.md +16 -16
  186. package/scripts/lib/docs/index.md +36 -36
  187. package/scripts/utils/docs/index.md +14 -14
package/rules/ga/main.mdc CHANGED
@@ -5,137 +5,29 @@ globs: ".github/workflows/*.yml"
5
5
  alwaysApply: false
6
6
  ---
7
7
 
8
- У `.github/workflows/` лише **`.yml`**. Мають бути **`clean-ga-workflows.yml`**, **`clean-merged-branch.yml`**, **`lint-ga.yml`**, **`git-ai.yml`**. Якщо є **`apply-k8s.yml`** / **`apply-nats-consumer.yml`** paths у тригері як у фрагментах.
8
+ Правило **ga** перевіряє структуру `.github/workflows/`, наявність обов'язкових workflow-файлів і їх відповідність канонам, а також налаштування VS Code та zizmor для роботи з GitHub Actions.
9
9
 
10
- **Кожен** workflow у `.github/workflows/*.yml` **обов'язково** містить блок `concurrency` з фіксованим `group` та `cancel-in-progress: true`:
10
+ [ga-workflows](./js/workflows.mdc)
11
11
 
12
- ```yaml
13
- concurrency:
14
- group: ${{ github.ref }}-${{ github.workflow }}
15
- cancel-in-progress: true
16
- ```
12
+ [ga-workflow_common](./js/workflow_common.mdc)
17
13
 
18
- Без винятків — у scheduled cleanup-воркфлоу, у `pull_request: types: [closed]`, у publish-воркфлоу теж. Це уникає паралельних запусків того самого workflow на тій самій ref і скасовує попередні в чергу нових.
14
+ [ga-required_workflows](./js/required_workflows.mdc)
19
15
 
20
- Повинен бути файл `.github/workflows/clean-ga-workflows.yml`:
16
+ [ga-vscode](./js/vscode.mdc)
21
17
 
22
- - Канон: [clean-ga-workflows.yml.snippet.yml](./policy/clean_ga_workflows/template/clean-ga-workflows.yml.snippet.yml)
18
+ [ga-zizmor](./js/zizmor.mdc)
23
19
 
24
- Повинен бути файл `.github/workflows/clean-merged-branch.yml`:
20
+ [ga-lint_toolchain](./js/lint_toolchain.mdc)
25
21
 
26
- - Канон: [clean-merged-branch.yml.snippet.yml](./policy/clean_merged_branch/template/clean-merged-branch.yml.snippet.yml)
22
+ ## Швидкий gate через conftest
27
23
 
28
- Інші гілки в `ignore_branches` — допустимо.
24
+ Rego-пакети (namespace що перевіряє):
29
25
 
30
- Повинен бути файл `.github/workflows/lint-ga.yml`:
31
-
32
- - Канон: [lint-ga.yml.snippet.yml](./policy/lint_ga/template/lint-ga.yml.snippet.yml)
33
-
34
- Повинен бути файл `.github/workflows/git-ai.yml`:
35
-
36
- - Канон: [git-ai.yml.snippet.yml](./policy/git_ai/template/git-ai.yml.snippet.yml)
37
-
38
- **Мінімальні версії marketplace actions** у `uses:` (перевіряє `ga.workflow_common`):
39
-
40
- - `actions/checkout` — **не нижче major `v6`** (`@v6`, `@v6.0.2` тощо дозволені; `@v5` — ні);
41
- - `Infisical/secrets-action` — **не нижче `v1.0.16`** (Node 24; нижчі теги лишаються на Node 20, deprecated з червня 2026).
42
-
43
- Канон: [uses-min-versions.snippet.json](./policy/workflow_common/template/uses-min-versions.snippet.json). SHA-pin (40 hex) semver-політику не застосовує. Для checkout рекомендується **`v6.0.2+`** (Node 24 у action), але мінімум політики — лише major **6**.
44
-
45
- **Локальний composite** (`uses: ./.github/actions/setup-bun-deps` або `./npm/github-actions/setup-bun-deps`): **спочатку** обов’язковий крок **`actions/checkout@v6`** (`persist-credentials: false`), інакше runner не знайде `action.yml`. Сам composite: **`actions/setup-node@v6`** (**Node 24**), **Bun**, **`actions/cache@v5`**, **`bun install --frozen-lockfile`**.
46
-
47
- **ЗАБОРОНЕНО** дублювати кроки встановлення Bun та кешування безпосередньо у workflow файлах. Завжди використовуй локальний composite action.
48
-
49
- **Кроки `run`:** не розбивай команду shell-продовженням через зворотний сліш у кінці рядка (`… \` у `run: |`). Замість багаторядкового буквального блока з `\\` оформ довгу одну shell-команду як **folded block** `>-` (рядки з’єднаються в один рядок із пробілами).
50
-
51
- **Читабельність `run: >-` з циклам/умовами:** оскільки `>-` **згортає рядки в один shell-рядок**, відступи потрібні **лише для читабельності** й не впливають на виконання. Але для конструкцій bash на кшталт `while …; do … done` та `if …; then … fi` **обовʼязково**:
52
-
53
- - став явні роздільники команд **`;`** або **`&&`** там, де при згортанні рядків інакше “злипнуться” токени (`do`/`then`/`fi`/`done` з наступною командою);
54
- - тримай `do` і `then` в одному логічному рядку як `…; do` / `…; then`, щоб після згортання це гарантовано лишалось валідним bash;
55
- - додавай відступи всередині `do/then/else` блоків, навіть якщо це один рядок після згортання — так workflow лишається читабельним у diff.
56
-
57
- ### Приклад (ПРАВИЛЬНО — читабельно, `run: >-`, з `while`/`if`)
58
-
59
- ```yaml
60
- - name: Apply changes
61
- shell: bash
62
- run: >-
63
- echo "$FILES" |
64
- while read -r FILE; do
65
- [ -z "$FILE" ] && continue;
66
- dirname "$FILE";
67
- done |
68
- sort -u |
69
- while read -r DIR; do
70
- (
71
- if [ -f "$DIR/kustomization.yaml" ]; then
72
- printf 'Applying %s\n' "$DIR" &&
73
- cd "$DIR" &&
74
- kubectl apply -k .;
75
- else
76
- echo "Skip $DIR - no kustomization.yaml";
77
- fi
78
- );
79
- done
80
- ```
81
-
82
- ### Приклад run (НЕПРАВИЛЬНО — `\\` на кінцях)
83
-
84
- ```yaml
85
- - run: |
86
- docker build \
87
- --push \
88
- --build-arg BRANCH=${{ github.ref_name }}
89
- ```
90
-
91
- ### Приклад run (ПРАВИЛЬНО — `>-`)
92
-
93
- ```yaml
94
- - run: >-
95
- docker build
96
- --push
97
- --build-arg BRANCH=${{ github.ref_name }}
98
- ```
99
-
100
- ### Приклад (НЕПРАВИЛЬНО)
101
-
102
- ```yaml
103
- steps:
104
- - uses: actions/checkout@v6
105
- - uses: oven-sh/setup-bun@v2
106
- - uses: actions/cache@v5
107
- # ... багато рядків кешування ...
108
- - run: bun install --frozen-lockfile
109
- ```
110
-
111
- ### Приклад (ПРАВИЛЬНО)
112
-
113
- ```yaml
114
- steps:
115
- - uses: actions/checkout@v6
116
- with:
117
- persist-credentials: false
118
- - uses: ./.github/actions/setup-bun-deps
119
- ```
120
-
121
- **Лінт:** [actionlint](https://github.com/rhysd/actionlint) через [github-actionlint](https://www.npmjs.com/package/github-actionlint); [zizmor](https://docs.zizmor.sh) — `uvx`, офлайн. Запуск — через **`n-cursor lint ga`** (CI — `--read-only`; бінарка з `node_modules/.bin/` пакету `@nitra/cursor`), який робить preflight на `shellcheck` і послідовно запускає `actionlint` та `zizmor`. Окремого `package.json`-скрипта немає.
122
-
123
- > Виклик через bin-ім’я `n-cursor` (а **не** `npx @nitra/cursor`): `bun x`/`npx` для скоупованого пакету з одним bin-ім’ям повертає 0 без виконання, тому в CI-кроці `run:` використовуй саме `n-cursor lint <rule>`.
124
-
125
- CLI робить preflight на `shellcheck` і `uv` (`uvx`) у `PATH`, потім запускає `bunx github-actionlint` і `uvx zizmor --offline --collect=workflows .`.
126
-
127
- **`.github/zizmor.yml`:** для [unpinned-uses](https://docs.zizmor.sh/audits/#unpinned-uses) — політика **`ref-pin`**, якщо в `uses:` семантичні теги. За потреби вимкни [template-injection](https://docs.zizmor.sh/audits/#template-injection):
128
-
129
- - Канон `.github/zizmor.yml`: [zizmor.yml.snippet.yml](./policy/zizmor_yml/template/zizmor.yml.snippet.yml)
130
-
131
- **`.vscode/extensions.json`** має рекомендувати `github.vscode-github-actions`:
132
-
133
- - Канон: [extensions.json.snippet.json](./policy/vscode_extensions/template/extensions.json.snippet.json)
134
-
135
- **`.vscode/settings.json`** для мови `github-actions-workflow` має `editor.defaultFormatter = "oxc.oxc-vscode"`:
136
-
137
- - Канон: [settings.json.snippet.json](./policy/vscode_settings/template/settings.json.snippet.json)
138
-
139
- **MegaLinter:** не використовувати; прибрати workflow, конфіги (`.mega-linter.yml`, `.megalinter.yaml`, `.mega-linter.yaml`), залежності та згадки в CI / pre-commit / документації.
140
-
141
- **`depcheck`:** не використовувати у `.github/workflows/*.yml` — мігровано на `knip` (див. `js.mdc`). Перевірка невикористаних залежностей виконується разом з рештою лінтерів у `lint-js`, окремий крок `npx depcheck` у workflow не потрібен і блокується полісі `ga.workflow_common`.
26
+ - `ga.workflow_common` усі `*.yml`: concurrency, composite setup-bun-deps, run:>-, мінімальні версії actions, заборона depcheck
27
+ - `ga.clean_ga_workflows` — структура `clean-ga-workflows.yml` (cron, permissions, cleanup step)
28
+ - `ga.clean_merged_branch` — структура `clean-merged-branch.yml` (cron, permissions, ignore_branches, dry_run)
29
+ - `ga.lint_ga` — структура `lint-ga.yml` (triggers, conftest install, n-cursor lint ga --read-only)
30
+ - `ga.git_ai` структура `git-ai.yml` (pr closed trigger, git-ai install + run)
31
+ - `ga.vscode_extensions` — `.vscode/extensions.json`: рекомендація `github.vscode-github-actions`
32
+ - `ga.vscode_settings` — `.vscode/settings.json`: `editor.defaultFormatter` для github-actions-workflow
33
+ - `ga.zizmor_yml` — `.github/zizmor.yml`: `rules.unpinned-uses.config.policies["*"]`
@@ -0,0 +1,13 @@
1
+ ## Умовна вимога `.graphqlrc.yml`
2
+
3
+ Якщо у `.vue` або в JavaScript / TypeScript джерелах (`.js`, `.mjs`, `.cjs`, `.ts`, `.tsx`, `.jsx` тощо) зустрічається **tagged template literal** з тегом **`gql`** (типово `gql\`query …\`` для GraphQL-запиту), у **корені репозиторію** має бути файл **`.graphqlrc.yml`** ([GraphQL Config](https://the-guild.dev/graphql/config/docs)).
4
+
5
+ Перевірка є **умовною**: якщо жодного `gql\`…\`` у дереві не знайдено — `.graphqlrc.yml` не вимагається.
6
+
7
+ Підстав свої шляхи до схеми та до файлів з операціями; приклад орієнтиру:
8
+
9
+ ```yaml title=".graphqlrc.yml"
10
+ schema: node_modules/@nitra/efes-shared/schema/maya.graphql
11
+ documents:
12
+ - '**/*.{vue,js,ts,tsx}'
13
+ ```
@@ -0,0 +1,13 @@
1
+ ## Розширення VS Code для GraphQL
2
+
3
+ Якщо у проєкті є `gql\`…\`` tagged template literals, у **`.vscode/extensions.json`** в масиві **`recommendations`** має бути запис **`graphql.vscode-graphql`**.
4
+
5
+ Канон задає мінімум — інші записи (від markdownlint, oxc тощо) дозволені. Перевірка виконується Rego-пакетом `graphql.vscode_extensions` через `conftest` і запускається лише після того, як JS-сканер виявив `gql` у дереві.
6
+
7
+ Додай `graphql.vscode-graphql` до наявного списку `recommendations` (не замінюй інші записи):
8
+
9
+ ```json title=".vscode/extensions.json"
10
+ {
11
+ "recommendations": ["graphql.vscode-graphql"]
12
+ }
13
+ ```
@@ -5,27 +5,8 @@ globs: "**/*.{vue,js,mjs,cjs,ts,tsx,jsx}"
5
5
  alwaysApply: false
6
6
  ---
7
7
 
8
- Якщо в **`.vue`** або в **JavaScript / TypeScript** джерелах (`.js`, `.mjs`, `.cjs`, `.ts`, `.tsx`, `.jsx` тощо) зустрічається **tagged template literal** з тегом **`gql`** (типово `gql\`query …\`` для GraphQL-запиту), у **корені репозиторію** мають бути:
8
+ Якщо у `.vue` або JavaScript / TypeScript джерелах зустрічається **tagged template literal** з тегом **`gql`**, у корені репозиторію мають бути `.graphqlrc.yml` та запис `graphql.vscode-graphql` у `.vscode/extensions.json`.
9
9
 
10
- - файл **`.graphqlrc.yml`** ([GraphQL Config](https://the-guild.dev/graphql/config/docs));
11
- - у **`.vscode/extensions.json`** в масиві **`recommendations`** — запис **`graphql.vscode-graphql`**.
10
+ [graphql-tooling](./js/tooling.mdc)
12
11
 
13
- ## `.graphqlrc.yml`
14
-
15
- Підстав свої шляхи до схеми та до файлів з операціями; приклад орієнтиру:
16
-
17
- ```yaml title=".graphqlrc.yml"
18
- schema: node_modules/@nitra/efes-shared/schema/maya.graphql
19
- documents:
20
- - '**/*.{vue,js,ts,tsx}'
21
- ```
22
-
23
- ## `.vscode/extensions.json`
24
-
25
- Додай **`graphql.vscode-graphql`** до наявного списку **`recommendations`** (не замінюй інші записи):
26
-
27
- ```json title=".vscode/extensions.json"
28
- {
29
- "recommendations": ["graphql.vscode-graphql"]
30
- }
31
- ```
12
+ [graphql-vscode-extensions](./js/vscode_extensions.mdc)
@@ -0,0 +1,27 @@
1
+ ## Внутрішній кластерний URL для HASURA_GRAPHQL_ENDPOINT
2
+
3
+ У `*.env` для атрибута `HASURA_GRAPHQL_ENDPOINT` підключення має бути **усередині кластера**, а не через публічний домен — інакше CI кладе метадані через зовнішній бекенд і ламає перевипуск/міграції.
4
+
5
+ Правило застосовується лише до проєктів **nitra** (у кореневому `package.json` `"repository": "https://github.com/nitra/*"`) і **abie** (`"repository": "https://github.com/abinbevefes/*"`); для інших репозиторіїв перевірка пропускається.
6
+
7
+ Файл `.env` (без імені) — виключення з цього правила, його змінювати не потрібно.
8
+
9
+ Приклад **неправильного** значення:
10
+
11
+ ```env
12
+ HASURA_GRAPHQL_ENDPOINT=https://vybeerai.com.ua/contract/ql
13
+ ```
14
+
15
+ Правильне значення:
16
+
17
+ ```env
18
+ HASURA_GRAPHQL_ENDPOINT=http://contract-h-hl.ua-contract.svc.abie-ua.internal:8080
19
+ ```
20
+
21
+ де `contract-h-hl` — це `metadata.name` headless Service з `hasura/k8s/base/svc-hl.yaml` (пара з clusterIP `contract-h` у `svc.yaml`, якщо є; узгоджено з k8s: суфікс `-hl` на базі `-h`), а `ua-contract` — `metadata.name` namespace з `hasura/k8s/base/namespace.yaml`.
22
+
23
+ Формат URL: `http://<service>.<namespace>.svc.<cluster>.internal:<port>`
24
+
25
+ - `http://` обов'язковий (TLS усередині кластера зайвий)
26
+ - DNS-суфікс — `<cluster>.internal` (GKE/GCP)
27
+ - Сервіс і namespace звіряються з `hasura/k8s/base/svc-hl.yaml` та `hasura/k8s/base/namespace.yaml`
@@ -0,0 +1,13 @@
1
+ ## Міграції: лише `up.sql`, без `down.sql`
2
+
3
+ При створенні будь-якої нової директорії міграції у `hasura/migrations/` **не створювати файл `down.sql`**.
4
+ У директорії міграції має бути **тільки `up.sql`**.
5
+
6
+ Приклад коректної структури:
7
+
8
+ ```
9
+ hasura/migrations/default/1781247030100_add_foo/
10
+ └── up.sql ✓
11
+ ```
12
+
13
+ Файл `down.sql` у цьому проєкті **не використовується** і не повинен з'являтися.
@@ -0,0 +1,17 @@
1
+ ## Іменування headless та clusterIP Service у hasura/k8s/base
2
+
3
+ Hasura-конвенція іменування Service у `hasura/k8s/base/svc.yaml` та `svc-hl.yaml`:
4
+
5
+ - **clusterIP Service** (`svc.yaml`) — базовий сегмент закінчується на **`-h`** (наприклад, `contract-h`)
6
+ - **Headless Service** (`svc-hl.yaml`) — закінчується на **`-h-hl`** (наприклад, `contract-h-hl`)
7
+
8
+ Пара: `contract-h` (clusterIP) + `contract-h-hl` (headless) — обидва імені мають бути узгоджені між собою та з `HASURA_GRAPHQL_ENDPOINT` у `*.env`.
9
+
10
+ Перевірка виконується Rego-полісі `hasura.svc_hl` (package `hasura.svc_hl`):
11
+
12
+ ```
13
+ conftest test hasura/k8s/base/svc-hl.yaml -p npm/rules/hasura/policy/svc_hl \
14
+ --namespace hasura.svc_hl
15
+ ```
16
+
17
+ Cross-file перевірку (узгодженість `HASURA_GRAPHQL_ENDPOINT` ↔ YAML) виконує `js/internal_urls.mjs`.
@@ -5,38 +5,16 @@ globs: "**/hasura/**,**/*.env"
5
5
  alwaysApply: false
6
6
  ---
7
7
 
8
- ## Підключення для оновлення метаданих у CI (Nitra та Abinbevefes)
8
+ Правило охоплює: коректність підключення Hasura у CI-середовищі (внутрішній URL кластера), конвенцію іменування k8s-сервісів та структуру директорій міграцій.
9
9
 
10
- У `*.env` для атрибута `HASURA_GRAPHQL_ENDPOINT` підключення має бути **усередині кластера**, а не через публічний домен — інакше CI кладе метадані через зовнішній бекенд і ламає перевипуск/міграції.
10
+ [hasura-internal_urls](./js/internal_urls.mdc)
11
11
 
12
- Приклад **неправильного** значення:
12
+ [hasura-svc_hl](./js/svc_hl.mdc)
13
13
 
14
- ```env
15
- HASURA_GRAPHQL_ENDPOINT=https://vybeerai.com.ua/contract/ql
16
- ```
14
+ [hasura-migrations](./js/migrations.mdc)
17
15
 
18
- Правильне значення:
16
+ ## Швидкий gate через conftest
19
17
 
20
- ```env
21
- HASURA_GRAPHQL_ENDPOINT=http://contract-h-hl.ua-contract.svc.abie-ua.internal:8080
22
- ```
23
-
24
- де `contract-h-hl` — це `metadata.name` headless Service з `hasura/k8s/base/svc-hl.yaml` (пара з clusterIP `contract-h` у `svc.yaml`, якщо є; узгоджено з k8s: суфікс `-hl` на базі `-h`), а `ua-contract` — `metadata.name` namespace з `hasura/k8s/base/namespace.yaml`.
25
-
26
- Правило застосовується для проєктів **nitra** (у кореневому `package.json` `"repository": "https://github.com/nitra/*"`) і **abie** (`"repository": "https://github.com/abinbevefes/*"`); для інших репозиторіїв перевірка пропускається.
27
-
28
- Файл .env це (без імені) це виключення з цього правила, його змінювати не потрібно
29
-
30
- ## Міграції: лише `up.sql`, без `down.sql`
31
-
32
- При створенні будь-якої нової директорії міграції у `hasura/migrations/` **не створювати файл `down.sql`**.
33
- У директорії міграції має бути **тільки `up.sql`**.
34
-
35
- Приклад коректної структури:
36
-
37
- ```
38
- hasura/migrations/default/1781247030100_add_foo/
39
- └── up.sql ✓
40
- ```
41
-
42
- Файл `down.sql` у цьому проєкті **не використовується** і не повинен з'являтися.
18
+ | Пакет | Файл | Що перевіряє |
19
+ |---|---|---|
20
+ | `hasura.svc_hl` | `policy/svc_hl/svc_hl.rego` | Іменування headless/clusterIP Service: суфікси `-h-hl` та `-h` |
@@ -0,0 +1,26 @@
1
+ ## Чотирикроковий пайплайн генерації AVIF-двійників
2
+
3
+ AVIF-двійники (`<name>.<ext>.avif`) генерує **виключно** `npx @nitra/cursor fix image-avif`. Правило `image-compress` відповідає лише за стиснення початкових raster/SVG-файлів через `@nitra/minify-image`, а AVIF лишається окремим правилом. Перевірка виконує чотири кроки в порядку:
4
+
5
+ 1. **Pre-scan**: шукає у `.vue`/`.html` хоча б одне raster-посилання, яке потенційно треба переписати на AVIF-двійник (`import x from '...png'` або `<img src="...png" />`). Пакети з opt-out `disable-avif: true` пропускаються. **Якщо жодного raster-посилання не знайдено — `check image-avif` завершується успіхом одразу: ні `npx --avif`, ні rewrite, ні cleanup-сиріт не виконуються** (нічого було б змінювати). Так уникаємо дорогого `npx @nitra/minify-image` у проєктах, де AVIF не вживається.
6
+ 2. Запускає `npx @nitra/minify-image --src=. --write --avif` (≥ **3.3.1**) — генерує `<name>.<ext>.avif` поряд з кожним PNG/JPEG/GIF. CLI порівнює sha1 кожного raster-сорсу зі збереженим у `.n-minify-image.tsv` і перезаписує `<source>.avif` при зміні оригіналу.
7
+ 3. Сканує `.vue` (а також `.html`) файли в кожному workspace-пакеті (root + workspaces) і автоматично переписує raster-посилання на AVIF-двійник у двох формах:
8
+ - **Імпорт-пов'язані** — `import x from '...png|jpg|jpeg|gif'` (далі `:src="x"` у шаблоні);
9
+ - **Прямі статичні** — `<img src="...png" />` у `<template>` (Vite перетворює такий шлях на asset-імпорт на етапі збірки, тож вимога та сама).
10
+ 4. Видаляє AVIF-сироти: ходить по всіх `<...>.avif` у репозиторії; якщо на двійник не лишилось жодного посилання у `.vue`/`.html` — файл видаляється. **AVIF на диску лишається лише там, де заміна реально відбулась** — тому невикористані оригінали не накопичують `.avif`-«хвости». **Зауваж**: cleanup виконується ЛИШЕ якщо pre-scan на кроці 1 знайшов хоча б одне raster-посилання — інакше осиротілі `.avif` залишаються недоторканими (видаляться вже наступним прогоном, коли в `.vue`/`.html` зʼявиться raster для конвертації).
11
+
12
+ ```vue title="App.vue (після check image-avif)"
13
+ <script setup>
14
+ import welcomeImage from './assets/welcome.png.avif'
15
+ </script>
16
+
17
+ <template>
18
+ <img :src="welcomeImage" alt="Welcome" />
19
+ </template>
20
+ ```
21
+
22
+ Реактивне `:src="..."` (з JS-виразом — змінною, тернарником, викликом тощо) **не сканується** — значення обчислюється у рантаймі й шлях туди потрапляє через імпорт або інший резолвинг, який ловить імпорт-перевірка вище. SVG не торкаємо (vector → AVIF безглуздо). Атрибути `data-src=`, `obj.src=` у `<script>` тощо також пропускаються.
23
+
24
+ Якщо raster-посилання у `.vue`/`.html` не вдалось переписати (наприклад, оригіналу немає на диску, тож і `.avif` не згенерувався) — `check image-avif` падає з помилкою на конкретний файл.
25
+
26
+ AVIF-двійники **зберігаємо в git** — це готові артефакти для віддачі браузеру (без них ефект від AVIF втрачається на чистому checkout-і).
@@ -0,0 +1,21 @@
1
+ ## Опт-аут для конкретного пакета
2
+
3
+ У workspace-пакеті, де AVIF-імпорти небажані (наприклад, мобільний бандл або публічний сайт без гарантованої AVIF-підтримки), додай у `package.json` цього пакета. Заборонений typo `disabled-avif` (канон — `disable-avif`): [package.json.deny.json](./policy/package_json/template/package.json.deny.json).
4
+
5
+ ```json title="apps/site/package.json"
6
+ {
7
+ "@nitra/minify-image": {
8
+ "disable-avif": true
9
+ }
10
+ }
11
+ ```
12
+
13
+ Тоді перевірка пропускає `.vue` файли цього пакета і не видаляє наявні `.avif` всередині як «сироти». У root-`package.json` опт-аут діє лише для файлів кореня (вкладені workspaces перевіряються незалежно — вмикай прапор у кожному пакеті, де треба).
14
+
15
+ ### Валідація package.json через conftest (Rego)
16
+
17
+ Rego-пакет `image_avif.package_json` перевіряє коректність конфігурації опт-ауту у `package.json`:
18
+
19
+ - Поле `"@nitra/minify-image"` має бути обʼєктом (якщо задано).
20
+ - Ключ `"disable-avif"` має бути `boolean`.
21
+ - Забороняє typo-ключі (наприклад, `"disabled-avif"`) через deny-template із [`package.json.deny.json`](./policy/package_json/template/package.json.deny.json).
@@ -5,41 +5,14 @@ globs: "**/*.{png,jpg,jpeg,gif,avif,vue,html}"
5
5
  alwaysApply: false
6
6
  ---
7
7
 
8
- AVIF-двійники (`<name>.<ext>.avif`) генерує **виключно** `npx @nitra/cursor fix image-avif`. Правило `image-compress` відповідає лише за стиснення початкових raster/SVG-файлів через `@nitra/minify-image`, а AVIF лишається окремим правилом. Перевірка робить чотири кроки в порядку:
8
+ Правило забезпечує, що кожне raster-зображення (`png`, `jpg`, `jpeg`, `gif`), на яке посилаються `.vue`/`.html` файли, має AVIF-двійник і що посилання переписані на нього. Генерація, rewrite і cleanup AVIF-сиріт виконуються автоматично через `npx @nitra/cursor fix image-avif`.
9
9
 
10
- 1. **Pre-scan**: шукає у `.vue`/`.html` хоча б одне raster-посилання, яке потенційно треба переписати на AVIF-двійник (`import x from '...png'` або `<img src="...png" />`). Пакети з opt-out `disable-avif: true` пропускаються. **Якщо жодного raster-посилання не знайдено — `check image-avif` завершується успіхом одразу: ні `npx --avif`, ні rewrite, ні cleanup-сиріт не виконуються** (нічого було б змінювати). Так уникаємо дорогого `npx @nitra/minify-image` у проєктах, де AVIF не вживається.
11
- 2. Запускає `npx @nitra/minify-image --src=. --write --avif` (≥ **3.3.1**) — генерує `<name>.<ext>.avif` поряд з кожним PNG/JPEG/GIF. CLI порівнює sha1 кожного raster-сорсу зі збереженим у `.n-minify-image.tsv` і перезаписує `<source>.avif` при зміні оригіналу.
12
- 3. Сканує `.vue` (а також `.html`) файли в кожному workspace-пакеті (root + workspaces) і автоматично переписує raster-посилання на AVIF-двійник у двох формах:
13
- - **Імпорт-пов'язані** — `import x from '...png|jpg|jpeg|gif'` (далі `:src="x"` у шаблоні);
14
- - **Прямі статичні** — `<img src="...png" />` у `<template>` (Vite перетворює такий шлях на asset-імпорт на етапі збірки, тож вимога та сама).
15
- 4. Видаляє AVIF-сироти: ходить по всіх `<...>.avif` у репозиторії; якщо на двійник не лишилось жодного посилання у `.vue`/`.html` — файл видаляється. **AVIF на диску лишається лише там, де заміна реально відбулась** — тому невикористані оригінали не накопичують `.avif`-«хвости». **Зауваж**: cleanup виконується ЛИШЕ якщо pre-scan на кроці 1 знайшов хоча б одне raster-посилання — інакше осиротілі `.avif` залишаються недоторканими (видаляться вже наступним прогоном, коли в `.vue`/`.html` зʼявиться raster для конвертації).
10
+ [image-avif-avif_generation](./js/avif_generation.mdc)
16
11
 
17
- ```vue title="App.vue (після check image-avif)"
18
- <script setup>
19
- import welcomeImage from './assets/welcome.png.avif'
20
- </script>
12
+ [image-avif-package_json_optout](./js/package_json_optout.mdc)
21
13
 
22
- <template>
23
- <img :src="welcomeImage" alt="Welcome" />
24
- </template>
25
- ```
14
+ ## Швидкий gate через conftest
26
15
 
27
- Реактивне `:src="..."` JS-виразом змінною, тернарником, викликом тощо) **не сканується** — значення обчислюється у рантаймі й шлях туди потрапляє через імпорт або інший резолвинг, який ловить імпорт-перевірка вище. SVG не торкаємо (vector → AVIF безглуздо). Атрибути `data-src=`, `obj.src=` у `<script>` тощо також пропускаються.
28
-
29
- Якщо raster-посилання у `.vue`/`.html` не вдалось переписати (наприклад, оригіналу немає на диску, тож і `.avif` не згенерувався) `check image-avif` падає з помилкою на конкретний файл.
30
-
31
- AVIF-двійники **зберігаємо в git** — це готові артефакти для віддачі браузеру (без них ефект від AVIF втрачається на чистому checkout-і).
32
-
33
- ## Опт-аут для конкретного пакета
34
-
35
- У workspace-пакеті, де AVIF-імпорти небажані (наприклад, мобільний бандл або публічний сайт без гарантованої AVIF-підтримки), додай у `package.json` цього пакета. Заборонений typo `disabled-avif` (канон — `disable-avif`): [package.json.deny.json](./policy/package_json/template/package.json.deny.json).
36
-
37
- ```json title="apps/site/package.json"
38
- {
39
- "@nitra/minify-image": {
40
- "disable-avif": true
41
- }
42
- }
43
- ```
44
-
45
- Тоді перевірка пропускає `.vue` файли цього пакета і не видаляє наявні `.avif` всередині як «сироти». У root-`package.json` опт-аут діє лише для файлів кореня (вкладені workspaces перевіряються незалежно — вмикай прапор у кожному пакеті, де треба).
16
+ | Rego-пакет | Що перевіряє |
17
+ |---|---|
18
+ | `image_avif.package_json` | typo-ключі, тип поля та тип `disable-avif` в опт-аут конфігу `package.json` |
@@ -0,0 +1,7 @@
1
+ ## Заборонені залежності у `package.json`
2
+
3
+ `@nitra/minify-image` не повинен зʼявлятися ні в `dependencies`, ні в `devDependencies` — CLI запускається лише через `npx`. Якщо потрібен явний пін — у самому виклику (`npx @nitra/minify-image@^3 --src=. --write`).
4
+
5
+ Перевіряється через Rego-пакет `image_compress.package_json`: [package.json.deny.json](./policy/package_json/template/package.json.deny.json)
6
+
7
+ Окремий `package.json`-скрипт `lint-image` не потрібен і не перевіряється — єдина точка входу `n-cursor lint image-compress`.
@@ -0,0 +1,13 @@
1
+ ## Кеш-файли: що зберігати в git
2
+
3
+ **`.n-minify-image.tsv`** у корені сканованого каталогу — **має бути в git** (source of truth для sha1-перевірок і lifetime savings). У `.gitignore` його не додавай.
4
+
5
+ **`node_modules/.cache/@nitra/minify-image/mtime.tsv`** — локальний fast-path; авто-gitignored через `node_modules/`.
6
+
7
+ Застарілий **`.minify-image-cache.tsv`** у корені (з версій `@nitra/minify-image` < 3.2) — видали після міграції на split-cache:
8
+
9
+ ```sh
10
+ git rm --cached .minify-image-cache.tsv 2>/dev/null || true && rm -f .minify-image-cache.tsv
11
+ ```
12
+
13
+ Також прибери відповідний рядок з `.gitignore`, якщо є.
@@ -9,18 +9,10 @@ CLI [`@nitra/minify-image`](https://www.npmjs.com/package/@nitra/minify-image) (
9
9
 
10
10
  Окремий workflow `lint-image.yml` створювати не треба; якщо потрібен CI-gate для зображень, використовуй `n-cursor lint image-compress --read-only`.
11
11
 
12
- ## `package.json`
12
+ [image-compress-package_setup](./js/package_setup.mdc)
13
13
 
14
- - Заборонені залежності `@nitra/minify-image` у deps/devDeps: [package.json.deny.json](./policy/package_json/template/package.json.deny.json)
14
+ [image-compress-package_json](./js/package_json.mdc)
15
15
 
16
- Окремий `package.json`-скрипт `lint-image` не потрібен і не перевіряється — єдина точка входу `n-cursor lint image-compress`.
16
+ ## Швидкий gate через conftest
17
17
 
18
- ## Кеш
19
-
20
- - **`.n-minify-image.tsv`** у корені сканованого каталогу — **має бути в git** (source of truth для sha1-перевірок і lifetime savings). У `.gitignore` його не додавай.
21
- - **`node_modules/.cache/@nitra/minify-image/mtime.tsv`** — локальний fast-path; авто-gitignored через `node_modules/`.
22
- - Застарілий `.minify-image-cache.tsv` у корені — видали (`git rm --cached`, прибери рядок з `.gitignore`).
23
-
24
- ## Заборонені залежності
25
-
26
- `@nitra/minify-image` не повинен зʼявлятися ні в `dependencies`, ні в `devDependencies` — CLI запускається лише через `npx`. Якщо потрібен явний пін — у самому виклику (`npx @nitra/minify-image@^3 --src=. --write`).
18
+ - `image_compress.package_json` — забороняє `@nitra/minify-image` у `dependencies`/`devDependencies` проєкту
@@ -6,7 +6,7 @@ resource: npm/rules/js/
6
6
 
7
7
  # npm/rules/js
8
8
 
9
- | Файл | Тип |
10
- |---|---|
11
- | [fix.mjs](fix.md) | JS Module |
9
+ | Файл | Тип |
10
+ | ------------------- | --------- |
11
+ | [fix.mjs](fix.md) | JS Module |
12
12
  | [main.mjs](main.md) | JS Module |
@@ -0,0 +1,17 @@
1
+ ## Залежнісна політика (що не додавати)
2
+
3
+ `@e18e/eslint-plugin` окремо не додавай — він уже в залежностях `@nitra/eslint-config` (з **3.8.0**), oxlint підвантажує його з `node_modules`. Пакети `oxlint`/`eslint`/`jscpd`/`knip` теж не додавай у `devDependencies` без потреби монорепо — `bunx` тягне їх ad-hoc.
4
+
5
+ ### Заборона `@nitra/as-integrations-fastify`
6
+
7
+ Пакет **`@nitra/as-integrations-fastify`** заборонений у **`dependencies`**, **`peerDependencies`** та в import-specifier-ах. Це чистий републіш upstream, застряглий на peer `@apollo/server: "^4.0.0"`, тож на Apollo 5 `bun install` дає `warn: incorrect peer dependency`. Заміна — upstream **`@as-integrations/fastify`** (**`^3.1.0`**): peer `@apollo/server: "^4.0.0 || ^5.0.0"` + `fastify: "^5.3.0"`.
8
+
9
+ Міграція — лише specifier у трьох місцях: залежність у `package.json`, `import`, та `vi.mock(...)` / `await import(...)` у тестах. **Код не міняється**, експорти ті самі: `default` → `fastifyApollo`, named → `fastifyApolloDrainPlugin`.
10
+
11
+ ```javascript title="❌ до"
12
+ import fastifyApollo, { fastifyApolloDrainPlugin } from '@nitra/as-integrations-fastify'
13
+ ```
14
+
15
+ ```javascript title="✅ після"
16
+ import fastifyApollo, { fastifyApolloDrainPlugin } from '@as-integrations/fastify'
17
+ ```
@@ -0,0 +1,28 @@
1
+ ## ESLint flat config — `eslint.config.js`
2
+
3
+ У корені проєкту має бути `eslint.config.js` (або `eslint.config.mjs`) з flat config на основі `getConfig` з `@nitra/eslint-config`.
4
+
5
+ ```javascript title="eslint.config.js"
6
+ import { getConfig } from '@nitra/eslint-config'
7
+
8
+ export default [
9
+ {
10
+ ignores: ['**/auto-imports.d.ts']
11
+ },
12
+ ...getConfig({
13
+ node: ['npm']
14
+ })
15
+ ]
16
+ ```
17
+
18
+ У монорепо пакети з Vite (frontend) вкажи в секції `vue`, решту — у секції `node` у виклику `getConfig`.
19
+
20
+ Обов'язкові умови:
21
+ - файл `eslint.config.js` або `eslint.config.mjs` існує;
22
+ - містить виклик `getConfig`;
23
+ - імпортує з `@nitra/eslint-config`;
24
+ - у `ignores` є запис `**/auto-imports.d.ts`.
25
+
26
+ Застарілі конфіги (`.eslintrc`, `.eslintrc.js`, `.eslintrc.json`, `.eslintrc.yml`) — **видали**, вони несумісні з flat config.
27
+
28
+ З версії `@nitra/eslint-config` **3.9.2** `getConfig` вбудовує ignore для `**/adr/**` — ADR-документи не валідуються ESLint, локально цей glob додавати не потрібно.
@@ -0,0 +1,8 @@
1
+ ## `.vscode/extensions.json` — рекомендовані розширення
2
+
3
+ У `.vscode/extensions.json` поле `recommendations` має містити:
4
+ - `dbaeumer.vscode-eslint`
5
+ - `github.vscode-github-actions`
6
+ - `oxc.oxc-vscode`
7
+
8
+ Канон: [extensions.json.snippet.json](./policy/vscode_extensions/template/extensions.json.snippet.json)
@@ -0,0 +1,12 @@
1
+ ## Розширення нових файлів — `.mjs` / `.cjs`, не `.js`
2
+
3
+ **Нові** JS-файли створюй з явним розширенням модуля:
4
+
5
+ - **`.mjs`** — для ESM (типовий випадок);
6
+ - **`.cjs`** — для CommonJS, де він справді потрібен.
7
+
8
+ Голий **`.js`** для нового файлу **заборонено**. Розширення `.js` інтерпретується як ESM чи CJS лише за полем `package.json#type`, тож той самий файл читається по-різному залежно від пакета. Явне `.mjs`/`.cjs` робить тип модуля однозначним **без читання `package.json`** — навіть якщо `type` зміниться або файл перемістять в інший пакет. Це доповнює вимогу `"type": "module"` у `package-json.mdc`: `type` лишається каноном для всього дерева, а розширення нового файлу прибирає залежність від нього.
9
+
10
+ Стосується **backend і frontend** — будь-який новий вихідний файл: `src/`, тести `*.test.*`, `scripts/`, `src/conn/` тощо.
11
+
12
+ **Існуючі `.js` лишаються як є** — масово перейменовувати не треба; це конвенція для нового коду. Автоматичної перевірки тут немає: stateless-скан не відрізнить новий файл від існуючого, тож `.js` нікого не фейлить.
@@ -0,0 +1,26 @@
1
+ ## `for...in` заборонено — рефакторити на `for...of`
2
+
3
+ Конструкція `for (const k in obj)` обходить успадковані ключі прототипу, тому майже завжди тягне `Object.hasOwn(obj, k)`-guard. Заборонена у `@nitra/eslint-config` через `no-restricted-syntax` для `ForInStatement` (з версії **3.8.0**). У каноні oxlint лишається `guard-for-in` як часткова страховка (oxlint не підтримує `no-restricted-syntax`).
4
+
5
+ Замість цього обходь масив напряму, а об'єкт — через `Object.entries` / `Object.keys` / `Object.values`. У такому коді guard за `Object.hasOwn` стає непотрібним і має зникнути разом із `for...in`.
6
+
7
+ ```javascript title="❌ до"
8
+ for (const k in obj) {
9
+ if (!Object.hasOwn(obj, k)) continue
10
+ use(k, obj[k])
11
+ }
12
+
13
+ for (const i in arr) {
14
+ use(arr[i])
15
+ }
16
+ ```
17
+
18
+ ```javascript title="✅ після"
19
+ for (const [k, v] of Object.entries(obj)) {
20
+ use(k, v)
21
+ }
22
+
23
+ for (const item of arr) {
24
+ use(item)
25
+ }
26
+ ```