@nitra/cursor 1.8.206 → 1.8.207

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 (55) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/package.json +1 -1
  3. package/policy/abie/health_check_policy/health_check_policy.rego +73 -0
  4. package/policy/abie/http_route_base/http_route_base.rego +45 -0
  5. package/policy/adr/settings_json/settings_json.rego +31 -0
  6. package/policy/adr/settings_local_json/settings_local_json.rego +28 -0
  7. package/policy/bun/bunfig/bunfig.rego +33 -0
  8. package/policy/bun/package_json/package_json.rego +94 -0
  9. package/policy/capacitor/package_json/package_json.rego +45 -0
  10. package/policy/ga/clean_ga_workflows/clean_ga_workflows.rego +0 -26
  11. package/policy/ga/clean_merged_branch/clean_merged_branch.rego +0 -25
  12. package/policy/ga/git_ai/git_ai.rego +0 -26
  13. package/policy/ga/lint_ga/lint_ga.rego +0 -26
  14. package/policy/ga/workflow_common/workflow_common.rego +161 -0
  15. package/policy/graphql/package_json/package_json.rego +35 -0
  16. package/policy/hasura/svc_hl/svc_hl.rego +27 -0
  17. package/policy/image_compress/package_json/package_json.rego +94 -0
  18. package/policy/js_bun_db/package_json/package_json.rego +28 -0
  19. package/policy/js_lint/lint_js_yml/lint_js_yml.rego +98 -0
  20. package/policy/js_lint/package_json/package_json.rego +137 -0
  21. package/policy/js_mssql/package_json/package_json.rego +57 -0
  22. package/policy/js_run/configmap/configmap.rego +45 -0
  23. package/policy/js_run/jsconfig/jsconfig.rego +66 -0
  24. package/policy/js_run/package_json/package_json.rego +31 -0
  25. package/policy/k8s/manifest/manifest.rego +130 -0
  26. package/policy/npm_module/emit_types_config/emit_types_config.rego +37 -0
  27. package/policy/npm_module/npm_package_json/npm_package_json.rego +55 -0
  28. package/policy/npm_module/npm_publish_yml/npm_publish_yml.rego +79 -0
  29. package/policy/npm_module/root_package_json/root_package_json.rego +28 -0
  30. package/policy/php/lint_php_yml/lint_php_yml.rego +32 -0
  31. package/policy/php/package_json/package_json.rego +19 -0
  32. package/policy/style_lint/lint_style_yml/lint_style_yml.rego +35 -0
  33. package/policy/style_lint/package_json/package_json.rego +49 -0
  34. package/policy/text/cspell/cspell.rego +91 -0
  35. package/policy/text/markdownlint/markdownlint.rego +21 -0
  36. package/policy/text/oxfmtrc/oxfmtrc.rego +90 -0
  37. package/policy/text/package_json/package_json.rego +88 -0
  38. package/policy/vue/package_json/package_json.rego +54 -0
  39. package/scripts/check-adr.mjs +3 -2
  40. package/scripts/check-bun.mjs +21 -117
  41. package/scripts/check-graphql.mjs +6 -45
  42. package/scripts/check-hasura.mjs +2 -3
  43. package/scripts/check-image-avif.mjs +3 -3
  44. package/scripts/check-image-compress.mjs +25 -132
  45. package/scripts/check-js-bun-db.mjs +3 -50
  46. package/scripts/check-js-run.mjs +8 -8
  47. package/scripts/check-k8s.mjs +4 -4
  48. package/scripts/check-npm-module.mjs +17 -8
  49. package/scripts/check-php.mjs +16 -51
  50. package/scripts/check-style-lint.mjs +28 -52
  51. package/scripts/check-text.mjs +47 -219
  52. package/scripts/check-vue.mjs +3 -16
  53. package/scripts/lint-conftest.mjs +351 -0
  54. package/scripts/lint-ga.mjs +39 -2
  55. package/scripts/run-shellcheck-text.mjs +2 -2
@@ -0,0 +1,45 @@
1
+ # Порт перевірки `k8s/base/configmap.yaml` з `npm/scripts/check-js-run.mjs`
2
+ # (js-run.mdc) — `OTEL_RESOURCE_ATTRIBUTES` має містити `service.name=` і
3
+ # `service.namespace=`.
4
+ #
5
+ # Запуск (локально):
6
+ # conftest test path/to/k8s/base/configmap.yaml -p npm/policy/js_run \
7
+ # --namespace js_run.configmap
8
+ #
9
+ # Відповідність імені ConfigMap імені Deployment (cross-file) — у JS і `check-k8s.mjs`.
10
+ #
11
+ # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
12
+ # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
13
+ # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
14
+ package js_run.configmap
15
+
16
+ import rego.v1
17
+
18
+ # Шаблони повідомлень — через `concat` для regal style/line-length.
19
+ otel_service_name_template := concat(" ", [
20
+ "ConfigMap %q: OTEL_RESOURCE_ATTRIBUTES має містити",
21
+ "`service.name=` (js-run.mdc)",
22
+ ])
23
+
24
+ otel_service_namespace_template := concat(" ", [
25
+ "ConfigMap %q: OTEL_RESOURCE_ATTRIBUTES має містити",
26
+ "`service.namespace=` (js-run.mdc)",
27
+ ])
28
+
29
+ deny contains msg if {
30
+ input.kind == "ConfigMap"
31
+ otel := object.get(object.get(input, "data", {}), "OTEL_RESOURCE_ATTRIBUTES", "")
32
+ otel != ""
33
+ not contains(otel, "service.name=")
34
+ msg := sprintf(otel_service_name_template, [cm_name])
35
+ }
36
+
37
+ deny contains msg if {
38
+ input.kind == "ConfigMap"
39
+ otel := object.get(object.get(input, "data", {}), "OTEL_RESOURCE_ATTRIBUTES", "")
40
+ otel != ""
41
+ not contains(otel, "service.namespace=")
42
+ msg := sprintf(otel_service_namespace_template, [cm_name])
43
+ }
44
+
45
+ cm_name := object.get(object.get(input, "metadata", {}), "name", "?")
@@ -0,0 +1,66 @@
1
+ # Порт перевірки `jsconfig.json` з `npm/scripts/check-js-run.mjs` (js-run.mdc).
2
+ #
3
+ # Запуск (локально, у backend-пакеті з каталогом `src/`):
4
+ # conftest test path/to/jsconfig.json -p npm/policy/js_run \
5
+ # --namespace js_run.jsconfig
6
+ #
7
+ # Перевіряє: `compilerOptions.{lib, module, moduleResolution, target, checkJs}` і
8
+ # `include` мають канонічні значення (js-run.mdc).
9
+ #
10
+ # FS-перевірка (наявність каталогу `src/` у пакеті, наявність самого `jsconfig.json`)
11
+ # і вибір файлу-кандидата — у JS.
12
+ #
13
+ # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
14
+ # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
15
+ # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
16
+ package js_run.jsconfig
17
+
18
+ import rego.v1
19
+
20
+ # ── deny: compilerOptions ──────────────────────────────────────────────────
21
+
22
+ deny contains msg if {
23
+ co := object.get(input, "compilerOptions", {})
24
+ not is_array(object.get(co, "lib", null))
25
+ msg := "jsconfig.json: compilerOptions.lib має бути [\"esnext\"] (js-run.mdc)"
26
+ }
27
+
28
+ deny contains msg if {
29
+ co := object.get(input, "compilerOptions", {})
30
+ is_array(co.lib)
31
+ {l | some l in co.lib} != {"esnext"}
32
+ msg := "jsconfig.json: compilerOptions.lib має бути [\"esnext\"] (js-run.mdc)"
33
+ }
34
+
35
+ deny contains msg if {
36
+ object.get(object.get(input, "compilerOptions", {}), "module", null) != "NodeNext"
37
+ msg := "jsconfig.json: compilerOptions.module має бути \"NodeNext\" (js-run.mdc)"
38
+ }
39
+
40
+ deny contains msg if {
41
+ object.get(object.get(input, "compilerOptions", {}), "moduleResolution", null) != "NodeNext"
42
+ msg := "jsconfig.json: compilerOptions.moduleResolution має бути \"NodeNext\" (js-run.mdc)"
43
+ }
44
+
45
+ deny contains msg if {
46
+ object.get(object.get(input, "compilerOptions", {}), "target", null) != "esnext"
47
+ msg := "jsconfig.json: compilerOptions.target має бути \"esnext\" (js-run.mdc)"
48
+ }
49
+
50
+ deny contains msg if {
51
+ object.get(object.get(input, "compilerOptions", {}), "checkJs", null) != false
52
+ msg := "jsconfig.json: compilerOptions.checkJs має бути false (js-run.mdc)"
53
+ }
54
+
55
+ # ── deny: include ──────────────────────────────────────────────────────────
56
+
57
+ deny contains msg if {
58
+ not is_array(object.get(input, "include", null))
59
+ msg := "jsconfig.json: include має бути [\"src/**/*\"] (js-run.mdc)"
60
+ }
61
+
62
+ deny contains msg if {
63
+ is_array(input.include)
64
+ {p | some p in input.include} != {"src/**/*"}
65
+ msg := "jsconfig.json: include має бути [\"src/**/*\"] (js-run.mdc)"
66
+ }
@@ -0,0 +1,31 @@
1
+ # Порт перевірки залежностей `package.json` з `npm/scripts/check-js-run.mjs`
2
+ # (js-run.mdc) — заборона `bunyan` / `@nitra/bunyan`.
3
+ #
4
+ # Запуск (локально, для будь-якого `package.json` у дереві):
5
+ # conftest test path/to/package.json -p npm/policy/js_run \
6
+ # --namespace js_run.package_json
7
+ #
8
+ # AST-скан коду на імпорти `bunyan` / `process.env` без `checkEnv`,
9
+ # `new Promise(resolve => setTimeout(resolve, ...))`, обмеження `#conn/*`-аліасів —
10
+ # у JS (потребує парсингу `.js` / `.ts` через oxc-parser).
11
+ #
12
+ # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
13
+ # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
14
+ # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
15
+ package js_run.package_json
16
+
17
+ import rego.v1
18
+
19
+ forbidden_packages := {"bunyan", "@nitra/bunyan"}
20
+
21
+ deny contains msg if {
22
+ some pkg_name in forbidden_packages
23
+ pkg_name in object.keys(object.get(input, "dependencies", {}))
24
+ msg := sprintf("dependencies містить %q — використовуй стандартні логери (js-run.mdc)", [pkg_name])
25
+ }
26
+
27
+ deny contains msg if {
28
+ some pkg_name in forbidden_packages
29
+ pkg_name in object.keys(object.get(input, "devDependencies", {}))
30
+ msg := sprintf("devDependencies містить %q — використовуй стандартні логери (js-run.mdc)", [pkg_name])
31
+ }
@@ -0,0 +1,130 @@
1
+ # Порт пер-документних структурних перевірок з `npm/scripts/check-k8s.mjs`
2
+ # (k8s.mdc). Цей пакет описує лише ті правила, що дивляться на ОДИН манифест
3
+ # (один YAML-документ): conftest за замовчуванням розрізає файли по `---` і
4
+ # запускає policy на кожен документ окремо.
5
+ #
6
+ # Запуск (локально, по одному файлу або по дереву):
7
+ # conftest test path/to/k8s/manifest.yaml -p npm/policy/k8s \
8
+ # --namespace k8s.manifest
9
+ #
10
+ # Перевіряє:
11
+ # - `kind: Ingress` заборонено (потрібен перехід на Gateway API);
12
+ # - `apiVersion: autoscaling/v1` заборонено (HPA → autoscaling/v2);
13
+ # - `kind: Service` без `cloud.google.com/neg` /
14
+ # `cloud.google.com/backend-config` в `metadata.annotations` (k8s.mdc);
15
+ # - `kind: Deployment` — у кожного контейнера спільно `containers` +
16
+ # `initContainers` має бути `resources.requests.cpu` (рядок на кшталт
17
+ # `"500m"` чи число), без порожнього значення.
18
+ #
19
+ # CROSS-FILE логіка (Kustomize-резолюція ресурсів, парність svc.yaml/svc-hl.yaml,
20
+ # HPA/PDB/topologySpreadConstraints за каталогом, BackendConfig-сепарація,
21
+ # yaml-language-server schema modeline, namespace-перевірки за деревом
22
+ # `…/k8s/base/`) лишається у `check-k8s.mjs`: вона потребує файлової системи.
23
+ #
24
+ # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
25
+ # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
26
+ # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
27
+ package k8s.manifest
28
+
29
+ import rego.v1
30
+
31
+ default_cpu_request := "0.5"
32
+
33
+ forbidden_service_annotations := {
34
+ "cloud.google.com/neg",
35
+ "cloud.google.com/backend-config",
36
+ }
37
+
38
+ ingress_template := concat(" ", [
39
+ "знайдено kind: Ingress — заміни на Gateway API:",
40
+ "HTTPRoute (hr.yaml), HealthCheckPolicy (hc.yaml) (k8s.mdc)",
41
+ ])
42
+
43
+ autoscaling_v1_template := concat(" ", [
44
+ "знайдено apiVersion: autoscaling/v1 (kind: %s) —",
45
+ "мігруй на autoscaling/v2 (k8s.mdc)",
46
+ ])
47
+
48
+ cpu_missing_template := concat(" ", [
49
+ "Deployment %q, контейнер %q: відсутнє resources.requests.cpu —",
50
+ "додай (за замовчуванням %s) (k8s.mdc)",
51
+ ])
52
+
53
+ cpu_empty_template := concat(" ", [
54
+ "Deployment %q, контейнер %q: resources.requests.cpu має бути непорожнім",
55
+ "значенням (наприклад \"500m\") (зараз: %v) (k8s.mdc)",
56
+ ])
57
+
58
+ # ── deny: заборонені kind/apiVersion ──────────────────────────────────────
59
+
60
+ deny contains ingress_template if {
61
+ input.kind == "Ingress"
62
+ }
63
+
64
+ deny contains msg if {
65
+ input.apiVersion == "autoscaling/v1"
66
+ msg := sprintf(autoscaling_v1_template, [object.get(input, "kind", "<no-kind>")])
67
+ }
68
+
69
+ # ── deny: заборонені анотації Service ─────────────────────────────────────
70
+
71
+ deny contains msg if {
72
+ input.kind == "Service"
73
+ annotations := object.get(object.get(input, "metadata", {}), "annotations", {})
74
+ some forbidden_key in forbidden_service_annotations
75
+ forbidden_key in object.keys(annotations)
76
+ msg := sprintf("Service %q: видали анотацію %q (k8s.mdc)", [input.metadata.name, forbidden_key])
77
+ }
78
+
79
+ # ── deny: Deployment — у кожного контейнера resources.requests.cpu ────────
80
+ #
81
+ # Дві гілки: відсутнє/null поле cpu (повідомлення про додавання) і явно
82
+ # присутнє, але порожнє/невалідне значення (повідомлення з підставленим value).
83
+
84
+ deny contains msg if {
85
+ input.kind == "Deployment"
86
+ some container in deployment_all_containers
87
+ not has_non_empty_cpu_request(container)
88
+ not has_cpu_field(container)
89
+ msg := sprintf(cpu_missing_template, [deployment_name, container.name, default_cpu_request])
90
+ }
91
+
92
+ deny contains msg if {
93
+ input.kind == "Deployment"
94
+ some container in deployment_all_containers
95
+ not has_non_empty_cpu_request(container)
96
+ has_cpu_field(container)
97
+ cpu := container.resources.requests.cpu
98
+ msg := sprintf(cpu_empty_template, [deployment_name, container.name, cpu])
99
+ }
100
+
101
+ # ── helpers ────────────────────────────────────────────────────────────────
102
+
103
+ deployment_name := object.get(object.get(input, "metadata", {}), "name", "<no-name>")
104
+
105
+ # Усі контейнери (звичайні + ініт) Deployment-а — для перевірки CPU.
106
+ deployment_all_containers contains container if {
107
+ some container in object.get(object.get(input.spec.template, "spec", {}), "containers", [])
108
+ }
109
+
110
+ deployment_all_containers contains container if {
111
+ some container in object.get(object.get(input.spec.template, "spec", {}), "initContainers", [])
112
+ }
113
+
114
+ # Чи у контейнера є непорожнє resources.requests.cpu (рядок або число > 0).
115
+ has_non_empty_cpu_request(container) if {
116
+ cpu := container.resources.requests.cpu
117
+ is_string(cpu)
118
+ trim_space(cpu) != ""
119
+ }
120
+
121
+ has_non_empty_cpu_request(container) if {
122
+ cpu := container.resources.requests.cpu
123
+ is_number(cpu)
124
+ cpu > 0
125
+ }
126
+
127
+ # Чи у контейнера в реальності присутнє поле resources.requests.cpu (хай і порожнє).
128
+ has_cpu_field(container) if {
129
+ _ := container.resources.requests.cpu
130
+ }
@@ -0,0 +1,37 @@
1
+ # Порт перевірок `npm/tsconfig.emit-types.json` з `npm/scripts/check-npm-module.mjs`
2
+ # (npm-module.mdc).
3
+ #
4
+ # Запуск (локально):
5
+ # conftest test npm/tsconfig.emit-types.json -p npm/policy/npm_module \
6
+ # --namespace npm_module.emit_types_config
7
+ #
8
+ # Перевіряє: `compilerOptions.{allowJs, declaration, emitDeclarationOnly, outDir,
9
+ # skipLibCheck}` мають канонічні значення (true/true/true/"types"/true). FS-перевірки
10
+ # (наявність самого `tsconfig.emit-types.json`, активність layout-варіанта) — у JS.
11
+ #
12
+ # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
13
+ # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
14
+ # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
15
+ package npm_module.emit_types_config
16
+
17
+ import rego.v1
18
+
19
+ required_compiler_options := {
20
+ "allowJs": true,
21
+ "declaration": true,
22
+ "emitDeclarationOnly": true,
23
+ "outDir": "types",
24
+ "skipLibCheck": true,
25
+ }
26
+
27
+ deny contains msg if {
28
+ not is_object(object.get(input, "compilerOptions", null))
29
+ msg := "npm/tsconfig.emit-types.json: відсутній compilerOptions (npm-module.mdc)"
30
+ }
31
+
32
+ deny contains msg if {
33
+ is_object(input.compilerOptions)
34
+ some key, expected in required_compiler_options
35
+ object.get(input.compilerOptions, key, null) != expected
36
+ msg := sprintf("npm/tsconfig.emit-types.json: compilerOptions.%s має бути %v (npm-module.mdc)", [key, expected])
37
+ }
@@ -0,0 +1,55 @@
1
+ # Порт перевірок `npm/package.json` з `npm/scripts/check-npm-module.mjs`
2
+ # (npm-module.mdc).
3
+ #
4
+ # Запуск (локально):
5
+ # conftest test npm/package.json -p npm/policy/npm_module \
6
+ # --namespace npm_module.npm_package_json
7
+ #
8
+ # Перевіряє: поле `types` має будь-який з двох канонічних патернів:
9
+ # - `./types/index.d.ts` (layout `npm/src` з `.js`); або
10
+ # - `./types/<…>.d.ts` чи `.d.mts` (layout `tsconfig.emit-types.json`).
11
+ #
12
+ # Масив `files` має містити `"types"`. Те, який саме layout активний (зокрема
13
+ # наявність `.js` під `npm/src`), а також існування файлу зі шляху `types` —
14
+ # у JS-перевірці (`check-npm-module.mjs`).
15
+ #
16
+ # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
17
+ # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
18
+ # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
19
+ package npm_module.npm_package_json
20
+
21
+ import rego.v1
22
+
23
+ # Шаблон повідомлення про неканонічне поле `types` — через `concat` для
24
+ # regal style/line-length.
25
+ types_field_template := concat(" ", [
26
+ "npm/package.json: поле \"types\" має бути \"./types/index.d.ts\"",
27
+ "або \"./types/<…>.d.ts|.d.mts\" (зараз: %v) (npm-module.mdc)",
28
+ ])
29
+
30
+ # ── deny: types ────────────────────────────────────────────────────────────
31
+
32
+ deny contains msg if {
33
+ types_field := object.get(input, "types", "")
34
+ not valid_types_field(types_field)
35
+ msg := sprintf(types_field_template, [types_field])
36
+ }
37
+
38
+ # ── deny: files має містити "types" ───────────────────────────────────────
39
+
40
+ deny contains msg if {
41
+ not is_array(object.get(input, "files", null))
42
+ msg := "npm/package.json: масив \"files\" відсутній — має містити \"types\" (npm-module.mdc)"
43
+ }
44
+
45
+ deny contains msg if {
46
+ is_array(input.files)
47
+ not "types" in {f | some f in input.files}
48
+ msg := "npm/package.json: масив \"files\" має містити \"types\" (npm-module.mdc)"
49
+ }
50
+
51
+ # ── helpers ────────────────────────────────────────────────────────────────
52
+
53
+ valid_types_field("./types/index.d.ts")
54
+
55
+ valid_types_field(t) if regex.match(`^\./types/.+\.d\.(ts|mts)$`, t)
@@ -0,0 +1,79 @@
1
+ # Порт перевірок `.github/workflows/npm-publish.yml` з `npm/scripts/check-npm-module.mjs`
2
+ # (npm-module.mdc).
3
+ #
4
+ # Запуск (локально):
5
+ # conftest test .github/workflows/npm-publish.yml -p npm/policy/npm_module \
6
+ # --namespace npm_module.npm_publish_yml
7
+ #
8
+ # Перевіряє: `on.push.paths` містить glob з `npm/**`, `on.push.branches` містить
9
+ # `main`, у jobs є `permissions.id-token: write` (OIDC), є крок з
10
+ # `uses: JS-DevTools/npm-publish` і `with.package: npm/package.json`.
11
+ #
12
+ # Універсальні workflow-перевірки (concurrency, заборонені setup-bun/cache/install,
13
+ # shell line-continuation) — у `ga.workflow_common`.
14
+ #
15
+ # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
16
+ # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
17
+ # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
18
+ package npm_module.npm_publish_yml
19
+
20
+ import rego.v1
21
+
22
+ # YAML 1.1 quirk: ключ `on:` → boolean true → у конфтесті ключ "true".
23
+ gha_on := input["true"]
24
+
25
+ # Шаблон повідомлення про відсутній JS-DevTools/npm-publish крок — через `concat`
26
+ # для regal style/line-length.
27
+ npm_publish_step_template := concat(" ", [
28
+ "npm-publish.yml: очікується `uses: JS-DevTools/npm-publish`",
29
+ "з `with.package: npm/package.json` (npm-module.mdc)",
30
+ ])
31
+
32
+ # ── deny: paths/branches ──────────────────────────────────────────────────
33
+
34
+ deny contains msg if {
35
+ not push_paths_have_npm_glob
36
+ msg := "npm-publish.yml: у on.push.paths має бути `npm/**` (npm-module.mdc)"
37
+ }
38
+
39
+ deny contains msg if {
40
+ not push_branches_have_main
41
+ msg := "npm-publish.yml: on.push.branches має містити `main` (npm-module.mdc)"
42
+ }
43
+
44
+ # ── deny: id-token: write у permissions хоч одного job ────────────────────
45
+
46
+ deny contains msg if {
47
+ not any_job_has_id_token_write
48
+ msg := "npm-publish.yml: permissions має містити `id-token: write` (OIDC) (npm-module.mdc)"
49
+ }
50
+
51
+ # ── deny: крок з uses JS-DevTools/npm-publish та with.package ─────────────
52
+
53
+ deny contains npm_publish_step_template if {
54
+ not has_npm_publish_step
55
+ }
56
+
57
+ # ── helpers ────────────────────────────────────────────────────────────────
58
+
59
+ push_paths_have_npm_glob if {
60
+ some p in gha_on.push.paths
61
+ is_string(p)
62
+ contains(p, "npm/**")
63
+ }
64
+
65
+ push_branches_have_main if {
66
+ "main" in {b | some b in gha_on.push.branches}
67
+ }
68
+
69
+ any_job_has_id_token_write if {
70
+ some job in object.get(input, "jobs", {})
71
+ job.permissions["id-token"] == "write"
72
+ }
73
+
74
+ has_npm_publish_step if {
75
+ some job in object.get(input, "jobs", {})
76
+ some step in object.get(job, "steps", [])
77
+ contains(object.get(step, "uses", ""), "JS-DevTools/npm-publish")
78
+ step.with.package == "npm/package.json"
79
+ }
@@ -0,0 +1,28 @@
1
+ # Порт перевірки кореневого `package.json` з `npm/scripts/check-npm-module.mjs`
2
+ # (npm-module.mdc) — масив `workspaces` має містити "npm".
3
+ #
4
+ # Запуск (локально):
5
+ # conftest test package.json -p npm/policy/npm_module \
6
+ # --namespace npm_module.root_package_json
7
+ #
8
+ # Решта кореневих `package.json`-перевірок (заборонені поля, devDeps лише @nitra/*)
9
+ # — у `bun.package_json`. FS-перевірки (наявність каталогу `npm/`,
10
+ # `npm/package.json`) — у JS.
11
+ #
12
+ # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
13
+ # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
14
+ # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
15
+ package npm_module.root_package_json
16
+
17
+ import rego.v1
18
+
19
+ deny contains msg if {
20
+ not is_array(object.get(input, "workspaces", null))
21
+ msg := "package.json: масив workspaces відсутній — має містити \"npm\" (npm-module.mdc)"
22
+ }
23
+
24
+ deny contains msg if {
25
+ is_array(input.workspaces)
26
+ not "npm" in {w | some w in input.workspaces}
27
+ msg := "package.json: workspaces має містити \"npm\" (npm-module.mdc)"
28
+ }
@@ -0,0 +1,32 @@
1
+ # Порт перевірки `lint-php.yml` з `npm/scripts/check-php.mjs` (php.mdc).
2
+ #
3
+ # Запуск (локально):
4
+ # conftest test .github/workflows/lint-php.yml -p npm/policy/php \
5
+ # --namespace php.lint_php_yml
6
+ #
7
+ # Перевіряє: хоча б один крок `run` містить `bun run lint-php`. Універсальні
8
+ # workflow-перевірки — у `ga.workflow_common`.
9
+ #
10
+ # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
11
+ # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
12
+ # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
13
+ package php.lint_php_yml
14
+
15
+ import rego.v1
16
+
17
+ all_run_text := concat("\n", [run_text |
18
+ some job in object.get(input, "jobs", {})
19
+ some step in object.get(job, "steps", [])
20
+ run_text := step_run_to_text(step)
21
+ ])
22
+
23
+ deny contains msg if {
24
+ not contains(all_run_text, "bun run lint-php")
25
+ msg := "lint-php.yml: жоден крок run не містить `bun run lint-php` (php.mdc)"
26
+ }
27
+
28
+ step_run_to_text(step) := step.run if is_string(step.run)
29
+
30
+ else := concat("\n", [s | some s in step.run]) if is_array(step.run)
31
+
32
+ else := ""
@@ -0,0 +1,19 @@
1
+ # Порт перевірки `package.json` з `npm/scripts/check-php.mjs` (php.mdc).
2
+ #
3
+ # Запуск (локально):
4
+ # conftest test package.json -p npm/policy/php --namespace php.package_json
5
+ #
6
+ # Перевіряє: наявність скрипта `lint-php`. FS-перевірки (`composer.json`, наявність
7
+ # `package.json` як такого) — у JS.
8
+ #
9
+ # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
10
+ # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
11
+ # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
12
+ package php.package_json
13
+
14
+ import rego.v1
15
+
16
+ deny contains msg if {
17
+ not object.get(object.get(input, "scripts", {}), "lint-php", false)
18
+ msg := "package.json: додай скрипт \"lint-php\" (php.mdc)"
19
+ }
@@ -0,0 +1,35 @@
1
+ # Порт перевірки `lint-style.yml` з `npm/scripts/check-style-lint.mjs` (style-lint.mdc).
2
+ #
3
+ # Запуск (локально):
4
+ # conftest test .github/workflows/lint-style.yml -p npm/policy/style_lint \
5
+ # --namespace style_lint.lint_style_yml
6
+ #
7
+ # Перевіряє: хоча б один крок `run` містить `npx stylelint` (саме через npx, не
8
+ # `bun run lint-style`). Універсальні workflow-перевірки (concurrency, заборонені
9
+ # setup-bun/cache/install) — у `ga.workflow_common`.
10
+ #
11
+ # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
12
+ # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
13
+ # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
14
+ package style_lint.lint_style_yml
15
+
16
+ import rego.v1
17
+
18
+ # Усі тексти `run:` зі steps усіх jobs, склеєні в один blob — для substring-перевірки.
19
+ all_run_text := concat("\n", [run_text |
20
+ some job in object.get(input, "jobs", {})
21
+ some step in object.get(job, "steps", [])
22
+ run_text := step_run_to_text(step)
23
+ ])
24
+
25
+ deny contains msg if {
26
+ not contains(all_run_text, "npx stylelint")
27
+ msg := "lint-style.yml: жоден крок run не містить `npx stylelint` (style-lint.mdc)"
28
+ }
29
+
30
+ # Текст `run:` як один рядок: підтримує string і array форми (YAML).
31
+ step_run_to_text(step) := step.run if is_string(step.run)
32
+
33
+ else := concat("\n", [s | some s in step.run]) if is_array(step.run)
34
+
35
+ else := ""
@@ -0,0 +1,49 @@
1
+ # Порт перевірок `package.json` з `npm/scripts/check-style-lint.mjs` (style-lint.mdc).
2
+ #
3
+ # Запуск (локально):
4
+ # conftest test package.json -p npm/policy/style_lint --namespace style_lint.package_json
5
+ #
6
+ # Перевіряє: наявність скрипта `lint-style` з `npx stylelint`, `@nitra/stylelint-config`
7
+ # у `devDependencies`, поле `stylelint.extends == "@nitra/stylelint-config"`. FS-частина
8
+ # (зовнішні `.stylelintrc.*` як альтернатива полю; `.stylelintignore`) лишається у JS.
9
+ #
10
+ # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
11
+ # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
12
+ # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
13
+ package style_lint.package_json
14
+
15
+ import rego.v1
16
+
17
+ # ── deny: lint-style скрипт ───────────────────────────────────────────────
18
+
19
+ deny contains msg if {
20
+ not object.get(object.get(input, "scripts", {}), "lint-style", false)
21
+ msg := "package.json не містить скрипт \"lint-style\" (style-lint.mdc)"
22
+ }
23
+
24
+ deny contains msg if {
25
+ lint_style := object.get(object.get(input, "scripts", {}), "lint-style", "")
26
+ lint_style != ""
27
+ not contains(lint_style, "npx stylelint")
28
+ msg := sprintf("lint-style має викликати stylelint через npx (зараз: %q) (style-lint.mdc)", [lint_style])
29
+ }
30
+
31
+ # ── deny: @nitra/stylelint-config у devDependencies ───────────────────────
32
+
33
+ deny contains msg if {
34
+ dev := object.get(input, "devDependencies", {})
35
+ not "@nitra/stylelint-config" in object.keys(dev)
36
+ msg := "@nitra/stylelint-config відсутній — bun add -d @nitra/stylelint-config (style-lint.mdc)"
37
+ }
38
+
39
+ # ── deny: поле stylelint.extends = "@nitra/stylelint-config" ──────────────
40
+ #
41
+ # JS-перевірка дозволяє альтернативу: окремий файл `.stylelintrc.*`. Цю частину
42
+ # перевіряємо в JS (FS-вибірка); тут — лише структурна валідація поля, якщо воно є.
43
+
44
+ deny contains msg if {
45
+ cfg := object.get(input, "stylelint", null)
46
+ is_object(cfg)
47
+ object.get(cfg, "extends", null) != "@nitra/stylelint-config"
48
+ msg := "package.json: stylelint.extends має бути \"@nitra/stylelint-config\" (style-lint.mdc)"
49
+ }