@nitra/cursor 3.22.0 → 3.23.1
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.
- package/.pi-template/extensions/n-cursor-adr/docs/index.md +181 -0
- package/AGENTS.template.md +4 -0
- package/CHANGELOG.md +37 -3
- package/bin/docs/n-cursor.md +636 -0
- package/bin/docs/rename-yaml-extensions.md +207 -0
- package/bin/n-cursor.js +30 -3
- package/package.json +1 -1
- package/rules/abie/docs/fix.md +18 -0
- package/rules/abie/js/docs/applies.md +26 -0
- package/rules/abie/js/docs/env_dns.md +32 -0
- package/rules/abie/js/docs/firebase_hosting.md +23 -0
- package/rules/abie/js/docs/hc_pairing.md +35 -0
- package/rules/abie/js/docs/ua_http_route.md +28 -0
- package/rules/abie/js/docs/ua_node_selector.md +28 -0
- package/rules/abie/lib/docs/enabled.md +29 -0
- package/rules/abie/lib/docs/env-dns.md +35 -0
- package/rules/abie/lib/docs/hc-yaml.md +33 -0
- package/rules/abie/lib/docs/http-route.md +44 -0
- package/rules/abie/lib/docs/k8s-tree.md +40 -0
- package/rules/abie/lib/docs/kustomization-patches.md +47 -0
- package/rules/abie/lib/docs/overlay-paths.md +38 -0
- package/rules/abie/lib/docs/yaml.md +29 -0
- package/rules/adr/docs/fix.md +148 -0
- package/rules/adr/js/docs/hooks.md +259 -0
- package/rules/bun/docs/fix.md +156 -0
- package/rules/bun/js/docs/layout.md +393 -0
- package/rules/capacitor/docs/fix.md +121 -0
- package/rules/capacitor/js/docs/platforms.md +295 -0
- package/rules/changelog/changelog.mdc +4 -2
- package/rules/changelog/docs/fix.md +174 -0
- package/rules/changelog/js/consistency.mjs +114 -13
- package/rules/changelog/js/docs/consistency.md +387 -0
- package/rules/changelog/lib/docs/package-manifest.md +210 -0
- package/rules/ci4/docs/fix.md +179 -0
- package/rules/ci4/js/docs/marksman_config.md +128 -0
- package/rules/docker/docker.mdc +8 -3
- package/rules/docker/docs/fix.md +171 -0
- package/rules/docker/js/docs/lint.md +258 -0
- package/rules/docker/lib/docs/docker-hadolint.md +184 -0
- package/rules/docker/lib/docs/docker-mirror.md +247 -0
- package/rules/docker/lib/docs/docker-native-addon.md +170 -0
- package/rules/docker/lib/docs/docker-nginx-user.md +219 -0
- package/rules/docker/lint/docs/lint.md +193 -0
- package/rules/efes/docs/fix.md +203 -0
- package/rules/feedback/docs/fix.md +140 -0
- package/rules/flow/docs/fix.md +152 -0
- package/rules/ga/docs/fix.md +158 -0
- package/rules/ga/js/docs/lint.md +100 -0
- package/rules/ga/js/docs/workflows.md +217 -0
- package/rules/ga/lint/docs/lint.md +209 -0
- package/rules/ga/policy/clean_merged_branch/clean_merged_branch.rego +11 -2
- package/rules/ga/policy/clean_merged_branch/template/clean-merged-branch.yml.snippet.yml +3 -4
- package/rules/graphql/docs/fix.md +126 -0
- package/rules/graphql/js/docs/tooling.md +264 -0
- package/rules/graphql/lib/docs/graphql-gql-scan.md +219 -0
- package/rules/hasura/docs/fix.md +120 -0
- package/rules/hasura/hasura.mdc +14 -0
- package/rules/hasura/js/docs/internal_urls.md +326 -0
- package/rules/image-avif/docs/fix.md +132 -0
- package/rules/image-avif/js/docs/avif_generation.md +241 -0
- package/rules/image-compress/docs/fix.md +150 -0
- package/rules/image-compress/js/docs/package_setup.md +191 -0
- package/rules/js-bun-db/docs/fix.md +148 -0
- package/rules/js-bun-db/js/docs/safety.md +231 -0
- package/rules/js-bun-db/js-bun-db.mdc +42 -13
- package/rules/js-bun-db/lib/docs/bun-sql-scan.md +347 -0
- package/rules/js-bun-redis/docs/fix.md +123 -0
- package/rules/js-bun-redis/js/docs/imports.md +176 -0
- package/rules/js-bun-redis/lib/docs/redis-imports.md +223 -0
- package/rules/js-lint/docs/fix.md +117 -0
- package/rules/js-lint/js/docs/lint.md +250 -0
- package/rules/js-lint/js/docs/tooling.md +348 -0
- package/rules/js-lint/js/docs/utils_imports.md +207 -0
- package/rules/js-lint-ci/docs/fix.md +154 -0
- package/rules/js-lint-ci/js/docs/lint.md +144 -0
- package/rules/js-mssql/docs/fix.md +128 -0
- package/rules/js-mssql/js/docs/deps.md +263 -0
- package/rules/js-mssql/lib/docs/mssql-pool-scan.md +367 -0
- package/rules/js-run/docs/fix.md +144 -0
- package/rules/js-run/js/docs/runtime.md +388 -0
- package/rules/js-run/lib/docs/bunyan-imports.md +117 -0
- package/rules/js-run/lib/docs/check-env-scan.md +433 -0
- package/rules/js-run/lib/docs/conn-file-rules.md +300 -0
- package/rules/js-run/lib/docs/conn-imports-scan.md +204 -0
- package/rules/js-run/lib/docs/promise-settimeout-scan.md +326 -0
- package/rules/k8s/docs/fix.md +129 -0
- package/rules/k8s/js/docs/manifests.md +344 -0
- package/rules/k8s/js/manifests.mjs +6 -2
- package/rules/k8s/k8s.mdc +4 -2
- package/rules/k8s/lint/docs/lint.md +411 -0
- package/rules/k8s/policy/network_policy/template/deployment.snippet.yaml +2 -0
- package/rules/k8s/policy/network_policy/template/stateful-set.snippet.yaml +2 -0
- package/rules/nginx-default-tpl/docs/fix.md +124 -0
- package/rules/nginx-default-tpl/js/docs/template.md +378 -0
- package/rules/npm-module/docs/fix.md +98 -0
- package/rules/npm-module/js/docs/package_structure.md +274 -0
- package/rules/npm-module/js/docs/rule_meta.md +137 -0
- package/rules/npm-module/js/docs/skill_meta.md +190 -0
- package/rules/php/docs/fix.md +107 -0
- package/rules/php/js/docs/tooling.md +152 -0
- package/rules/php/lint/docs/lint.md +215 -0
- package/rules/python/docs/fix.md +163 -0
- package/rules/python/js/docs/applies.md +108 -0
- package/rules/python/js/docs/tooling.md +153 -0
- package/rules/python/lint/docs/lint.md +322 -0
- package/rules/rego/docs/fix.md +121 -0
- package/rules/rego/js/docs/applies.md +174 -0
- package/rules/rego/js/docs/lint.md +118 -0
- package/rules/rego/lint/docs/lint.md +204 -0
- package/rules/release/docs/change.md +185 -0
- package/rules/release/docs/fix.md +119 -0
- package/rules/release/docs/release.md +222 -0
- package/rules/release/lib/docs/aggregate.md +246 -0
- package/rules/release/lib/docs/change-file.md +200 -0
- package/rules/release/lib/docs/fallback.md +203 -0
- package/rules/rust/docs/fix.md +129 -0
- package/rules/rust/js/docs/applies.md +140 -0
- package/rules/rust/lib/docs/has-cargo-toml.md +130 -0
- package/rules/security/docs/fix.md +86 -0
- package/rules/security/js/docs/lint.md +171 -0
- package/rules/security/js/docs/sample_secret.md +190 -0
- package/rules/security/js/docs/trufflehog.md +137 -0
- package/rules/security/js/lint.mjs +9 -1
- package/rules/style-lint/docs/fix.md +155 -0
- package/rules/style-lint/js/docs/lint.md +184 -0
- package/rules/style-lint/js/docs/tooling.md +194 -0
- package/rules/tauri/docs/fix.md +158 -0
- package/rules/tauri/js/docs/cargo_mutants_config.md +168 -0
- package/rules/tauri/js/docs/tooling.md +228 -0
- package/rules/test/coverage/coverage.mjs +15 -3
- package/rules/test/docs/fix.md +132 -0
- package/rules/test/js/data/stryker_config/docs/stryker-vue-macros-ignorer.md +138 -0
- package/rules/test/js/data/stryker_config/docs/stryker.config.baseline.md +134 -0
- package/rules/test/js/data/stryker_config/docs/stryker.config.vue.baseline.md +160 -0
- package/rules/test/js/data/vitest_config/docs/vitest.config.baseline.md +195 -0
- package/rules/test/js/docs/cargo_mutants_config.md +173 -0
- package/rules/test/js/docs/location.md +136 -0
- package/rules/test/js/docs/no-process-chdir.md +160 -0
- package/rules/test/js/docs/no-relative-fs-path.md +271 -0
- package/rules/test/js/docs/stryker_config.md +152 -0
- package/rules/test/js/docs/vitest-config-pool-forks.md +174 -0
- package/rules/text/docs/fix.md +118 -0
- package/rules/text/js/docs/forbidden-prettier.md +143 -0
- package/rules/text/js/docs/formatting.md +256 -0
- package/rules/text/js/docs/lint.md +122 -0
- package/rules/text/lint/docs/lint.md +220 -0
- package/rules/text/lint/docs/run-dotenv-linter.md +157 -0
- package/rules/text/lint/docs/run-shellcheck.md +212 -0
- package/rules/text/lint/docs/run-v8r.md +197 -0
- package/rules/vue/docs/fix.md +127 -0
- package/rules/vue/js/docs/packages.md +335 -0
- package/rules/vue/lib/docs/vue-forbidden-imports.md +261 -0
- package/rules/worktree/docs/fix.md +161 -0
- package/schemas/rule-meta.json +5 -1
- package/scripts/auto-rules.mjs +7 -4
- package/scripts/coverage-classify/docs/apply.md +202 -0
- package/scripts/coverage-classify/docs/cache.md +203 -0
- package/scripts/coverage-classify/docs/index.md +218 -0
- package/scripts/coverage-classify/docs/prompt.md +132 -0
- package/scripts/coverage-classify/docs/verdict-schema.md +169 -0
- package/scripts/coverage-fix-extract.mjs +122 -0
- package/scripts/coverage-fix.mjs +1 -1
- package/scripts/dispatcher/docs/graph.md +346 -0
- package/scripts/dispatcher/docs/index.md +236 -0
- package/scripts/dispatcher/docs/trace.md +296 -0
- package/scripts/dispatcher/index.mjs +1 -1
- package/scripts/dispatcher/lib/active.mjs +4 -8
- package/scripts/dispatcher/lib/commands.mjs +7 -11
- package/scripts/dispatcher/lib/docs/active.md +348 -0
- package/scripts/dispatcher/lib/docs/artifact.md +232 -0
- package/scripts/dispatcher/lib/docs/budget.md +167 -0
- package/scripts/dispatcher/lib/docs/capability.md +196 -0
- package/scripts/dispatcher/lib/docs/commands.md +210 -0
- package/scripts/dispatcher/lib/docs/events.md +182 -0
- package/scripts/dispatcher/lib/docs/executor.md +190 -0
- package/scripts/dispatcher/lib/docs/flow-lock.md +161 -0
- package/scripts/dispatcher/lib/docs/flow-resolve.md +267 -0
- package/scripts/dispatcher/lib/docs/gate.md +231 -0
- package/scripts/dispatcher/lib/docs/level.md +335 -0
- package/scripts/dispatcher/lib/docs/plan-panel.md +181 -0
- package/scripts/dispatcher/lib/docs/plan.md +200 -0
- package/scripts/dispatcher/lib/docs/planner.md +269 -0
- package/scripts/dispatcher/lib/docs/review.md +255 -0
- package/scripts/dispatcher/lib/docs/reviewer.md +240 -0
- package/scripts/dispatcher/lib/docs/snapshot.md +247 -0
- package/scripts/dispatcher/lib/docs/spec.md +203 -0
- package/scripts/dispatcher/lib/docs/state-store.md +303 -0
- package/scripts/dispatcher/lib/docs/subagent-runner.md +173 -0
- package/scripts/dispatcher/lib/executor.mjs +6 -1
- package/scripts/dispatcher/lib/flow-resolve.mjs +3 -1
- package/scripts/dispatcher/lib/level.mjs +29 -3
- package/scripts/dispatcher/lib/review.mjs +1 -1
- package/scripts/dispatcher/lib/subagent-runner.mjs +5 -3
- package/scripts/docs/auto-rules.md +376 -0
- package/scripts/docs/auto-skills.md +173 -0
- package/scripts/docs/build-agents-commands.md +183 -0
- package/scripts/docs/cli-entry.md +153 -0
- package/scripts/docs/coverage-fix.md +177 -0
- package/scripts/docs/ensure-nitra-cursor-dev-dependencies.md +189 -0
- package/scripts/lib/changed-files.mjs +4 -1
- package/scripts/lib/docs/changed-files.md +149 -0
- package/scripts/lib/docs/check-mdc-template-refs.md +222 -0
- package/scripts/lib/docs/check-reporter.md +175 -0
- package/scripts/lib/docs/discover-check-rules-from-cursor.md +157 -0
- package/scripts/lib/docs/discover-checkable-rules.md +165 -0
- package/scripts/lib/docs/ensure-tool.md +254 -0
- package/scripts/lib/docs/generated-markdown.md +275 -0
- package/scripts/lib/docs/gha-workflow.md +326 -0
- package/scripts/lib/docs/inline-template-links.md +303 -0
- package/scripts/lib/docs/list-rule-ids.md +156 -0
- package/scripts/lib/docs/load-cursor-config.md +147 -0
- package/scripts/lib/docs/mirror-parity.md +167 -0
- package/scripts/lib/worktree.mjs +26 -0
- package/scripts/worktree-cli.mjs +12 -2
- package/skills/coverage-fix/SKILL.md +34 -45
- package/skills/docgen/SKILL.md +44 -23
- package/skills/docgen/bench/etalon/firebase_hosting.md +19 -0
- package/skills/docgen/bench/etalon/k8s-tree.md +24 -0
- package/skills/docgen/bench/etalon/overlay-paths.md +24 -0
- package/skills/docgen/js/docgen-ignore.mjs +54 -0
- package/skills/docgen/js/docgen-scan.mjs +37 -21
- package/skills/llm-patch/SKILL.md +23 -2
- package/skills/start-check/SKILL.md +26 -53
- package/skills/start-check/js/check.mjs +211 -0
- package/skills/taze/SKILL.md +9 -3
- package/skills/taze/js/diff.mjs +154 -0
- package/types/bin/n-cursor.d.ts +1 -1
- package/skills/fix-tests/SKILL.md +0 -119
- package/skills/fix-tests/meta.json +0 -1
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# sample_secret.mjs
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Модуль `sample_secret.mjs` — це FS-частина правила `security`, концерн `sample_secret`. Він реалізує перевірку, що фейкові credential-значення у **прикладних** файлах репозиторію записані як канонічний placeholder `sample-secret`, а не як bare `secret`.
|
|
6
|
+
|
|
7
|
+
Мотивація: рядок `sample-secret` містить підрядок `sample`, який є у вшитому списку `DefaultFalsePositives` сканера TruffleHog. Завдяки цьому таке значення гарантовано відсіюється сканером незалежно від його версії. Bare `secret` наразі не фіксується TruffleHog лише через випадкову присутність у словнику `fp_words.txt`, що є крихкою поведінкою, залежною від версії інструмента — покладатися на неї не можна.
|
|
8
|
+
|
|
9
|
+
«Прикладними» вважаються файли, що відповідають хоча б одному з критеріїв:
|
|
10
|
+
|
|
11
|
+
- basename має суфікс `.example`, `.sample`, `.template` або `.dist` (наприклад, `.env.dist`, `config.example`);
|
|
12
|
+
- basename має infix `.example.`, `.sample.` або `.template.` (наприклад, `docker-compose.example.yml`, `app.config.sample.json`);
|
|
13
|
+
- файл лежить усередині каталогу `fixtures`, `fixture` або `__fixtures__` (на будь-якому рівні дерева).
|
|
14
|
+
|
|
15
|
+
Решта файлів не сканується — там слово `secret` майже завжди є частиною реального коду, а не placeholder.
|
|
16
|
+
|
|
17
|
+
Порушенням є лише `secret` у **позиції значення** — одразу після `=`, `:` чи `=>` (з опційними одинарними або подвійними лапками). Імена ключів (`client_secret`, `JWT_SECRET`) свідомо не чіпаються: матч прив'язаний до значення, а не до ключа.
|
|
18
|
+
|
|
19
|
+
Архітектурний вибір (regex замість AST) пояснюється тим, що прикладні файли — це різнорідні конфіги (`.env`, YAML, JSON, TOML, plain `.dist`), єдиного AST для них немає, тож скан виконується порядково. Вибір JavaScript замість Rego зумовлений необхідністю обходу дерева (`readdir`) для пошуку прикладних файлів, тоді як conftest/Rego парсить лише структуровані документи.
|
|
20
|
+
|
|
21
|
+
## Експорти / API
|
|
22
|
+
|
|
23
|
+
Файл є ES-модулем (`.mjs`) і експортує одну публічну функцію:
|
|
24
|
+
|
|
25
|
+
| Експорт | Тип | Призначення |
|
|
26
|
+
| ------- | ----------------------------------------- | ----------------------------------------------------------------- |
|
|
27
|
+
| `check` | `async (cwd?: string) => Promise<number>` | Запускає перевірку та повертає exit-код (0 — OK, 1 — є порушення) |
|
|
28
|
+
|
|
29
|
+
Внутрішні артефакти модуля (не експортуються):
|
|
30
|
+
|
|
31
|
+
- `EXAMPLE_SUFFIX_RE` — RegExp для перевірки суфікса basename'а прикладного файлу.
|
|
32
|
+
- `EXAMPLE_INFIX_RE` — RegExp для перевірки infix'а у basename'і.
|
|
33
|
+
- `FIXTURE_DIR_RE` — RegExp для виявлення сегмента шляху з фікстурами.
|
|
34
|
+
- `VALUE_SECRET_RE` — RegExp для виявлення bare-`secret` у позиції значення.
|
|
35
|
+
- `isExampleFile(relPosix)` — допоміжна функція класифікації файлу.
|
|
36
|
+
|
|
37
|
+
## Функції
|
|
38
|
+
|
|
39
|
+
### `isExampleFile(relPosix)`
|
|
40
|
+
|
|
41
|
+
**Сигнатура:** `function isExampleFile(relPosix: string): boolean`
|
|
42
|
+
|
|
43
|
+
**Параметри:**
|
|
44
|
+
|
|
45
|
+
- `relPosix` (`string`) — відносний шлях файлу від кореня репозиторію у POSIX-форматі (роздільник `/`, не `\`).
|
|
46
|
+
|
|
47
|
+
**Повертає:** `boolean` — `true`, якщо файл слід сканувати на наявність bare `secret`.
|
|
48
|
+
|
|
49
|
+
**Логіка:**
|
|
50
|
+
|
|
51
|
+
1. Виокремлює basename з `relPosix` шляхом обрізання за останнім `/`.
|
|
52
|
+
2. Перевіряє три умови (логічне АБО):
|
|
53
|
+
- `EXAMPLE_SUFFIX_RE.test(base)` — basename закінчується на `.example`, `.sample`, `.template` або `.dist`.
|
|
54
|
+
- `EXAMPLE_INFIX_RE.test(base)` — basename містить `.example.`, `.sample.` або `.template.`.
|
|
55
|
+
- `FIXTURE_DIR_RE.test(relPosix)` — повний відносний шлях містить сегмент `fixtures`, `fixture` або `__fixtures__`.
|
|
56
|
+
3. Повертає результат диз'юнкції.
|
|
57
|
+
|
|
58
|
+
**Side effects:** немає (чиста функція, працює лише з аргументом).
|
|
59
|
+
|
|
60
|
+
### `check(cwd?)`
|
|
61
|
+
|
|
62
|
+
**Сигнатура:** `async function check(cwd?: string): Promise<number>`
|
|
63
|
+
|
|
64
|
+
**Параметри:**
|
|
65
|
+
|
|
66
|
+
- `cwd` (`string`, опційний) — абсолютний шлях до кореня репозиторію. За замовчуванням — `process.cwd()`.
|
|
67
|
+
|
|
68
|
+
**Повертає:** `Promise<number>` — exit-код перевірки:
|
|
69
|
+
|
|
70
|
+
- `0` — порушень не знайдено (або прикладних файлів не знайдено взагалі);
|
|
71
|
+
- `1` — знайдено хоча б один bare `secret` у позиції значення.
|
|
72
|
+
|
|
73
|
+
**Алгоритм:**
|
|
74
|
+
|
|
75
|
+
1. Створює репортер через `createCheckReporter()` і деструктурує з нього методи `pass` і `fail`.
|
|
76
|
+
2. Ініціалізує масив `examples` для зберігання пар `{ abs, rel }` прикладних файлів.
|
|
77
|
+
3. Викликає `walkDir(cwd, callback)` — рекурсивний обхід дерева від `cwd`. У callback'у:
|
|
78
|
+
- обчислює відносний шлях `rel` через `relative(cwd, abs)`;
|
|
79
|
+
- нормалізує роздільники до `/` (заміна `sep` на `/`) — це важливо на Windows;
|
|
80
|
+
- якщо `isExampleFile(rel)` повертає `true`, додає об'єкт у `examples`.
|
|
81
|
+
4. Сортує `examples` за `rel` у лексикографічному порядку (`localeCompare`) — детермінує вихід.
|
|
82
|
+
5. Якщо `examples.length === 0`, репортує `pass('прикладних файлів не знайдено — placeholder перевіряти нема де')` і повертає exit-код репортера.
|
|
83
|
+
6. Інакше для кожного файлу:
|
|
84
|
+
- читає вміст через `readFile(abs, 'utf8')`;
|
|
85
|
+
- при будь-якій помилці читання (`try/catch` без re-throw) файл мовчки пропускається — `continue`;
|
|
86
|
+
- розбиває вміст на рядки за `\n`;
|
|
87
|
+
- для кожного рядка обрізає завершальний `\r` (CRLF-нормалізація);
|
|
88
|
+
- тестує рядок проти `VALUE_SECRET_RE`; якщо матч є:
|
|
89
|
+
- інкрементує лічильник `violations`;
|
|
90
|
+
- викликає `fail` з повідомленням формату `${rel}:${i + 1}: \`${line.trim()}\` — заміни placeholder \`secret\` на \`sample-secret\` (security.mdc)` (нумерація рядків — 1-based).
|
|
91
|
+
7. Якщо після обходу `violations === 0`, репортує `pass('прикладні файли (${examples.length}) не містять bare \`secret\`')`.
|
|
92
|
+
8. Повертає `reporter.getExitCode()`.
|
|
93
|
+
|
|
94
|
+
**Side effects:**
|
|
95
|
+
|
|
96
|
+
- Читання файлової системи: рекурсивний `walkDir` і `readFile` для кожного прикладного файлу.
|
|
97
|
+
- Виклики `pass`/`fail` репортера, який пише повідомлення у stdout/stderr (поведінка інкапсульована в `createCheckReporter`).
|
|
98
|
+
|
|
99
|
+
**Обробка помилок:** помилки `readFile` ловляться і ігноруються (continue). Це дозволяє пропускати файли без прав читання чи з тимчасовими IO-проблемами без падіння всієї перевірки.
|
|
100
|
+
|
|
101
|
+
## Залежності
|
|
102
|
+
|
|
103
|
+
### Стандартна бібліотека Node.js
|
|
104
|
+
|
|
105
|
+
| Імпорт | Шлях | Призначення |
|
|
106
|
+
| ---------- | ------------------ | ------------------------------------------------------------------ |
|
|
107
|
+
| `readFile` | `node:fs/promises` | Асинхронне читання файлу як UTF-8 рядка |
|
|
108
|
+
| `relative` | `node:path` | Обчислення відносного шляху від `cwd` до абсолютного шляху |
|
|
109
|
+
| `sep` | `node:path` | Платформозалежний роздільник шляхів (`/` на POSIX, `\` на Windows) |
|
|
110
|
+
|
|
111
|
+
### Внутрішні модулі проєкту
|
|
112
|
+
|
|
113
|
+
| Імпорт | Шлях | Призначення |
|
|
114
|
+
| --------------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------- |
|
|
115
|
+
| `createCheckReporter` | `../../../scripts/lib/check-reporter.mjs` | Створює об'єкт-репортер з методами `pass`, `fail`, `getExitCode` для уніфікованого виводу check-функцій |
|
|
116
|
+
| `walkDir` | `../../../scripts/utils/walkDir.mjs` | Рекурсивний обхід директорії з застосуванням callback'а до кожного файлу |
|
|
117
|
+
|
|
118
|
+
### Регулярні вирази
|
|
119
|
+
|
|
120
|
+
Усі RegExp мають флаги `u` (Unicode) і, де потрібно, `i` (case-insensitive):
|
|
121
|
+
|
|
122
|
+
- `EXAMPLE_SUFFIX_RE = /\.(?:example|sample|template|dist)$/iu` — суфікс basename'а.
|
|
123
|
+
- `EXAMPLE_INFIX_RE = /\.(?:example|sample|template)\./iu` — infix у basename'і (без `dist`, бо `dist` доречно лише як кінцевий суфікс).
|
|
124
|
+
- `FIXTURE_DIR_RE = /(?:^|\/)(?:__fixtures__|fixtures?)(?:\/|$)/u` — сегмент шляху з фікстурами (без флага `i`, бо назви каталогів зазвичай у нижньому регістрі).
|
|
125
|
+
- `VALUE_SECRET_RE = /[:=]>?\s*(['"]?)secret\1[\s,;}\])]*(?:(?:#|\/\/).*)?$/iu`:
|
|
126
|
+
- `[:=]>?` — `=`, `:` або `=>`;
|
|
127
|
+
- `\s*` — опційні пробіли;
|
|
128
|
+
- `(['"]?)secret\1` — слово `secret` з опційними **парними** лапками (`'`, `"` або без);
|
|
129
|
+
- `[\s,;}\])]*` — опційний хвіст (пробіли, кома, крапка з комою, закриваючі дужки);
|
|
130
|
+
- `(?:(?:#|\/\/).*)?$` — опційний коментар (`#` для shell/YAML/TOML, `//` для JS/JSON5) до кінця рядка;
|
|
131
|
+
- `$` гарантує, що `secret` — увесь токен значення (`secret-key`, `secretValue` не матчаться);
|
|
132
|
+
- `[:=]` як префікс відсікає імена ключів (`client_secret`).
|
|
133
|
+
|
|
134
|
+
## Потік виконання / Використання
|
|
135
|
+
|
|
136
|
+
### Типовий сценарій запуску
|
|
137
|
+
|
|
138
|
+
Модуль є точкою FS-частини правила `security/sample_secret`. Викликається оркестратором правил (зазвичай через CLI або CI), який:
|
|
139
|
+
|
|
140
|
+
1. Імпортує функцію `check` з `npm/rules/security/js/sample_secret.mjs`.
|
|
141
|
+
2. Викликає `await check(repoRoot)`, де `repoRoot` — абсолютний шлях до кореня монорепо.
|
|
142
|
+
3. Передає отриманий exit-код у власний агрегатор результатів.
|
|
143
|
+
|
|
144
|
+
Приклад прямого виклику з CLI-обгортки:
|
|
145
|
+
|
|
146
|
+
```js
|
|
147
|
+
import { check } from './sample_secret.mjs'
|
|
148
|
+
|
|
149
|
+
const code = await check(process.cwd())
|
|
150
|
+
process.exit(code)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Послідовність дій усередині `check`
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
walkDir(cwd) ──► фільтр isExampleFile ──► examples[] (відсортовано)
|
|
157
|
+
│
|
|
158
|
+
▼
|
|
159
|
+
для кожного файлу:
|
|
160
|
+
readFile ──► split('\n') ──► для кожного рядка:
|
|
161
|
+
├─ обрізати \r
|
|
162
|
+
├─ VALUE_SECRET_RE.test(line)?
|
|
163
|
+
│ ├─ ні → пропустити
|
|
164
|
+
│ └─ так → fail(rel:line: ...); violations++
|
|
165
|
+
▼
|
|
166
|
+
violations === 0 ? pass(...) : (вже були fail)
|
|
167
|
+
▼
|
|
168
|
+
return reporter.getExitCode()
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Інтеграція з системою правил
|
|
172
|
+
|
|
173
|
+
- Файл лежить у `npm/rules/security/js/`, що є FS-частиною модульного правила `security` (концерн `sample_secret`).
|
|
174
|
+
- Поряд може існувати Rego/conftest-частина для структурованих перевірок; цей файл відповідає лише за неструктуроване сканування тексту.
|
|
175
|
+
- Репортер `createCheckReporter` повертає exit-код, який інтегрується з загальним пайплайном лінтерів/перевірок проєкту.
|
|
176
|
+
|
|
177
|
+
### Поведінкові гарантії
|
|
178
|
+
|
|
179
|
+
- **Детермінізм:** сортування `examples` за `rel.localeCompare` гарантує однаковий порядок повідомлень `fail` на різних запусках.
|
|
180
|
+
- **Кросплатформність:** нормалізація `sep → '/'` забезпечує єдиний формат шляхів у логах і регекспах незалежно від ОС.
|
|
181
|
+
- **Стійкість:** помилки `readFile` ловляться — недоступний файл не валить перевірку.
|
|
182
|
+
- **Точність:** прив'язки `[:=]` зліва і `$` справа у `VALUE_SECRET_RE` мінімізують false positives — імена ключів, складені значення (`secret-key`) і слово `secret` у середині рядка не матчаться.
|
|
183
|
+
|
|
184
|
+
### Що НЕ робить цей модуль
|
|
185
|
+
|
|
186
|
+
- Не сканує не-прикладні файли (звичайний код, документація поза `fixtures/`).
|
|
187
|
+
- Не перевіряє імена ключів (`client_secret`, `JWT_SECRET` дозволені).
|
|
188
|
+
- Не автоматично замінює `secret` на `sample-secret` — лише репортує; виправлення робить розробник вручну.
|
|
189
|
+
- Не парсить структуровані формати (YAML/JSON/TOML) — це робота Rego-частини правила, якщо вона існує.
|
|
190
|
+
- Не виконує реальну детекцію секретів — це робота TruffleHog; модуль лише гарантує сумісність placeholder'ів із вшитим whitelist'ом TruffleHog.
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# trufflehog.mjs
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Модуль `trufflehog.mjs` — це FS-частина (файлово-системна перевірка) правила `security` з родини cursor-rules монорепозиторію. Його єдина відповідальність — перевірити, що в корені перевіреного репозиторію присутні два артефакти, без яких правило `security.mdc` вважається порушеним:
|
|
6
|
+
|
|
7
|
+
1. файл `package.json` у корені — самим фактом існування (структуру цього файлу валідує окрема policy `security.package_json` через Rego/OPA, тут вона навмисно не дублюється);
|
|
8
|
+
2. файл `.trufflehog-exclude` у корені, який містить канонічний набір patterns. Оскільки `.trufflehog-exclude` — це plain-text формат (а не JSON/YAML), порівняння виконується методом «text-subset»: усі канонічні рядки зі snippet-шаблону мають бути присутні в репозитарному файлі, але репозиторій може мати додаткові локальні рядки.
|
|
9
|
+
|
|
10
|
+
Модуль експортує єдину async-функцію `check`, яка повертає exit-код для подальшого використання CLI-обгорткою (наприклад, скриптом-агрегатором перевірок або CI-сценарієм). Усі повідомлення про результат проходять через спільний reporter, тож формат виводу й коди виходу узгоджені з іншими `check-*` модулями цього репозиторію.
|
|
11
|
+
|
|
12
|
+
Файл вмонтований у конвенцію `npm/rules/<rule>/js/`: він навмисно не виконує мережевих або системних операцій, не пише в файли, не запускає підпроцеси — це чиста read-only перевірка артефактів у переданому або поточному cwd.
|
|
13
|
+
|
|
14
|
+
## Експорти / API
|
|
15
|
+
|
|
16
|
+
Модуль експортує лише one named export:
|
|
17
|
+
|
|
18
|
+
| Експорт | Тип | Призначення |
|
|
19
|
+
| ------- | -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
20
|
+
| `check` | `async function (cwd?: string) => Promise<number>` | Запустити перевірки FS-частини правила `security` для заданого кореня репозиторію і повернути числовий exit-код. |
|
|
21
|
+
|
|
22
|
+
Default export відсутній. Жодних інших символів (констант, класів, утиліт) модуль назовні не передає; внутрішні константи `HERE` та `SNIPPET_PATH` залишаються приватними для модуля.
|
|
23
|
+
|
|
24
|
+
## Функції
|
|
25
|
+
|
|
26
|
+
### `check(cwd?)`
|
|
27
|
+
|
|
28
|
+
Головна (і єдина) експортована функція модуля.
|
|
29
|
+
|
|
30
|
+
Сигнатура:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
async function check(cwd: string = process.cwd()): Promise<number>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Параметри:
|
|
37
|
+
|
|
38
|
+
| Ім'я | Тип | Default | Опис |
|
|
39
|
+
| ----- | -------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
40
|
+
| `cwd` | `string` | `process.cwd()` | Абсолютний (або відносний до процесу) шлях до кореня репозиторію, який треба перевірити. Усі шляхи до `package.json` і `.trufflehog-exclude` будують через `join(cwd, ...)`. Якщо параметр не передано — використовується поточний робочий каталог процесу Node.js. |
|
|
41
|
+
|
|
42
|
+
Повертає: `Promise<number>` — exit-код, який повертає reporter (`reporter.getExitCode()`). Конвенція reporter-а: `0` — усі перевірки `pass`, ненульове значення — є хоча б одна `fail`. Конкретне ненульове значення (1, 2 тощо) визначається реалізацією `createCheckReporter` у `scripts/lib/check-reporter.mjs` і модулем `trufflehog.mjs` не нав'язується.
|
|
43
|
+
|
|
44
|
+
Покрокова поведінка:
|
|
45
|
+
|
|
46
|
+
1. Створює локальний reporter через `createCheckReporter()` і деструктурує з нього методи `pass` та `fail` для читабельності викликів.
|
|
47
|
+
2. Перевіряє наявність `package.json` у корені:
|
|
48
|
+
- якщо файлу немає — викликає `fail('package.json не знайдено в корені — додай (security.mdc)')` і **одразу** повертає `reporter.getExitCode()`, не виконуючи решту перевірок (це early-exit, оскільки без `package.json` решта канонів безпеки втрачає сенс);
|
|
49
|
+
- якщо файл є — викликає `pass('package.json є (структуру перевіряє Rego)')` і йде далі.
|
|
50
|
+
3. Перевіряє наявність `.trufflehog-exclude` у корені:
|
|
51
|
+
- якщо файлу немає — викликає `fail('.trufflehog-exclude не знайдено в корені — додай за каноном (security.mdc)')` і повертає exit-код (теж early-exit);
|
|
52
|
+
- якщо файл є — переходить до text-subset порівняння.
|
|
53
|
+
4. Читає вміст обох файлів — фактичного `.trufflehog-exclude` із cwd та snippet-шаблону `templates/trufflehog/.trufflehog-exclude.snippet.txt` поряд із модулем — в `utf8`.
|
|
54
|
+
5. Викликає `checkTextSubset(actual, template, { targetPath: '.trufflehog-exclude', source: 'security.mdc' })`. Утиліта повертає масив рядків-повідомлень про помилки: порожній масив означає, що всі канонічні patterns з snippet-у наявні у фактичному файлі.
|
|
55
|
+
6. Для кожного повідомлення з `errors` викликає `fail(msg)`. Якщо `errors` порожній — викликає `pass('.trufflehog-exclude містить канонічні patterns')`.
|
|
56
|
+
7. Повертає `reporter.getExitCode()`.
|
|
57
|
+
|
|
58
|
+
Side effects:
|
|
59
|
+
|
|
60
|
+
- Читання файлової системи: синхронний `existsSync` (двічі) та асинхронний `readFile` (двічі);
|
|
61
|
+
- Запис у reporter (внутрішній акумулятор стану `pass`/`fail`), який, залежно від реалізації, може писати в `stdout`/`stderr`;
|
|
62
|
+
- Жодних мутацій FS, жодних мережевих викликів, жодних env-залежностей крім стандартного `process.cwd()` як default-значення параметра.
|
|
63
|
+
|
|
64
|
+
Поведінкові інваріанти:
|
|
65
|
+
|
|
66
|
+
- Функція **не** кидає винятки в нормальному сценарії «файла нема» — це штатний `fail`. Винятки можливі лише з нижчих рівнів: наприклад, якщо `readFile` не може прочитати існуючий файл через права доступу, або якщо `SNIPPET_PATH` не існує в дистрибутиві модуля (це вже баг дистрибутива, а не вхідних даних).
|
|
67
|
+
- Порядок перевірок фіксований: `package.json` → `.trufflehog-exclude` (наявність) → `.trufflehog-exclude` (text-subset). При фейлі на ранньому кроці наступні **не** виконуються.
|
|
68
|
+
- Перевірка text-subset не вимагає точної рівності файлів: snippet — це мінімум, репозиторій може містити додаткові рядки понад нього.
|
|
69
|
+
|
|
70
|
+
## Залежності
|
|
71
|
+
|
|
72
|
+
Built-in модулі Node.js:
|
|
73
|
+
|
|
74
|
+
- `node:fs` — використовується іменований імпорт `existsSync` для перевірки наявності `package.json` та `.trufflehog-exclude` без читання їхнього вмісту;
|
|
75
|
+
- `node:fs/promises` — іменований імпорт `readFile` для асинхронного читання текстового вмісту обох файлів у кодуванні `utf8`;
|
|
76
|
+
- `node:path` — `dirname` для отримання директорії поточного модуля з його URL і `join` для побудови шляхів до `package.json`, `.trufflehog-exclude` та snippet-шаблону;
|
|
77
|
+
- `node:url` — `fileURLToPath` для перетворення `import.meta.url` у звичайний шлях файлової системи (стандартний ESM-патерн).
|
|
78
|
+
|
|
79
|
+
Внутрішні модулі репозиторію:
|
|
80
|
+
|
|
81
|
+
- `../../../scripts/lib/check-reporter.mjs` — фабрика reporter-а `createCheckReporter`. Reporter інкапсулює стан перевірок: акумулює `pass`/`fail`-повідомлення та віддає кінцевий exit-код через `getExitCode()`. Уся семантика виводу (де і як друкується, як кодуються рівні) лежить там.
|
|
82
|
+
- `../../../scripts/lib/template.mjs` — утиліта `checkTextSubset(actual, template, opts)`. Перевіряє, що `template` є підмножиною `actual` (у текстовому сенсі — рядки/блоки шаблону мають зустрічатися у фактичному файлі). Опції `targetPath` та `source` використовуються для форматування повідомлень про помилки (щоб користувач бачив, який саме файл і яке правило порушено).
|
|
83
|
+
|
|
84
|
+
Файлові артефакти, які модуль читає в runtime:
|
|
85
|
+
|
|
86
|
+
- `<cwd>/package.json` — фактичний package.json репозиторію, що перевіряється (лише наявність);
|
|
87
|
+
- `<cwd>/.trufflehog-exclude` — фактичний exclude-файл (наявність + вміст);
|
|
88
|
+
- `<HERE>/templates/trufflehog/.trufflehog-exclude.snippet.txt` — канонічний snippet-шаблон, що поставляється разом із модулем. `HERE` = `dirname(fileURLToPath(import.meta.url))`, тобто директорія самого `trufflehog.mjs`. Цей файл — джерело істини для text-subset порівняння.
|
|
89
|
+
|
|
90
|
+
Зовнішніх npm-залежностей модуль не має.
|
|
91
|
+
|
|
92
|
+
## Потік виконання / Використання
|
|
93
|
+
|
|
94
|
+
Типовий сценарій використання — виклик з вищерівневого скрипта-агрегатора перевірок правила `security`, який послідовно прогоняє FS-частину (`trufflehog.mjs`) і policy-частину (Rego/OPA для структури `package.json`). У цій ролі модуль працює як CLI-сумісна функція:
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
import { check } from './trufflehog.mjs'
|
|
98
|
+
|
|
99
|
+
const code = await check() // використає process.cwd()
|
|
100
|
+
process.exit(code)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Або з явним коренем:
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
import { check } from './trufflehog.mjs'
|
|
107
|
+
|
|
108
|
+
const code = await check('/path/to/repo')
|
|
109
|
+
process.exit(code)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Послідовність кроків у runtime (happy path):
|
|
113
|
+
|
|
114
|
+
1. Викликач передає `cwd` (або не передає — береться `process.cwd()`).
|
|
115
|
+
2. Створюється reporter — порожній акумулятор стану.
|
|
116
|
+
3. `existsSync(join(cwd, 'package.json'))` → `true` → `pass(...)`.
|
|
117
|
+
4. `existsSync(join(cwd, '.trufflehog-exclude'))` → `true` → перехід до читання.
|
|
118
|
+
5. Паралельно (через `await` послідовно, але без перехресної залежності) читаються `actual` та `template`.
|
|
119
|
+
6. `checkTextSubset(actual, template, opts)` → `[]` (порожній масив помилок).
|
|
120
|
+
7. Цикл `for (const msg of errors)` нічого не виконує.
|
|
121
|
+
8. Викликається `pass('.trufflehog-exclude містить канонічні patterns')`.
|
|
122
|
+
9. Повертається `reporter.getExitCode()` — у happy-сценарії це `0`.
|
|
123
|
+
|
|
124
|
+
Гілки відмови (sad paths):
|
|
125
|
+
|
|
126
|
+
- Немає `package.json` у `cwd` → один `fail` про відсутність → exit-код ≠ 0, перевірка `.trufflehog-exclude` **не** виконується.
|
|
127
|
+
- Є `package.json`, але немає `.trufflehog-exclude` → `pass` для першого та `fail` для другого → exit-код ≠ 0.
|
|
128
|
+
- Обидва файли є, але `.trufflehog-exclude` не містить деяких канонічних patterns → стільки `fail`, скільки повідомлень повернула `checkTextSubset` → exit-код ≠ 0. Жодного `pass` про канонічність у цьому випадку не друкується.
|
|
129
|
+
|
|
130
|
+
Контекст у проєкті:
|
|
131
|
+
|
|
132
|
+
- Модуль належить до родини `npm/rules/<rule>/js/` і реалізує JS-half правила `security`. Поряд із ним очікується тека `templates/trufflehog/` з файлом `.trufflehog-exclude.snippet.txt` — без неї `readFile` для snippet кине помилку.
|
|
133
|
+
- Текстовий формат повідомлень `pass`/`fail` ідентичний іншим перевіркам репозиторію (через спільний `check-reporter`), що дозволяє агрегатору одноманітно показувати результати.
|
|
134
|
+
- Розділення «FS-частина перевіряє наявність, Rego — структуру» — навмисний design choice: він зафіксований у `security.mdc` і повторений у doc-string модуля. У `trufflehog.mjs` **не** можна додавати парсинг JSON `package.json` чи логіку валідації полів — це порушує контракт із Rego-policy.
|
|
135
|
+
- text-subset (а не точна рівність) обраний саме тому, що `.trufflehog-exclude` — plain text без структури: канон постачає мінімум обов'язкових patterns, а команди можуть розширювати свій exclude-файл локальними правилами без порушення security-канону.
|
|
136
|
+
|
|
137
|
+
Rebuild test: за цією документацією можна відновити поведінку модуля від інтерфейсу до side-effects — сигнатуру `check`, default-значення `cwd`, послідовність двох FS-перевірок з early-exit, виклик `checkTextSubset` з опціями `targetPath`/`source`, формування exit-коду через `reporter.getExitCode()`, перелік повідомлень `fail`/`pass` і відсутність зовнішніх npm-залежностей.
|
|
@@ -11,7 +11,15 @@ import { spawnSync } from 'node:child_process'
|
|
|
11
11
|
export function lint(_files, cwd = process.cwd()) {
|
|
12
12
|
const r = spawnSync(
|
|
13
13
|
'trufflehog',
|
|
14
|
-
[
|
|
14
|
+
[
|
|
15
|
+
'filesystem',
|
|
16
|
+
'.',
|
|
17
|
+
'--no-update',
|
|
18
|
+
'--exclude-paths',
|
|
19
|
+
'.trufflehog-exclude',
|
|
20
|
+
'--results=verified,unknown',
|
|
21
|
+
'--fail'
|
|
22
|
+
],
|
|
15
23
|
{ cwd, stdio: 'inherit' }
|
|
16
24
|
)
|
|
17
25
|
return Promise.resolve(typeof r.status === 'number' ? r.status : 1)
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# fix.mjs — точка входу правила `style-lint`
|
|
2
|
+
|
|
3
|
+
## Огляд
|
|
4
|
+
|
|
5
|
+
Файл `npm/rules/style-lint/fix.mjs` — це точка входу (entry-point) правила з ідентифікатором `style-lint` у системі `@nitra/cursor`. Він виконує дві ролі одночасно:
|
|
6
|
+
|
|
7
|
+
1. **Library mode** — експортує функцію `run(ctx)`, яку зовнішній CLI-оркестратор (`npx @nitra/cursor fix <id>` чи внутрішній диспетчер правил) імпортує й запускає в межах загального прогону пакета правил.
|
|
8
|
+
2. **Standalone mode** — якщо файл запущено напряму через `bun rules/style-lint/fix.mjs`, він самостійно ініціалізує конфіг, whitelist, summary та повертає exit-code, повністю еквівалентний виклику `npx @nitra/cursor fix style-lint`.
|
|
9
|
+
|
|
10
|
+
Сам файл навмисно тонкий: вся логіка перевірок винесена у спільні утиліти `runStandardRule` (стандартний пайплайн правила: `applies → JS-concerns → policy → mdc-refs`) і `runRuleCli` (обгортка для standalone-запуску). Це канонічна форма entry-point для будь-якого правила в монорепо `@nitra/cursor`, що дозволяє додавати нові правила, не дублюючи orchestration-код.
|
|
11
|
+
|
|
12
|
+
Правило `style-lint` оперує над стилями (Style/CSS-частина) — деталі його перевірок мешкають у сусідніх теках (`js/`, `policy/`, `style-lint.mdc`), але сам цей файл їх не імпортує: пайплайн виявляє їх автоматично за конвенцією директорії `import.meta.dirname`.
|
|
13
|
+
|
|
14
|
+
## Експорти / API
|
|
15
|
+
|
|
16
|
+
| Експорт | Тип | Призначення |
|
|
17
|
+
| ------- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
|
|
18
|
+
| `run` | `function (ctx?: RuleContext) => Promise<number>` | Запускає стандартний пайплайн правила `style-lint`. Повертає `0` за відсутності порушень, `1` — за наявності. |
|
|
19
|
+
|
|
20
|
+
Експорт `run` — це **публічний контракт правила**. Будь-який оркестратор, що знає лише шлях до директорії правила, може динамічно імпортувати `fix.mjs` і викликати `run(ctx)`.
|
|
21
|
+
|
|
22
|
+
Жодних інших іменованих експортів, default-експорту, констант чи класів файл не надає.
|
|
23
|
+
|
|
24
|
+
## Функції
|
|
25
|
+
|
|
26
|
+
### `run(ctx)`
|
|
27
|
+
|
|
28
|
+
```js
|
|
29
|
+
export function run(ctx) {
|
|
30
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
- **Сигнатура:** `run(ctx?: RuleContext): Promise<number>`
|
|
35
|
+
- **Параметри:**
|
|
36
|
+
- `ctx` (необов'язковий) — об'єкт контексту прогону типу `RuleContext` (визначений у `../../scripts/lib/run-standard-rule.mjs`). Містить, зокрема, `walkCache` — кеш обходу файлової системи, який оркестратор переюзує між правилами, щоб не сканувати дерево повторно. Якщо `ctx` не передано — `runStandardRule` створить дефолтний контекст самостійно.
|
|
37
|
+
- **Повертає:** `Promise<number>` — exit-code:
|
|
38
|
+
- `0` — правило виконано, порушень не знайдено;
|
|
39
|
+
- `1` — знайдено порушення (CI має зафейлити збірку).
|
|
40
|
+
- **Side effects:**
|
|
41
|
+
- Звертається до файлової системи через `runStandardRule` (читає файли проєкту, що підпадають під `applies`-фільтр правила).
|
|
42
|
+
- Може писати у stdout/stderr (summary, помилки) через утиліти всередині `runStandardRule`.
|
|
43
|
+
- Сам по собі функція **не** мутує жодного файлу — навіть для правил із суфіксом `fix.mjs` пайплайн у режимі звичайного прогону є read-only (модифікації виконуються в окремих fix-режимах, які тут не задіяні).
|
|
44
|
+
- **Як працює:** делегує всю роботу `runStandardRule`, передаючи їй власну директорію (`import.meta.dirname` = абсолютний шлях до `npm/rules/style-lint/`). Пайплайн всередині послідовно прогонить чотири фази:
|
|
45
|
+
1. **applies** — визначення, які файли проєкту підпадають під дію правила;
|
|
46
|
+
2. **JS-concerns** — JS/MJS-перевірки (логіка з `js/`);
|
|
47
|
+
3. **policy** — Rego/політики (логіка з `policy/`);
|
|
48
|
+
4. **mdc-refs** — звірення посилань у `style-lint.mdc` (актуальність документації).
|
|
49
|
+
|
|
50
|
+
### Standalone-блок (top-level `if`)
|
|
51
|
+
|
|
52
|
+
```js
|
|
53
|
+
if (isRunAsCli(import.meta.url)) {
|
|
54
|
+
process.exit(await runRuleCli(import.meta.dirname))
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
- **Не функція**, а top-level guard, що виконується лише коли файл стартує безпосередньо як CLI-точка (а не імпортується іншим модулем).
|
|
59
|
+
- **Детект:** `isRunAsCli(import.meta.url)` повертає `true`, коли `import.meta.url` відповідає аргументу запуску процесу (тобто `bun npm/rules/style-lint/fix.mjs` чи `node npm/rules/style-lint/fix.mjs`).
|
|
60
|
+
- **Дія:** викликає `runRuleCli(import.meta.dirname)`, яка проганяє повний цикл CLI (config-loading, whitelist, summary, exit-code) і завершує процес через `process.exit` зі здобутим кодом.
|
|
61
|
+
- **Чому `await` на верхньому рівні:** файл — ES-модуль `.mjs`, top-level `await` дозволений.
|
|
62
|
+
- **Eslint-винятки:** `n/no-process-exit` та `unicorn/no-process-exit` навмисно відключені рядковим коментарем, бо для standalone entry-point явний `process.exit` — єдиний коректний спосіб віддати exit-code CI/IDE.
|
|
63
|
+
|
|
64
|
+
## Залежності
|
|
65
|
+
|
|
66
|
+
### Внутрішні (relative imports)
|
|
67
|
+
|
|
68
|
+
| Модуль | Що з нього імпортовано | Роль |
|
|
69
|
+
| ----------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
|
70
|
+
| `../../scripts/lib/run-rule-cli.mjs` | `isRunAsCli`, `runRuleCli` | Утиліти standalone-режиму: детект CLI-запуску та повний CLI-цикл (config + whitelist + summary). |
|
|
71
|
+
| `../../scripts/lib/run-standard-rule.mjs` | `runStandardRule` | Стандартний пайплайн правила (applies → JS-concerns → policy → mdc-refs). Також надає тип `RuleContext` (через JSDoc-typedef). |
|
|
72
|
+
|
|
73
|
+
Шляхи `../../scripts/lib/...` обчислюються відносно `npm/rules/style-lint/` і вказують на `npm/scripts/lib/`.
|
|
74
|
+
|
|
75
|
+
### Зовнішні
|
|
76
|
+
|
|
77
|
+
- **Жодних** npm-пакетів файл напряму не імпортує. Усі залежності — внутрішні.
|
|
78
|
+
- **Runtime:** Node.js / Bun з підтримкою ESM, `import.meta.url`, `import.meta.dirname` та top-level `await`.
|
|
79
|
+
|
|
80
|
+
### Сусідні артефакти правила (не імпортуються тут, але задіяні через пайплайн)
|
|
81
|
+
|
|
82
|
+
- `npm/rules/style-lint/meta.json` — метадані правила (id, applies-патерни тощо), читається `runStandardRule`/`runRuleCli`.
|
|
83
|
+
- `npm/rules/style-lint/style-lint.mdc` — людиночитна специфікація правила (target для mdc-refs-фази).
|
|
84
|
+
- `npm/rules/style-lint/js/` — JS-частина перевірок (підвантажується JS-concerns-фазою).
|
|
85
|
+
- `npm/rules/style-lint/policy/` — Rego-політики (підвантажуються policy-фазою).
|
|
86
|
+
|
|
87
|
+
## Потік виконання / Використання
|
|
88
|
+
|
|
89
|
+
### Сценарій A — імпорт оркестратором (library mode)
|
|
90
|
+
|
|
91
|
+
```text
|
|
92
|
+
@nitra/cursor CLI (або інший runner)
|
|
93
|
+
│
|
|
94
|
+
│ dynamic import('npm/rules/style-lint/fix.mjs')
|
|
95
|
+
▼
|
|
96
|
+
fix.mjs → export run(ctx)
|
|
97
|
+
│
|
|
98
|
+
│ runStandardRule(import.meta.dirname, ctx)
|
|
99
|
+
▼
|
|
100
|
+
run-standard-rule.mjs
|
|
101
|
+
│
|
|
102
|
+
├── applies-фаза (фільтр файлів)
|
|
103
|
+
├── JS-concerns-фаза (js/)
|
|
104
|
+
├── policy-фаза (policy/)
|
|
105
|
+
└── mdc-refs-фаза (style-lint.mdc)
|
|
106
|
+
│
|
|
107
|
+
▼
|
|
108
|
+
return 0 | 1
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Оркестратор зазвичай передає `ctx` із попередньо побудованим `walkCache`, щоб уникнути повторного обходу файлового дерева між десятками правил.
|
|
112
|
+
|
|
113
|
+
### Сценарій B — пряма CLI-точка (standalone mode)
|
|
114
|
+
|
|
115
|
+
```text
|
|
116
|
+
$ bun npm/rules/style-lint/fix.mjs
|
|
117
|
+
│
|
|
118
|
+
│ Node/Bun завантажує fix.mjs як головний модуль
|
|
119
|
+
▼
|
|
120
|
+
import-блок виконано → run експортовано
|
|
121
|
+
│
|
|
122
|
+
▼
|
|
123
|
+
top-level if (isRunAsCli(import.meta.url)) // true
|
|
124
|
+
│
|
|
125
|
+
│ await runRuleCli(import.meta.dirname)
|
|
126
|
+
▼
|
|
127
|
+
run-rule-cli.mjs
|
|
128
|
+
│
|
|
129
|
+
├── завантажує конфіг проєкту
|
|
130
|
+
├── застосовує whitelist
|
|
131
|
+
├── викликає внутрішньо аналог run(ctx)
|
|
132
|
+
├── друкує summary
|
|
133
|
+
└── повертає number (exit-code)
|
|
134
|
+
│
|
|
135
|
+
▼
|
|
136
|
+
process.exit(<exit-code>)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Цей режим використовується розробником локально (`bun npm/rules/style-lint/fix.mjs`) або IDE-інтеграцією, що очікує POSIX exit-code.
|
|
140
|
+
|
|
141
|
+
### Інваріанти та контракт
|
|
142
|
+
|
|
143
|
+
- Файл — **stateless**: жодних модульних змінних, кешів, синглтонів. Усе передається через `ctx`.
|
|
144
|
+
- Функція `run` **ідемпотентна** щодо файлової системи (read-only прогон).
|
|
145
|
+
- `run` **завжди** повертає `Promise<number>` зі значенням `0` або `1` — оркестратор не повинен очікувати інших значень чи кидків.
|
|
146
|
+
- Standalone-блок виконується **виключно** коли файл — головний модуль; при `import` із іншого коду блок мовчазно пропускається завдяки `isRunAsCli`.
|
|
147
|
+
|
|
148
|
+
### Як додати аналогічний entry-point для нового правила
|
|
149
|
+
|
|
150
|
+
1. Створити теку `npm/rules/<new-rule-id>/`.
|
|
151
|
+
2. Скопіювати цей `fix.mjs` без змін (шляхи відносні до `npm/rules/<id>/` ідентичні).
|
|
152
|
+
3. Покласти поруч `meta.json`, `<new-rule-id>.mdc`, `js/`, `policy/` за потреби.
|
|
153
|
+
4. Пайплайн `runStandardRule` підхопить нове правило автоматично, без правок самого `fix.mjs`.
|
|
154
|
+
|
|
155
|
+
Це і є цінність «тонкого» entry-point: правила додаються декларативно, без дублювання orchestration-логіки.
|