@nitra/cursor 1.13.31 → 1.13.38
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/CHANGELOG.md +47 -1
- package/package.json +1 -1
- package/rules/changelog/fix/consistency/check.mjs +100 -85
- package/rules/ci4/ci4.mdc +7 -7
- package/rules/image-avif/image-avif.mdc +2 -2
- package/rules/js-lint/policy/vscode_extensions/template/extensions.json.snippet.json +1 -5
- package/rules/js-run/fix/runtime/check.mjs +3 -0
- package/rules/js-run/js-run.mdc +16 -1
- package/rules/js-run/policy/package_json/package_json.rego +17 -0
- package/rules/js-run/policy/package_json/template/package.json.deny.json +13 -1
- package/rules/k8s/fix/kubescape_exceptions/template/.kubescape-exceptions.json.snippet.json +21 -0
- package/rules/k8s/fix/manifests/check.mjs +775 -139
- package/rules/k8s/k8s.mdc +60 -6
- package/rules/k8s/lint/lint.mjs +29 -4
- package/rules/k8s/policy/base_kustomization/base_kustomization.rego +13 -6
- package/rules/k8s/policy/network_policy/network_policy.rego +158 -0
- package/rules/k8s/policy/network_policy/template/networkpolicy.snippet.yaml +32 -0
- package/rules/security/fix/trufflehog/check.mjs +3 -0
- package/rules/security/policy/package_json/template/package.json.snippet.json +5 -1
- package/rules/style-lint/style-lint.mdc +20 -1
- package/rules/text/policy/cspell/cspell.rego +1 -1
- package/rules/text/policy/markdownlint/markdownlint.rego +1 -1
- package/rules/text/policy/oxfmtrc/template/.oxfmtrc.json.snippet.json +1 -5
- package/rules/text/policy/vscode_extensions/template/extensions.json.snippet.json +1 -5
- package/rules/vue/vue.mdc +1 -0
- package/scripts/sync-claude-config.mjs +2 -2
- package/scripts/utils/check-mdc-template-refs.mjs +15 -5
- package/scripts/utils/inline-template-links.mjs +15 -8
- package/scripts/utils/package-manifest.mjs +24 -19
- package/scripts/utils/run-conftest-batch.mjs +22 -15
- package/scripts/utils/template.mjs +89 -21
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,52 @@
|
|
|
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.38] - 2026-05-18
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `js-run` rule: у backend `package.json#scripts` заборонено `env $(cat …) bun` — заміна на `bun --env-file=…` (по файлу з `cat`); Rego `scriptsForbidden` `env-cat-bun`. Bump `js-run.mdc` `1.10` → `1.11`.
|
|
12
|
+
|
|
13
|
+
## [1.13.37] - 2026-05-18
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- `js-run` rule: у backend `package.json#scripts` заборонено запуск через `node` — один runtime **Bun** у dev і prod; Rego `js_run.package_json` (`scriptsForbidden` у `package.json.deny.json`), frontend з `vite` у `devDependencies` пропускається. Bump `js-run.mdc` `1.9` → `1.10`.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- `k8s` rule: канон **NetworkPolicy** egress для всіх workload-ів — kube-dns; **TCP 80/443** на `0.0.0.0/0`; інші порти лише in-cluster (`namespaceSelector: {}`, `*.svc`). Заборонено `egress: [{}]`. Оновлено `buildNetworkPolicyYaml`, rego `k8s.network_policy`, template. Bump `k8s.mdc` `1.33` → `1.34`.
|
|
22
|
+
|
|
23
|
+
## [1.13.36] - 2026-05-18
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- `k8s` rule: **NetworkPolicy** обов'язковий не лише для **Deployment**, а й для **StatefulSet**, **DaemonSet**, **Job**, **CronJob** (`workloadAppLabel`, multi-doc `networkpolicy.yaml`, autofix/validate для всіх шарів `k8s`). HPA/PDB лишаються прив'язаними до Deployment. Bump `k8s.mdc` `1.32` → `1.33`.
|
|
28
|
+
|
|
29
|
+
## [1.13.35] - 2026-05-18
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
- `k8s` rule: для кожного **Deployment** під `k8s` обов'язковий **NetworkPolicy** — у `components/networkpolicy.yaml` для base (разом із HPA/PDB) або `networkpolicy.yaml` поруч у не-base оверлеях. Rego-пакет `k8s.network_policy`, перевірка прив'язки за `metadata.name` / міткою `app` у JS. **`check k8s`** автоматично створює відсутній `networkpolicy.yaml` і додає його в `components/kustomization.yaml` (`resources`). Bump `k8s.mdc` `1.31` → `1.32`.
|
|
34
|
+
|
|
35
|
+
## [1.13.34] - 2026-05-18
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
|
|
39
|
+
- `k8s` rule: винесено канонічний приклад `.kubescape-exceptions.json` з inline-fenced-блоку в `k8s.mdc` у `fix/kubescape_exceptions/template/.kubescape-exceptions.json.snippet.json`; `.mdc` тепер посилається на template markdown-лінком, `inlineTemplateLinks` підставить вміст у `.cursor/rules/n-k8s.mdc` під час sync. Dogfood новій клаузі `scripts.mdc` ("Принцип поширюється і на pure-doc канони"). Bump `k8s.mdc` `1.30` → `1.31`.
|
|
40
|
+
|
|
41
|
+
## [1.13.33] - 2026-05-18
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
|
|
45
|
+
- `style-lint`, `image-avif` rules: markdown-посилання на `policy/*/template/*` у канонічних `<id>.mdc` — `findMissingMdcRefs` (викликається з `run-rule.mjs`) падав, бо шаблони не були згадані в `npm/rules/<id>/<id>.mdc`. Bump: `style-lint.mdc` `1.3` → `1.4`, `image-avif.mdc` `1.2` → `1.3`.
|
|
46
|
+
|
|
47
|
+
## [1.13.32] - 2026-05-18
|
|
48
|
+
|
|
49
|
+
### Added
|
|
50
|
+
|
|
51
|
+
- `k8s` rule (`lint-k8s`): підтримка per-project винятків kubescape — якщо в корені проєкту є `.kubescape-exceptions.json`, `runKubescape` автоматично передає його через `--exceptions <file>`. Канонічний приклад — control **C-0012** (`Applications credentials in configuration files`) на ConfigMap з публічним JWT-конфігом (`HASURA_GRAPHQL_JWT_SECRET={"jwk_url": "https://…"}`): control тригериться лише на імʼя env, не на значення, тому точкове `postureExceptionPolicy` з `kind: ConfigMap` + `attributes.name` знімає false-positive без глобального вимкнення контролю. Bump `k8s.mdc` `1.29` → `1.30`. Документація — секція "Винятки kubescape" в `k8s.mdc`.
|
|
52
|
+
|
|
7
53
|
## [1.13.31] - 2026-05-18
|
|
8
54
|
|
|
9
55
|
### Changed
|
|
@@ -174,7 +220,7 @@
|
|
|
174
220
|
|
|
175
221
|
- `ga.mdc` — 4 inline YAML-блоки повних workflow канонів замінено на markdown-посилання до `template/<workflow>.yml.snippet.yml`. Файл скоротився суттєво — канон тепер живе як data, а не як прозовий приклад.
|
|
176
222
|
- `template/clean-merged-branch.yml.snippet.yml`: `dry_run: false` (явний bool) замість `dry_run: no` — `yaml` npm (YAML 1.2) лишає `no` рядком, а Go-yaml у conftest нормалізує до `false`; пишемо канонізовану форму під runtime conftest-парсингу.
|
|
177
|
-
- `docs/adr/template-dir-concern-inventory.md` — додано 4 нові full-canon ga
|
|
223
|
+
- `docs/adr/template-dir-concern-inventory.md` — додано 4 нові full-canon ga.\* концерни з ✓; оновлено summary (89 концернів, 43 з template — мігровано 13/43 = 30%).
|
|
178
224
|
|
|
179
225
|
### TODO
|
|
180
226
|
|
package/package.json
CHANGED
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
getMonorepoProjectRootDirs,
|
|
27
27
|
manifestFilePath,
|
|
28
28
|
parsePyprojectFields,
|
|
29
|
-
readPackageManifest
|
|
29
|
+
readPackageManifest
|
|
30
30
|
} from '../../../../scripts/utils/package-manifest.mjs'
|
|
31
31
|
|
|
32
32
|
const execFileAsync = promisify(execFile)
|
|
@@ -46,10 +46,12 @@ const CHANGELOG_IGNORE_PATH_EXACT = Object.freeze(['docs', 'doc'])
|
|
|
46
46
|
/** Таймаут на `npm view` / PyPI (мс) */
|
|
47
47
|
const REGISTRY_TIMEOUT_MS = 10_000
|
|
48
48
|
|
|
49
|
+
const LEADING_DOTSLASH_RE = /^\.\//
|
|
50
|
+
|
|
49
51
|
/**
|
|
50
52
|
* Тихо запускає `git` і повертає stdout або `null` при будь-якій помилці.
|
|
51
53
|
* @param {string[]} args аргументи `git`
|
|
52
|
-
* @returns {Promise<string | null>}
|
|
54
|
+
* @returns {Promise<string | null>} результат
|
|
53
55
|
*/
|
|
54
56
|
async function gitOrNull(args) {
|
|
55
57
|
try {
|
|
@@ -61,7 +63,7 @@ async function gitOrNull(args) {
|
|
|
61
63
|
}
|
|
62
64
|
|
|
63
65
|
/**
|
|
64
|
-
* @returns {Promise<boolean>}
|
|
66
|
+
* @returns {Promise<boolean>} результат
|
|
65
67
|
*/
|
|
66
68
|
async function isInsideGitRepo() {
|
|
67
69
|
const out = await gitOrNull(['rev-parse', '--is-inside-work-tree'])
|
|
@@ -69,7 +71,7 @@ async function isInsideGitRepo() {
|
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
/**
|
|
72
|
-
* @returns {Promise<string | null>}
|
|
74
|
+
* @returns {Promise<string | null>} результат
|
|
73
75
|
*/
|
|
74
76
|
async function currentBranchName() {
|
|
75
77
|
const out = await gitOrNull(['rev-parse', '--abbrev-ref', 'HEAD'])
|
|
@@ -77,27 +79,27 @@ async function currentBranchName() {
|
|
|
77
79
|
}
|
|
78
80
|
|
|
79
81
|
/**
|
|
80
|
-
* @param {string | null} branch
|
|
81
|
-
* @returns {boolean}
|
|
82
|
+
* @param {string | null} branch параметр
|
|
83
|
+
* @returns {boolean} результат
|
|
82
84
|
*/
|
|
83
85
|
function isIntegrationBranch(branch) {
|
|
84
86
|
return branch !== null && INTEGRATION_BRANCHES.includes(branch)
|
|
85
87
|
}
|
|
86
88
|
|
|
87
89
|
/**
|
|
88
|
-
* @param {string} ref
|
|
89
|
-
* @returns {string}
|
|
90
|
+
* @param {string} ref параметр
|
|
91
|
+
* @returns {string} результат
|
|
90
92
|
*/
|
|
91
93
|
function baseRefLabel(ref) {
|
|
92
94
|
return ref.startsWith('origin/') ? ref.slice('origin/'.length) : ref
|
|
93
95
|
}
|
|
94
96
|
|
|
95
97
|
/**
|
|
96
|
-
* @param {string} relPath
|
|
97
|
-
* @returns {boolean}
|
|
98
|
+
* @param {string} relPath параметр
|
|
99
|
+
* @returns {boolean} результат
|
|
98
100
|
*/
|
|
99
101
|
function isChangelogIgnoredPath(relPath) {
|
|
100
|
-
const p = relPath.
|
|
102
|
+
const p = relPath.replaceAll('\\', '/').replace(LEADING_DOTSLASH_RE, '')
|
|
101
103
|
if (CHANGELOG_IGNORE_PATH_EXACT.includes(p)) {
|
|
102
104
|
return true
|
|
103
105
|
}
|
|
@@ -105,8 +107,8 @@ function isChangelogIgnoredPath(relPath) {
|
|
|
105
107
|
}
|
|
106
108
|
|
|
107
109
|
/**
|
|
108
|
-
* @param {string} relPath
|
|
109
|
-
* @returns {Promise<boolean>}
|
|
110
|
+
* @param {string} relPath параметр
|
|
111
|
+
* @returns {Promise<boolean>} результат
|
|
110
112
|
*/
|
|
111
113
|
async function isPathGitIgnored(relPath) {
|
|
112
114
|
try {
|
|
@@ -118,7 +120,7 @@ async function isPathGitIgnored(relPath) {
|
|
|
118
120
|
}
|
|
119
121
|
|
|
120
122
|
/**
|
|
121
|
-
* @returns {Promise<string | null>}
|
|
123
|
+
* @returns {Promise<string | null>} результат
|
|
122
124
|
*/
|
|
123
125
|
async function resolveBaseRef() {
|
|
124
126
|
for (const name of BASE_BRANCH_CANDIDATES) {
|
|
@@ -133,8 +135,8 @@ async function resolveBaseRef() {
|
|
|
133
135
|
}
|
|
134
136
|
|
|
135
137
|
/**
|
|
136
|
-
* @param {string} baseRef
|
|
137
|
-
* @returns {Promise<string | null>}
|
|
138
|
+
* @param {string} baseRef параметр
|
|
139
|
+
* @returns {Promise<string | null>} результат
|
|
138
140
|
*/
|
|
139
141
|
async function resolveMergeBase(baseRef) {
|
|
140
142
|
const out = await gitOrNull(['merge-base', baseRef, 'HEAD'])
|
|
@@ -144,9 +146,9 @@ async function resolveMergeBase(baseRef) {
|
|
|
144
146
|
}
|
|
145
147
|
|
|
146
148
|
/**
|
|
147
|
-
* @param {string} ws
|
|
148
|
-
* @param {string[]} subWorkspaces
|
|
149
|
-
* @returns {string[]}
|
|
149
|
+
* @param {string} ws параметр
|
|
150
|
+
* @param {string[]} subWorkspaces параметр
|
|
151
|
+
* @returns {string[]} результат
|
|
150
152
|
*/
|
|
151
153
|
function pathspecForWorkspace(ws, subWorkspaces) {
|
|
152
154
|
if (ws !== '.') return [`${ws}/`]
|
|
@@ -154,12 +156,14 @@ function pathspecForWorkspace(ws, subWorkspaces) {
|
|
|
154
156
|
}
|
|
155
157
|
|
|
156
158
|
/**
|
|
157
|
-
* @param {string} baseRef
|
|
158
|
-
* @param {string[]} pathspec
|
|
159
|
-
* @returns {Promise<string[]>}
|
|
159
|
+
* @param {string} baseRef параметр
|
|
160
|
+
* @param {string[]} pathspec параметр
|
|
161
|
+
* @returns {Promise<string[]>} результат
|
|
160
162
|
*/
|
|
161
163
|
async function listChangedPathsAgainstBase(baseRef, pathspec) {
|
|
162
|
-
/**
|
|
164
|
+
/**
|
|
165
|
+
@type {string[]}
|
|
166
|
+
*/
|
|
163
167
|
const out = []
|
|
164
168
|
const diffArgs =
|
|
165
169
|
baseRef === 'HEAD'
|
|
@@ -177,10 +181,10 @@ async function listChangedPathsAgainstBase(baseRef, pathspec) {
|
|
|
177
181
|
}
|
|
178
182
|
|
|
179
183
|
/**
|
|
180
|
-
* @param {string} baseRef
|
|
181
|
-
* @param {string} ws
|
|
182
|
-
* @param {string[]} subWorkspaces
|
|
183
|
-
* @returns {Promise<boolean>}
|
|
184
|
+
* @param {string} baseRef параметр
|
|
185
|
+
* @param {string} ws параметр
|
|
186
|
+
* @param {string[]} subWorkspaces параметр
|
|
187
|
+
* @returns {Promise<boolean>} результат
|
|
184
188
|
*/
|
|
185
189
|
async function workspaceHasRelevantChangesAgainstBase(baseRef, ws, subWorkspaces) {
|
|
186
190
|
const pathspec = pathspecForWorkspace(ws, subWorkspaces)
|
|
@@ -199,9 +203,9 @@ async function workspaceHasRelevantChangesAgainstBase(baseRef, ws, subWorkspaces
|
|
|
199
203
|
|
|
200
204
|
/**
|
|
201
205
|
* Версія з маніфесту на `baseRef`.
|
|
202
|
-
* @param {string} baseRef
|
|
203
|
-
* @param {import('../../../../scripts/utils/package-manifest.mjs').PackageManifest} manifest
|
|
204
|
-
* @returns {Promise<string | null>}
|
|
206
|
+
* @param {string} baseRef параметр
|
|
207
|
+
* @param {import('../../../../scripts/utils/package-manifest.mjs').PackageManifest} manifest параметр
|
|
208
|
+
* @returns {Promise<string | null>} результат
|
|
205
209
|
*/
|
|
206
210
|
async function readBaseVersion(baseRef, manifest) {
|
|
207
211
|
const wsPath = manifest.ws === '.' ? manifest.manifestRel : `${manifest.ws}/${manifest.manifestRel}`
|
|
@@ -219,9 +223,9 @@ async function readBaseVersion(baseRef, manifest) {
|
|
|
219
223
|
}
|
|
220
224
|
|
|
221
225
|
/**
|
|
222
|
-
* @param {string} text
|
|
223
|
-
* @param {string} version
|
|
224
|
-
* @returns {boolean}
|
|
226
|
+
* @param {string} text параметр
|
|
227
|
+
* @param {string} version параметр
|
|
228
|
+
* @returns {boolean} результат
|
|
225
229
|
*/
|
|
226
230
|
function changelogHasVersionEntry(text, version) {
|
|
227
231
|
const needle = `## [${version}]`
|
|
@@ -229,8 +233,8 @@ function changelogHasVersionEntry(text, version) {
|
|
|
229
233
|
}
|
|
230
234
|
|
|
231
235
|
/**
|
|
232
|
-
* @param {string} name
|
|
233
|
-
* @returns {Promise<string | null>}
|
|
236
|
+
* @param {string} name параметр
|
|
237
|
+
* @returns {Promise<string | null>} результат
|
|
234
238
|
*/
|
|
235
239
|
async function defaultGetPublishedNpmVersion(name) {
|
|
236
240
|
try {
|
|
@@ -243,13 +247,13 @@ async function defaultGetPublishedNpmVersion(name) {
|
|
|
243
247
|
}
|
|
244
248
|
|
|
245
249
|
/**
|
|
246
|
-
* @param {string} name
|
|
247
|
-
* @returns {Promise<string | null>}
|
|
250
|
+
* @param {string} name параметр
|
|
251
|
+
* @returns {Promise<string | null>} результат
|
|
248
252
|
*/
|
|
249
253
|
async function defaultGetPublishedPyPiVersion(name) {
|
|
250
254
|
try {
|
|
251
255
|
const res = await fetch(`https://pypi.org/pypi/${encodeURIComponent(name)}/json`, {
|
|
252
|
-
signal: AbortSignal.timeout(REGISTRY_TIMEOUT_MS)
|
|
256
|
+
signal: AbortSignal.timeout(REGISTRY_TIMEOUT_MS)
|
|
253
257
|
})
|
|
254
258
|
if (!res.ok) return null
|
|
255
259
|
const data = await res.json()
|
|
@@ -261,31 +265,38 @@ async function defaultGetPublishedPyPiVersion(name) {
|
|
|
261
265
|
}
|
|
262
266
|
|
|
263
267
|
/**
|
|
264
|
-
* @param {import('../../../../scripts/utils/package-manifest.mjs').PackageManifest} manifest
|
|
265
|
-
* @param {(name: string, kind?: import('../../../../scripts/utils/package-manifest.mjs').PackageKind) => Promise<string | null>} getPublishedVersion
|
|
266
|
-
* @returns {Promise<string | null>}
|
|
268
|
+
* @param {import('../../../../scripts/utils/package-manifest.mjs').PackageManifest} manifest параметр
|
|
269
|
+
* @param {(name: string, kind?: import('../../../../scripts/utils/package-manifest.mjs').PackageKind) => Promise<string | null>} getPublishedVersion параметр
|
|
270
|
+
* @returns {Promise<string | null>} результат
|
|
267
271
|
*/
|
|
268
|
-
|
|
269
|
-
if (!manifest.name) return null
|
|
272
|
+
function resolvePublishedVersion(manifest, getPublishedVersion) {
|
|
273
|
+
if (!manifest.name) return Promise.resolve(null)
|
|
270
274
|
return getPublishedVersion(manifest.name, manifest.kind)
|
|
271
275
|
}
|
|
272
276
|
|
|
273
277
|
/**
|
|
274
|
-
* @
|
|
278
|
+
* @param {string} name пакет
|
|
279
|
+
* @param {import('../../../../scripts/utils/package-manifest.mjs').PackageKind} [kind] тип пакета
|
|
280
|
+
* @returns {Promise<string | null>} опублікована версія або null
|
|
275
281
|
*/
|
|
276
|
-
function
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
return defaultGetPublishedPyPiVersion(name)
|
|
280
|
-
}
|
|
281
|
-
return defaultGetPublishedNpmVersion(name)
|
|
282
|
+
function defaultGetPublishedVersion(name, kind = 'npm') {
|
|
283
|
+
if (kind === 'python') {
|
|
284
|
+
return defaultGetPublishedPyPiVersion(name)
|
|
282
285
|
}
|
|
286
|
+
return defaultGetPublishedNpmVersion(name)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* @returns {(name: string, kind?: import('../../../../scripts/utils/package-manifest.mjs').PackageKind) => Promise<string | null>} стандартний резолвер
|
|
291
|
+
*/
|
|
292
|
+
function createDefaultGetPublishedVersion() {
|
|
293
|
+
return defaultGetPublishedVersion
|
|
283
294
|
}
|
|
284
295
|
|
|
285
296
|
/**
|
|
286
|
-
* @param {import('../../../../scripts/utils/package-manifest.mjs').PackageManifest} manifest
|
|
287
|
-
* @param {(msg: string) => void} pass
|
|
288
|
-
* @param {(msg: string) => void} fail
|
|
297
|
+
* @param {import('../../../../scripts/utils/package-manifest.mjs').PackageManifest} manifest параметр
|
|
298
|
+
* @param {(msg: string) => void} pass параметр
|
|
299
|
+
* @param {(msg: string) => void} fail параметр
|
|
289
300
|
*/
|
|
290
301
|
function checkNpmFilesArrayContainsChangelog(manifest, pass, fail) {
|
|
291
302
|
if (manifest.kind !== 'npm' || !manifest.npmFiles) return
|
|
@@ -298,11 +309,11 @@ function checkNpmFilesArrayContainsChangelog(manifest, pass, fail) {
|
|
|
298
309
|
}
|
|
299
310
|
|
|
300
311
|
/**
|
|
301
|
-
* @param {string} ws
|
|
302
|
-
* @param {string} version
|
|
303
|
-
* @param {(msg: string) => void} pass
|
|
304
|
-
* @param {(msg: string) => void} fail
|
|
305
|
-
* @returns {Promise<boolean>}
|
|
312
|
+
* @param {string} ws параметр
|
|
313
|
+
* @param {string} version параметр
|
|
314
|
+
* @param {(msg: string) => void} pass параметр
|
|
315
|
+
* @param {(msg: string) => void} fail параметр
|
|
316
|
+
* @returns {Promise<boolean>} результат
|
|
306
317
|
*/
|
|
307
318
|
async function verifyChangelogEntry(ws, version, pass, fail) {
|
|
308
319
|
const label = ws === '.' ? '<root>' : ws
|
|
@@ -321,20 +332,20 @@ async function verifyChangelogEntry(ws, version, pass, fail) {
|
|
|
321
332
|
}
|
|
322
333
|
|
|
323
334
|
/**
|
|
324
|
-
* @param {import('../../../../scripts/utils/package-manifest.mjs').PackageManifest} manifest
|
|
325
|
-
* @returns {string}
|
|
335
|
+
* @param {import('../../../../scripts/utils/package-manifest.mjs').PackageManifest} manifest параметр
|
|
336
|
+
* @returns {string} результат
|
|
326
337
|
*/
|
|
327
338
|
function workspaceLabel(manifest) {
|
|
328
339
|
return manifest.ws === '.' ? '<root>' : manifest.ws
|
|
329
340
|
}
|
|
330
341
|
|
|
331
342
|
/**
|
|
332
|
-
* @param {import('../../../../scripts/utils/package-manifest.mjs').PackageManifest} manifest
|
|
333
|
-
* @param {string} Vcurrent
|
|
334
|
-
* @param {string[]} subWorkspaces
|
|
335
|
-
* @param {(msg: string) => void} pass
|
|
336
|
-
* @param {(msg: string) => void} fail
|
|
337
|
-
* @returns {Promise<void>}
|
|
343
|
+
* @param {import('../../../../scripts/utils/package-manifest.mjs').PackageManifest} manifest параметр
|
|
344
|
+
* @param {string} Vcurrent параметр
|
|
345
|
+
* @param {string[]} subWorkspaces параметр
|
|
346
|
+
* @param {(msg: string) => void} pass параметр
|
|
347
|
+
* @param {(msg: string) => void} fail параметр
|
|
348
|
+
* @returns {Promise<void>} результат
|
|
338
349
|
*/
|
|
339
350
|
async function checkPublishedWorkspacePendingGitChanges(manifest, Vcurrent, subWorkspaces, pass, fail) {
|
|
340
351
|
const label = workspaceLabel(manifest)
|
|
@@ -379,12 +390,12 @@ async function checkPublishedWorkspacePendingGitChanges(manifest, Vcurrent, subW
|
|
|
379
390
|
}
|
|
380
391
|
|
|
381
392
|
/**
|
|
382
|
-
* @param {import('../../../../scripts/utils/package-manifest.mjs').PackageManifest} manifest
|
|
383
|
-
* @param {string[]} subWorkspaces
|
|
384
|
-
* @param {(name: string, kind?: import('../../../../scripts/utils/package-manifest.mjs').PackageKind) => Promise<string | null>} getPublishedVersion
|
|
385
|
-
* @param {(msg: string) => void} pass
|
|
386
|
-
* @param {(msg: string) => void} fail
|
|
387
|
-
* @returns {Promise<void>}
|
|
393
|
+
* @param {import('../../../../scripts/utils/package-manifest.mjs').PackageManifest} manifest параметр
|
|
394
|
+
* @param {string[]} subWorkspaces параметр
|
|
395
|
+
* @param {(name: string, kind?: import('../../../../scripts/utils/package-manifest.mjs').PackageKind) => Promise<string | null>} getPublishedVersion параметр
|
|
396
|
+
* @param {(msg: string) => void} pass параметр
|
|
397
|
+
* @param {(msg: string) => void} fail параметр
|
|
398
|
+
* @returns {Promise<void>} результат
|
|
388
399
|
*/
|
|
389
400
|
async function checkPublishedWorkspace(manifest, subWorkspaces, getPublishedVersion, pass, fail) {
|
|
390
401
|
const label = workspaceLabel(manifest)
|
|
@@ -415,11 +426,11 @@ async function checkPublishedWorkspace(manifest, subWorkspaces, getPublishedVers
|
|
|
415
426
|
}
|
|
416
427
|
|
|
417
428
|
/**
|
|
418
|
-
* @param {string} mergeBase
|
|
419
|
-
* @param {import('../../../../scripts/utils/package-manifest.mjs').PackageManifest} manifest
|
|
420
|
-
* @param {string} baseLabel
|
|
421
|
-
* @param {(msg: string) => void} pass
|
|
422
|
-
* @param {(msg: string) => void} fail
|
|
429
|
+
* @param {string} mergeBase параметр
|
|
430
|
+
* @param {import('../../../../scripts/utils/package-manifest.mjs').PackageManifest} manifest параметр
|
|
431
|
+
* @param {string} baseLabel параметр
|
|
432
|
+
* @param {(msg: string) => void} pass параметр
|
|
433
|
+
* @param {(msg: string) => void} fail параметр
|
|
423
434
|
*/
|
|
424
435
|
async function checkLocalOnlyChangedWorkspace(mergeBase, manifest, baseLabel, pass, fail) {
|
|
425
436
|
const label = workspaceLabel(manifest)
|
|
@@ -442,10 +453,10 @@ async function checkLocalOnlyChangedWorkspace(mergeBase, manifest, baseLabel, pa
|
|
|
442
453
|
}
|
|
443
454
|
|
|
444
455
|
/**
|
|
445
|
-
* @param {import('../../../../scripts/utils/package-manifest.mjs').PackageManifest[]} localOnly
|
|
446
|
-
* @param {string[]} subWorkspaces
|
|
447
|
-
* @param {(msg: string) => void} pass
|
|
448
|
-
* @param {(msg: string) => void} fail
|
|
456
|
+
* @param {import('../../../../scripts/utils/package-manifest.mjs').PackageManifest[]} localOnly параметр
|
|
457
|
+
* @param {string[]} subWorkspaces параметр
|
|
458
|
+
* @param {(msg: string) => void} pass параметр
|
|
459
|
+
* @param {(msg: string) => void} fail параметр
|
|
449
460
|
*/
|
|
450
461
|
async function runLocalOnlyChecks(localOnly, subWorkspaces, pass, fail) {
|
|
451
462
|
if (localOnly.length === 0) return
|
|
@@ -483,9 +494,9 @@ async function runLocalOnlyChecks(localOnly, subWorkspaces, pass, fail) {
|
|
|
483
494
|
}
|
|
484
495
|
|
|
485
496
|
/**
|
|
486
|
-
* @param {object} [opts]
|
|
497
|
+
* @param {object} [opts] опції перевірки
|
|
487
498
|
* @param {(name: string, kind?: import('../../../../scripts/utils/package-manifest.mjs').PackageKind) => Promise<string | null>} [opts.getPublishedVersion] перевизначення npm/PyPI у тестах
|
|
488
|
-
* @returns {Promise<number>}
|
|
499
|
+
* @returns {Promise<number>} exit-код перевірки
|
|
489
500
|
*/
|
|
490
501
|
export async function check(opts = {}) {
|
|
491
502
|
const reporter = createCheckReporter()
|
|
@@ -495,9 +506,13 @@ export async function check(opts = {}) {
|
|
|
495
506
|
const workspaces = await getMonorepoProjectRootDirs(process.cwd())
|
|
496
507
|
const subWorkspaces = workspaces.filter(w => w !== '.')
|
|
497
508
|
|
|
498
|
-
/**
|
|
509
|
+
/**
|
|
510
|
+
@type {import('../../../../scripts/utils/package-manifest.mjs').PackageManifest[]}
|
|
511
|
+
*/
|
|
499
512
|
const published = []
|
|
500
|
-
/**
|
|
513
|
+
/**
|
|
514
|
+
@type {import('../../../../scripts/utils/package-manifest.mjs').PackageManifest[]}
|
|
515
|
+
*/
|
|
501
516
|
const localOnly = []
|
|
502
517
|
|
|
503
518
|
for (const ws of workspaces) {
|
package/rules/ci4/ci4.mdc
CHANGED
|
@@ -95,7 +95,7 @@ docs/
|
|
|
95
95
|
- **Date** — рядок `**Date:** YYYY-MM-DD`, для впорядкування і відображення у проекції.
|
|
96
96
|
- **Heading H1** — перший `#`-заголовок, як назва рішення.
|
|
97
97
|
- **Розділи MADR** — `## Context and Problem Statement`, `## Considered Options`, `## Decision Outcome`, `### Consequences`, `## More Information`. Для проекції зазвичай беруться Decision Outcome + Consequences.
|
|
98
|
-
- **`## Update YYYY-MM-DD`** —
|
|
98
|
+
- **`## Update YYYY-MM-DD`** — appended-секції у тому ж файлі. Враховуються як еволюція рішення в хронологічному порядку.
|
|
99
99
|
|
|
100
100
|
**Маршрутизація ADR → зони** робиться двома способами:
|
|
101
101
|
|
|
@@ -278,13 +278,13 @@ User Service автентифікує користувачів і керує п
|
|
|
278
278
|
|
|
279
279
|
## Типові поломки LLM і захист
|
|
280
280
|
|
|
281
|
-
| Проблема | Захист
|
|
282
|
-
| ---------------------------- |
|
|
281
|
+
| Проблема | Захист |
|
|
282
|
+
| ---------------------------- | ----------------------------------------------------------------------------------------- |
|
|
283
283
|
| Галюцинація деталей | Правило `[<slug>]` маркера + post-gen linter, що перевіряє існування `docs/adr/<slug>.md` |
|
|
284
|
-
| Втрата `## Update`-еволюції | Pre-process: збирати всі `## Update YYYY-MM-DD` у ADR, передавати у промпт як патчі
|
|
285
|
-
| Дрейф термінології | `docs/glossary.md` як перший вхід у кожен промпт
|
|
286
|
-
| Перенасичення контексту | Двостадійна генерація: спочатку summary кожного ADR (кеш), потім проекція
|
|
287
|
-
| Inconsistency між проекціями | Cross-projection validator порівнює факти між документами
|
|
284
|
+
| Втрата `## Update`-еволюції | Pre-process: збирати всі `## Update YYYY-MM-DD` у ADR, передавати у промпт як патчі |
|
|
285
|
+
| Дрейф термінології | `docs/glossary.md` як перший вхід у кожен промпт |
|
|
286
|
+
| Перенасичення контексту | Двостадійна генерація: спочатку summary кожного ADR (кеш), потім проекція |
|
|
287
|
+
| Inconsistency між проекціями | Cross-projection validator порівнює факти між документами |
|
|
288
288
|
|
|
289
289
|
## Валідатор (обов'язково)
|
|
290
290
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: AVIF-двійники для raster-зображень з ув'язуванням у .vue/.html
|
|
3
|
-
version: '1.
|
|
3
|
+
version: '1.3'
|
|
4
4
|
globs: "**/*.{png,jpg,jpeg,gif,avif,vue,html}"
|
|
5
5
|
alwaysApply: false
|
|
6
6
|
---
|
|
@@ -31,7 +31,7 @@ AVIF-двійники **зберігаємо в git** — це готові ар
|
|
|
31
31
|
|
|
32
32
|
## Опт-аут для конкретного пакета
|
|
33
33
|
|
|
34
|
-
У workspace-пакеті, де AVIF-імпорти небажані (наприклад, мобільний бандл або публічний сайт без гарантованої AVIF-підтримки), додай у `package.json` цього
|
|
34
|
+
У workspace-пакеті, де AVIF-імпорти небажані (наприклад, мобільний бандл або публічний сайт без гарантованої AVIF-підтримки), додай у `package.json` цього пакета. Заборонений typo `disabled-avif` (канон — `disable-avif`): [package.json.deny.json](./policy/package_json/template/package.json.deny.json).
|
|
35
35
|
|
|
36
36
|
```json title="apps/site/package.json"
|
|
37
37
|
{
|
|
@@ -28,6 +28,9 @@
|
|
|
28
28
|
* (див. `utils/promise-settimeout-scan.mjs`);
|
|
29
29
|
* - «jsconfig.json»: у backend-пакеті з каталогом `src/` у корені має бути `jsconfig.json`,
|
|
30
30
|
* вміст якого збігається з каноном js-run.mdc (NodeNext і include на дерево `src`).
|
|
31
|
+
*
|
|
32
|
+
* Per-document валідація `package.json` (bunyan, `node` у `scripts`) делегована rego-пакету
|
|
33
|
+
* `js_run.package_json` у `npm/rules/js-run/policy/package_json/`; JS — cross-file (AST, FS).
|
|
31
34
|
*/
|
|
32
35
|
import { existsSync, statSync } from 'node:fs'
|
|
33
36
|
import { readFile } from 'node:fs/promises'
|
package/rules/js-run/js-run.mdc
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
description: Це правила для backend проектів на JavaScript/Node.js, сюди входять і job і WEB сервери.
|
|
3
3
|
globs: "**/package.json,**/jsconfig.json,**/src/**/*.{js,mjs,cjs,ts,tsx}"
|
|
4
4
|
alwaysApply: false
|
|
5
|
-
version: '1.
|
|
5
|
+
version: '1.11'
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
## Область застосування
|
|
@@ -17,6 +17,21 @@ version: '1.9'
|
|
|
17
17
|
|
|
18
18
|
Тому **у frontend-пакетах не торкайся `process.env.*`** і **не додавай** `import { env } from 'node:process'`. Якщо натрапив на `process.env.NODE_ENV` у frontend-коді — заміна, якщо взагалі потрібна, лише на `import.meta.env.MODE`.
|
|
19
19
|
|
|
20
|
+
## Runtime у `package.json#scripts`
|
|
21
|
+
|
|
22
|
+
У **backend**-пакетах (без `vite` у `devDependencies`) код запускають через **Bun**, не через бінарник **`node`** у значеннях `scripts`:
|
|
23
|
+
|
|
24
|
+
- `"start": "node src/index.js"` → `"start": "bun src/index.js"` (або `bun run …`, якщо так прийнято в репо);
|
|
25
|
+
- `node --watch app.js` → `bun --watch app.js`;
|
|
26
|
+
- `NODE_OPTIONS=… node app.js` → `NODE_OPTIONS=… bun app.js`.
|
|
27
|
+
- `env $(cat .env .env.local) bun src/index.js` → `bun --env-file=.env --env-file=.env.local src/index.js` (нативне завантаження env у Bun, без `env`/`cat`).
|
|
28
|
+
|
|
29
|
+
Заборонено викликати **`node`** у ланцюжках (`&&`, `;`, `|`). Заборонено обгортку **`env $(cat …) bun`** — файли з `cat` перелічуй у **`--env-file=`** (по одному прапорцю на файл, порядок як у `cat`). Допустимо: `bun`, `bunx`, `npx` (див. **bun.mdc**), інші CLI, якщо вони не підміняють рантайм на `node`.
|
|
30
|
+
|
|
31
|
+
Це **не** стосується поля `engines.node` (мінімальна версія Node для сумісності інструментів) і **не** стосується frontend-пакетів з `vite` у `devDependencies`.
|
|
32
|
+
|
|
33
|
+
Канон заборонених патернів у `scripts`: [package.json.deny.json](./policy/package_json/template/package.json.deny.json) (`scriptsForbidden`).
|
|
34
|
+
|
|
20
35
|
## Структура проекту
|
|
21
36
|
|
|
22
37
|
Рекомендується використовувати таку структуру проекту:
|
|
@@ -18,3 +18,20 @@ deny contains msg if {
|
|
|
18
18
|
pkg in object.keys(object.get(input, "devDependencies", {}))
|
|
19
19
|
msg := sprintf("devDependencies.%s — %s", [pkg, reason])
|
|
20
20
|
}
|
|
21
|
+
|
|
22
|
+
# ── deny: `node` як рантайм у `scripts` (backend-пакети без vite) ─────────
|
|
23
|
+
|
|
24
|
+
deny contains msg if {
|
|
25
|
+
js_run_backend_package
|
|
26
|
+
is_object(input.scripts)
|
|
27
|
+
some script_name, script_value in input.scripts
|
|
28
|
+
is_string(script_value)
|
|
29
|
+
some rule in object.get(data.template.deny, "scriptsForbidden", [])
|
|
30
|
+
regex.match(rule.pattern, script_value)
|
|
31
|
+
msg := sprintf("package.json: scripts.%s — %s", [script_name, rule.message])
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# Frontend-пакети (`vite` у devDependencies) — поза js-run (див. js-run.mdc).
|
|
35
|
+
js_run_backend_package if {
|
|
36
|
+
not "vite" in object.keys(object.get(input, "devDependencies", {}))
|
|
37
|
+
}
|
|
@@ -6,5 +6,17 @@
|
|
|
6
6
|
"devDependencies": {
|
|
7
7
|
"bunyan": "використовуй стандартні логери (js-run.mdc)",
|
|
8
8
|
"@nitra/bunyan": "використовуй стандартні логери (js-run.mdc)"
|
|
9
|
-
}
|
|
9
|
+
},
|
|
10
|
+
"scriptsForbidden": [
|
|
11
|
+
{
|
|
12
|
+
"id": "node-runner",
|
|
13
|
+
"pattern": "\\bnode(\\s|$)",
|
|
14
|
+
"message": "заміни `node` на `bun` у scripts — один runtime у dev і prod (js-run.mdc)"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"id": "env-cat-bun",
|
|
18
|
+
"pattern": "\\benv\\s+\\$\\(cat\\s+[^)]+\\)\\s+bun\\b",
|
|
19
|
+
"message": "заміни `env $(cat A B) bun` на `bun --env-file=A --env-file=B` (нативний Bun, js-run.mdc)"
|
|
20
|
+
}
|
|
21
|
+
]
|
|
10
22
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"name": "hasura-jwt-public-config",
|
|
4
|
+
"policyType": "postureExceptionPolicy",
|
|
5
|
+
"actions": ["alertOnly"],
|
|
6
|
+
"resources": [
|
|
7
|
+
{
|
|
8
|
+
"designatorType": "Attributes",
|
|
9
|
+
"attributes": {
|
|
10
|
+
"kind": "ConfigMap",
|
|
11
|
+
"name": "hasura-config"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"posturePolicies": [
|
|
16
|
+
{
|
|
17
|
+
"controlID": "C-0012"
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
]
|