@nitra/cursor 3.12.0 → 3.13.0

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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.13.0] - 2026-06-02
4
+
5
+ ### Changed
6
+
7
+ - k8s hasura_configmap: розширено перелік обов'язкових env у ConfigMap Hasura-Deployment — додатково до HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS="true" тепер вимагаються HASURA_GRAPHQL_ENABLE_RELAY="false", HASURA_GRAPHQL_ENABLE_TELEMETRY="false", HASURA_GRAPHQL_ENABLED_LOG_TYPES="startup,http-log" (точний рядок) і HASURA_GRAPHQL_DISABLE_EVENTING (ключ обов'язковий, значення довільне, за замовчуванням "true")
8
+
3
9
  ## [3.12.0] - 2026-06-02
4
10
 
5
11
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "3.12.0",
3
+ "version": "3.13.0",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",
@@ -2349,47 +2349,17 @@ export function isHasuraDeploymentManifest(manifest) {
2349
2349
  }
2350
2350
 
2351
2351
  /**
2352
- * Обов'язковий ключ у **`data`** ConfigMap для Hasura-Deployment (узгоджено з k8s.mdc).
2353
- */
2354
- export const HASURA_REMOTE_SCHEMA_PERMISSIONS_KEY = 'HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS'
2355
-
2356
- /**
2357
- * Чи значення поля `data.<key>` у ConfigMap читається як логічне **true**.
2358
- * ConfigMap у Kubernetes тримає значення як рядки, але в YAML часто пишуть без лапок —
2359
- * тому приймаємо і булевий **true**, і рядок **"true"** (без регістрової залежності).
2360
- * @param {unknown} v значення з `data[HASURA_REMOTE_SCHEMA_PERMISSIONS_KEY]`
2361
- * @returns {boolean} true, якщо значення — `true` або рядок `'true'`
2362
- */
2363
- function isConfigMapValueTrue(v) {
2364
- if (v === true) return true
2365
- if (typeof v === 'string' && v.trim().toLowerCase() === 'true') return true
2366
- return false
2367
- }
2368
-
2369
- /**
2370
- * Чи порушує ConfigMap вимогу щодо **`HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS: "true"`** (k8s.mdc).
2371
- * Перевірка застосовна, коли в тому ж каталозі є Hasura-Deployment (див. `isHasuraDeploymentManifest`).
2372
- * @param {unknown} manifest корінь YAML-документа ConfigMap
2373
- * @returns {string | null} текст порушення або null, якщо не ConfigMap / ключ є і значення `true`
2374
- */
2375
- export function hasuraConfigMapRemoteSchemaPermissionsViolation(manifest) {
2376
- if (manifest === null || manifest === undefined || typeof manifest !== 'object' || Array.isArray(manifest))
2377
- return null
2378
- const rec = /** @type {Record<string, unknown>} */ (manifest)
2379
- if (rec.kind !== 'ConfigMap') return null
2380
- const data = rec.data
2381
- if (data === null || data === undefined || typeof data !== 'object' || Array.isArray(data)) {
2382
- return `data.${HASURA_REMOTE_SCHEMA_PERMISSIONS_KEY}: додай ключ зі значенням "true" (Deployment з hasura/graphql-engine — див. k8s.mdc)`
2383
- }
2384
- const d = /** @type {Record<string, unknown>} */ (data)
2385
- if (!Object.hasOwn(d, HASURA_REMOTE_SCHEMA_PERMISSIONS_KEY)) {
2386
- return `data.${HASURA_REMOTE_SCHEMA_PERMISSIONS_KEY}: додай ключ зі значенням "true" (Deployment з hasura/graphql-engine — див. k8s.mdc)`
2387
- }
2388
- if (!isConfigMapValueTrue(d[HASURA_REMOTE_SCHEMA_PERMISSIONS_KEY])) {
2389
- return `data.${HASURA_REMOTE_SCHEMA_PERMISSIONS_KEY}: значення має бути "true" (зараз: ${JSON.stringify(d[HASURA_REMOTE_SCHEMA_PERMISSIONS_KEY])}) (див. k8s.mdc)`
2390
- }
2391
- return null
2392
- }
2352
+ * Обов'язкові env-ключі у **`data`** ConfigMap для Hasura-Deployment (узгоджено з
2353
+ * rego-пакетом `k8s.hasura_configmap` та k8s.mdc). Лише для людиночитного pass-повідомлення —
2354
+ * авторитетна пер-документна валідація (наявність ключів і значення) живе в rego.
2355
+ */
2356
+ export const HASURA_REQUIRED_ENV_KEYS = [
2357
+ 'HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS',
2358
+ 'HASURA_GRAPHQL_ENABLE_RELAY',
2359
+ 'HASURA_GRAPHQL_ENABLE_TELEMETRY',
2360
+ 'HASURA_GRAPHQL_ENABLED_LOG_TYPES',
2361
+ 'HASURA_GRAPHQL_DISABLE_EVENTING'
2362
+ ]
2393
2363
 
2394
2364
  const K8S_YAML_EXT_RE = /\.ya?ml$/iu
2395
2365
 
@@ -3676,7 +3646,10 @@ async function validateConfigMapNameMatchesDeployment(root, yamlFilesAbs, fail,
3676
3646
 
3677
3647
  /**
3678
3648
  * Для кожного `k8s/base/configmap.yaml`, у каталозі якого поруч є Hasura-Deployment,
3679
- * вимагає у `data` ключ **`HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS`** зі значенням **`"true"`** (k8s.mdc).
3649
+ * вимагає у `data` обов'язкові env-ключі (`HASURA_REQUIRED_ENV_KEYS`) з очікуваними
3650
+ * значеннями (`HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS="true"`,
3651
+ * `HASURA_GRAPHQL_ENABLE_RELAY="false"`, `HASURA_GRAPHQL_ENABLE_TELEMETRY="false"`,
3652
+ * `HASURA_GRAPHQL_ENABLED_LOG_TYPES="startup,http-log"`, `HASURA_GRAPHQL_DISABLE_EVENTING` — будь-яке) (k8s.mdc).
3680
3653
  * @param {string} root корінь репозиторію
3681
3654
  * @param {string[]} yamlFilesAbs yaml під k8s
3682
3655
  * @param {(msg: string) => void} fail callback при помилці
@@ -3688,8 +3661,8 @@ async function validateHasuraConfigMapRemoteSchemaPermissions(root, yamlFilesAbs
3688
3661
  return CONFIGMAP_BASE_PATH_RE.test(`/${rel}`) || rel === 'k8s/base/configmap.yaml'
3689
3662
  })
3690
3663
  // JS gating: відберемо ConfigMap-файли, у каталозі яких поруч є Hasura-Deployment.
3691
- // Per-document валідація `data.HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS == "true"`
3692
- // — у rego-пакеті `k8s.hasura_configmap`.
3664
+ // Per-document валідація обов'язкових `data.HASURA_GRAPHQL_*` env — у rego-пакеті
3665
+ // `k8s.hasura_configmap`.
3693
3666
  const paired = []
3694
3667
  for (const cmAbs of cmFiles) {
3695
3668
  const deployment = await findDeploymentDocInDir(dirname(cmAbs))
@@ -3708,7 +3681,7 @@ async function validateHasuraConfigMapRemoteSchemaPermissions(root, yamlFilesAbs
3708
3681
  fail(`${rel}: ${v.message}`)
3709
3682
  }
3710
3683
  if (violations.length === 0) {
3711
- passFn(`Hasura-ConfigMap (${paired.length}) відповідає ${HASURA_REMOTE_SCHEMA_PERMISSIONS_KEY}="true" (rego)`)
3684
+ passFn(`Hasura-ConfigMap (${paired.length}) містить обов'язкові env [${HASURA_REQUIRED_ENV_KEYS.join(', ')}] (rego)`)
3712
3685
  }
3713
3686
  }
3714
3687
 
package/rules/k8s/k8s.mdc CHANGED
@@ -297,11 +297,23 @@ spec:
297
297
 
298
298
  ## ConfigMap для Hasura-Deployment
299
299
 
300
- Якщо в `k8s/base/` поруч із **`configmap.yaml`** є **Deployment** з образом **`hasura/graphql-engine`**, у `data` ConfigMap **обов'язково** має бути ключ **`HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS`** зі значенням **`"true"`**. Точні умови перевірки — **`rules/k8s/fix.mjs`**.
300
+ Якщо в `k8s/base/` поруч із **`configmap.yaml`** є **Deployment** з образом **`hasura/graphql-engine`**, у `data` ConfigMap **обов'язково** мають бути env-ключі:
301
+
302
+ - **`HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS`** зі значенням **`"true"`**;
303
+ - **`HASURA_GRAPHQL_ENABLE_RELAY`** зі значенням **`"false"`**;
304
+ - **`HASURA_GRAPHQL_ENABLE_TELEMETRY`** зі значенням **`"false"`**;
305
+ - **`HASURA_GRAPHQL_ENABLED_LOG_TYPES`** зі значенням **`"startup,http-log"`** (точний рядок);
306
+ - **`HASURA_GRAPHQL_DISABLE_EVENTING`** — ключ обов'язковий, значення довільне (за замовчуванням **`"true"`**).
307
+
308
+ Точні умови перевірки — rego-пакет **`k8s.hasura_configmap`** (cross-file прив'язка ConfigMap↔Deployment — у `rules/k8s/js/manifests.mjs`).
301
309
 
302
310
  ```yaml
303
311
  data:
304
312
  HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS: 'true'
313
+ HASURA_GRAPHQL_ENABLE_RELAY: 'false'
314
+ HASURA_GRAPHQL_ENABLE_TELEMETRY: 'false'
315
+ HASURA_GRAPHQL_ENABLED_LOG_TYPES: 'startup,http-log'
316
+ HASURA_GRAPHQL_DISABLE_EVENTING: 'true'
305
317
  ```
306
318
 
307
319
  ## Kustomize: структура каталогів (`base` / overlays)
@@ -1,18 +1,30 @@
1
1
  # Порт перевірки ConfigMap для Hasura-Deployment з
2
2
  # `npm/scripts/rules/k8s/fix.mjs` (k8s.mdc): у ConfigMap, що сусідствує з
3
- # Hasura-Deployment, у `data` обов'язково має бути ключ
4
- # `HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS` зі значенням `"true"`.
3
+ # Hasura-Deployment, у `data` обов'язково мають бути env-ключі зі списку
4
+ # `required_env` з очікуваними значеннями:
5
+ # "HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS" → "true"
6
+ # "HASURA_GRAPHQL_ENABLE_RELAY" → "false"
7
+ # "HASURA_GRAPHQL_ENABLE_TELEMETRY" → "false"
8
+ # "HASURA_GRAPHQL_ENABLED_LOG_TYPES" → "startup,http-log" (точний рядок)
9
+ # "HASURA_GRAPHQL_DISABLE_EVENTING" → null (ключ обов'язковий,
10
+ # значення довільне; за замовчуванням рекомендовано "true")
11
+ #
12
+ # Семантика очікуваного значення у `required_env`:
13
+ # "true" — має читатись як логічне true (boolean true або рядок "true", case-insensitive);
14
+ # "false" — має читатись як логічне false (boolean false або рядок "false", case-insensitive);
15
+ # null — ключ обов'язковий, значення довільне (за замовчуванням "true");
16
+ # інший рядок — значення має точно дорівнювати рядку (exact match).
5
17
  #
6
18
  # Запуск (локально, лише для ConfigMap у каталозі з Hasura-Deployment):
7
19
  # conftest test path/to/k8s/.../configmap.yaml \
8
20
  # -p npm/policy/k8s/hasura_configmap \
9
21
  # --namespace k8s.hasura_configmap
10
22
  #
11
- # Прив'язка ConfigMap-Deployment cross-file — у JS (`rules/k8s/fix.mjs`:
23
+ # Прив'язка ConfigMap-Deployment cross-file — у JS (`rules/k8s/js/manifests.mjs`:
12
24
  # `validateHasuraConfigMapRemoteSchemaPermissions` шукає Hasura-Deployment
13
25
  # у тому ж dir-у і викликає conftest з цією намеспейс лише для відповідних
14
- # ConfigMap-ів). JS authoritative (`hasuraConfigMapRemoteSchemaPermissionsViolation`,
15
- # константа `HASURA_REMOTE_SCHEMA_PERMISSIONS_KEY`).
26
+ # ConfigMap-ів). Rego authoritative для пер-документної валідації; JS лишає
27
+ # лише cross-file orchestration.
16
28
  #
17
29
  # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
18
30
  # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`.
@@ -20,45 +32,82 @@ package k8s.hasura_configmap
20
32
 
21
33
  import rego.v1
22
34
 
23
- # Обов'язковий ключ у `data` (узгоджено з `rules/k8s/fix.mjs`).
24
- required_key := "HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS"
35
+ # Обов'язкові env-ключі у `data` (узгоджено з `rules/k8s/js/manifests.mjs` та k8s.mdc).
36
+ # Значення — очікуваний стан ключа (семантика — у шапці файлу).
37
+ required_env := {
38
+ "HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS": "true",
39
+ "HASURA_GRAPHQL_ENABLE_RELAY": "false",
40
+ "HASURA_GRAPHQL_ENABLE_TELEMETRY": "false",
41
+ "HASURA_GRAPHQL_ENABLED_LOG_TYPES": "startup,http-log",
42
+ "HASURA_GRAPHQL_DISABLE_EVENTING": null,
43
+ }
44
+
45
+ # Множина "boolean-подібних" очікувань — для них значення читається як логічне,
46
+ # а не звіряється точним рядком.
47
+ bool_expected := {"true", "false"}
48
+
49
+ # Підказка про очікуване значення для повідомлення про відсутній ключ.
50
+ expected_hint(null) := "(значення довільне, за замовчуванням \"true\")"
25
51
 
26
- key_missing_template := concat(" ", [
27
- "data.%s: додай ключ зі значенням \"true\"",
28
- "(Deployment з hasura/graphql-engine — k8s.mdc)",
29
- ])
52
+ expected_hint(expected) := sprintf("зі значенням \"%s\"", [expected]) if is_string(expected)
30
53
 
31
- key_value_wrong_template := concat(" ", ["data.%s: значення має бути \"true\" (зараз: %v) (k8s.mdc)"])
54
+ key_value_wrong_template := concat(" ", ["data.%s: значення має бути \"%s\" (зараз: %v) (k8s.mdc)"])
55
+
56
+ # Ключ відсутній: `data` не об'єкт або в ньому немає обов'язкового ключа.
57
+ deny contains msg if {
58
+ input.kind == "ConfigMap"
59
+ some key, expected in required_env
60
+ not key_present(key)
61
+ msg := sprintf(
62
+ "data.%s: додай ключ %s (Deployment з hasura/graphql-engine — k8s.mdc)",
63
+ [key, expected_hint(expected)],
64
+ )
65
+ }
32
66
 
67
+ # Очікуване "true", а значення не читається як логічне true.
33
68
  deny contains msg if {
34
69
  input.kind == "ConfigMap"
35
- not is_object(object.get(input, "data", null))
36
- msg := sprintf(key_missing_template, [required_key])
70
+ d := object.get(input, "data", null)
71
+ is_object(d)
72
+ some key, expected in required_env
73
+ expected == "true"
74
+ key in object.keys(d)
75
+ not is_value_true(d[key])
76
+ msg := sprintf(key_value_wrong_template, [key, "true", d[key]])
37
77
  }
38
78
 
79
+ # Очікуване "false", а значення не читається як логічне false.
39
80
  deny contains msg if {
40
81
  input.kind == "ConfigMap"
41
82
  d := object.get(input, "data", null)
42
83
  is_object(d)
43
- not key_present(d)
44
- msg := sprintf(key_missing_template, [required_key])
84
+ some key, expected in required_env
85
+ expected == "false"
86
+ key in object.keys(d)
87
+ not is_value_false(d[key])
88
+ msg := sprintf(key_value_wrong_template, [key, "false", d[key]])
45
89
  }
46
90
 
91
+ # Очікуване — точний рядок (не "true"/"false"/null), а значення не збігається.
47
92
  deny contains msg if {
48
93
  input.kind == "ConfigMap"
49
94
  d := object.get(input, "data", null)
50
95
  is_object(d)
51
- key_present(d)
52
- value := d[required_key]
53
- not is_value_true(value)
54
- msg := sprintf(key_value_wrong_template, [required_key, value])
96
+ some key, expected in required_env
97
+ is_string(expected)
98
+ not expected in bool_expected
99
+ key in object.keys(d)
100
+ d[key] != expected
101
+ msg := sprintf(key_value_wrong_template, [key, expected, d[key]])
55
102
  }
56
103
 
57
- key_present(d) if {
58
- required_key in object.keys(d)
104
+ key_present(key) if {
105
+ d := object.get(input, "data", null)
106
+ is_object(d)
107
+ key in object.keys(d)
59
108
  }
60
109
 
61
- # Значення вважається "true", якщо це boolean true або рядок "true"
110
+ # Значення вважається "true"/"false", якщо це відповідний boolean або рядок
62
111
  # (case-insensitive). ConfigMap у Kubernetes тримає рядки, але YAML без лапок
63
112
  # дає boolean — приймаємо обидва варіанти.
64
113
  is_value_true(true)
@@ -67,3 +116,10 @@ is_value_true(v) if {
67
116
  is_string(v)
68
117
  lower(trim_space(v)) == "true"
69
118
  }
119
+
120
+ is_value_false(false)
121
+
122
+ is_value_false(v) if {
123
+ is_string(v)
124
+ lower(trim_space(v)) == "false"
125
+ }