@nitra/cursor 1.13.13 → 1.13.14
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 +14 -0
- package/package.json +1 -1
- package/rules/bun/bun.mdc +3 -6
- package/rules/bun/policy/bunfig/bunfig.rego +19 -23
- package/rules/bun/policy/bunfig/template/bunfig.toml.snippet.toml +2 -0
- package/rules/bun/policy/package_json/package_json.rego +15 -33
- package/rules/bun/policy/package_json/template/package.json.deny.json +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,20 @@
|
|
|
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.14] - 2026-05-17
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `bun` rule template/ міграція (Phase 4): 2 концерни — `bunfig` (snippet-walker 2-level, `[install].linker = "hoisted"`) + `package_json` (partial — top-level deny-fields у template).
|
|
12
|
+
- `bunfig_test.rego` створено з нуля (раніше тестів не було) — 5 позитивних/негативних + drift.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- `bun.bunfig.rego` — використовує той самий 2-level snippet-walker, що `ga.vscode_settings` (leaf-by-leaf + guard на non-object section).
|
|
17
|
+
- `bun.package_json.rego` — top-level deny-fields (`packageManager`, `dependencies`) тепер читаються з `data.template.deny`. Сентинельний value у `object.get(input, field, "__bun_missing__")` зберігає behavior «field present навіть якщо порожній обʼєкт». Логіка `@nitra/*`-only у devDependencies та lint-aggregator (cross-script) лишається у rego — це inverse-patterns, які не виносяться у template.
|
|
18
|
+
- `bun.mdc` — inline `bunfig.toml` snippet замінено на template-link; додано посилання на `package.json.deny.json` для документації заборонених top-level полів.
|
|
19
|
+
- `docs/adr/template-dir-concern-inventory.md` — `bun.*` концерни позначено ✓; додано Phase 4 у прогрес-секцію; tally: 15/39 (38%).
|
|
20
|
+
|
|
7
21
|
## [1.13.13] - 2026-05-17
|
|
8
22
|
|
|
9
23
|
### Added
|
package/package.json
CHANGED
package/rules/bun/bun.mdc
CHANGED
|
@@ -34,10 +34,7 @@ Lockfile у репозиторії: `bun.lock`.
|
|
|
34
34
|
|
|
35
35
|
У корені репозиторію має бути **`bunfig.toml`** з **hoisted** лінкером (пласке `node_modules`, сумісне з інструментами, які не розуміють ізольований layout Bun):
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
[install]
|
|
39
|
-
linker = "hoisted"
|
|
40
|
-
```
|
|
37
|
+
- Канон `bunfig.toml`: [bunfig.toml.snippet.toml](./policy/bunfig/template/bunfig.toml.snippet.toml)
|
|
41
38
|
|
|
42
39
|
Для Bun monorepo:
|
|
43
40
|
|
|
@@ -45,9 +42,9 @@ linker = "hoisted"
|
|
|
45
42
|
- Якщо залежність потрібна лише одному пакету, додавати її в директорії цього пакета.
|
|
46
43
|
- У CI та локально запускати скрипти через `bun run`.
|
|
47
44
|
|
|
48
|
-
В кореневому в package.json не повинно бути `dependencies`, а в `devDependencies` — тільки модулі `@nitra
|
|
45
|
+
В кореневому в package.json не повинно бути `dependencies`, а в `devDependencies` — тільки модулі `@nitra/*`. Якщо в package.json є поля `packageManager`, то прибрати їх, також прибрати всі директорії та файли для yarn.
|
|
49
46
|
|
|
50
|
-
|
|
47
|
+
- Заборонені top-level поля у root `package.json` (з причинами): [package.json.deny.json](./policy/package_json/template/package.json.deny.json)
|
|
51
48
|
|
|
52
49
|
Коли зміна відбувається в Dockerfile, то використовувати
|
|
53
50
|
|
|
@@ -1,33 +1,29 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Перевірка `bunfig.toml` для bun (bun.mdc).
|
|
2
2
|
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
# `package-lock.json` тощо, директорія `.yarn/`) живуть у `check-bun.mjs` — Rego
|
|
9
|
-
# працює лише з вже завантаженим input.
|
|
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).
|
|
3
|
+
# Канон надходить через --data: { "template": { "snippet": ... } }
|
|
4
|
+
# Структура --data сформована з template/bunfig.toml.snippet.toml.
|
|
5
|
+
# Snippet — 2-рівнева мапа (section → key → expected). Walker такий самий,
|
|
6
|
+
# як для ga.vscode_settings: leaf-by-leaf коли section-обʼєкт існує + окремий
|
|
7
|
+
# deny коли section відсутній / не обʼєкт.
|
|
14
8
|
package bun.bunfig
|
|
15
9
|
|
|
16
10
|
import rego.v1
|
|
17
11
|
|
|
12
|
+
# Leaf-by-leaf: section присутня й обʼєкт.
|
|
18
13
|
deny contains msg if {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
some section, expected_inner in data.template.snippet
|
|
15
|
+
inner := object.get(input, section, {})
|
|
16
|
+
is_object(inner)
|
|
17
|
+
some leaf_key, expected_value in expected_inner
|
|
18
|
+
actual := object.get(inner, leaf_key, null)
|
|
19
|
+
actual != expected_value
|
|
20
|
+
msg := sprintf("bunfig.toml: у секції [%s] має бути %s = %q (bun.mdc)", [section, leaf_key, expected_value])
|
|
24
21
|
}
|
|
25
22
|
|
|
23
|
+
# Section відсутня (null) або не обʼєкт.
|
|
26
24
|
deny contains msg if {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
object.get(input.install, "linker", null) != "hoisted"
|
|
32
|
-
msg := "bunfig.toml: у секції [install] має бути linker = \"hoisted\" (bun.mdc)"
|
|
25
|
+
some section in object.keys(data.template.snippet)
|
|
26
|
+
raw := object.get(input, section, null)
|
|
27
|
+
not is_object(raw)
|
|
28
|
+
msg := sprintf("bunfig.toml: відсутня секція [%s] (bun.mdc)", [section])
|
|
33
29
|
}
|
|
@@ -1,47 +1,38 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Перевірка кореневого `package.json` для bun (bun.mdc).
|
|
2
2
|
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
3
|
+
# Канон надходить через --data: { "template": { "deny": ... } }
|
|
4
|
+
# Структура --data сформована з template/package.json.deny.json
|
|
5
|
+
# (top-level fields заборонені у root).
|
|
5
6
|
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
7
|
+
# Логіка, що ЛИШАЄТЬСЯ у rego (inverse-patterns, не виносяться у template):
|
|
8
|
+
# - `devDependencies` лише `@nitra/*` (inverse-pattern: every dep must match)
|
|
9
|
+
# - Агрегований `lint` скрипт (cross-script aggregation logic)
|
|
9
10
|
#
|
|
10
11
|
# Перевірки, які ЗАЛИШИЛИСЬ у JS (потребують FS / cross-file):
|
|
11
12
|
# - `lint-docker` / `lint-k8s` коли `.n-cursor.json:rules` містить відповідне
|
|
12
13
|
# правило (потрібен другий файл-вхід — у Rego без `--combine` не зробити).
|
|
13
|
-
#
|
|
14
|
-
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
15
|
-
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
16
|
-
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
17
14
|
package bun.package_json
|
|
18
15
|
|
|
19
16
|
import rego.v1
|
|
20
17
|
|
|
21
18
|
# ── Шаблони повідомлень ────────────────────────────────────────────────────
|
|
22
19
|
|
|
23
|
-
# Через `concat` — дотримуємося regal style/line-length.
|
|
24
20
|
lint_aggregate_missing_template := concat(" ", [
|
|
25
21
|
"У package.json є скрипти %v, але немає агрегованого `lint`.",
|
|
26
22
|
"Додай скрипт, який запускає їх через `bun run` (bun.mdc)",
|
|
27
23
|
])
|
|
28
24
|
|
|
29
|
-
# ── deny: заборонені поля
|
|
30
|
-
|
|
31
|
-
deny contains msg if {
|
|
32
|
-
pm := object.get(input, "packageManager", "")
|
|
33
|
-
pm != ""
|
|
34
|
-
msg := sprintf("package.json містить поле packageManager: %q — видали його (bun.mdc)", [pm])
|
|
35
|
-
}
|
|
25
|
+
# ── deny: заборонені top-level поля (template-driven) ─────────────────────
|
|
36
26
|
|
|
37
|
-
#
|
|
38
|
-
#
|
|
27
|
+
# Сентинельний value відрізняє «поле відсутнє» від «поле є з будь-яким значенням»
|
|
28
|
+
# (наприклад `dependencies: {}` — присутнє але порожнє → теж заборонено).
|
|
39
29
|
deny contains msg if {
|
|
40
|
-
|
|
41
|
-
|
|
30
|
+
some field, reason in data.template.deny
|
|
31
|
+
object.get(input, field, "__bun_missing__") != "__bun_missing__"
|
|
32
|
+
msg := sprintf("package.json: поле %s — %s", [field, reason])
|
|
42
33
|
}
|
|
43
34
|
|
|
44
|
-
# ── deny: devDependencies — лише `@nitra/*`
|
|
35
|
+
# ── deny: devDependencies — лише `@nitra/*` (inverse pattern; не виноситься у template) ─
|
|
45
36
|
|
|
46
37
|
deny contains msg if {
|
|
47
38
|
is_object(input.devDependencies)
|
|
@@ -50,11 +41,7 @@ deny contains msg if {
|
|
|
50
41
|
msg := sprintf("Кореневі devDependencies: дозволені лише @nitra/* — прибери або перенеси: %s (bun.mdc)", [name])
|
|
51
42
|
}
|
|
52
43
|
|
|
53
|
-
# ── deny: агрегований lint-скрипт
|
|
54
|
-
#
|
|
55
|
-
# Якщо в `scripts` є хоч один `lint-*`, має бути скрипт `lint`, у якому
|
|
56
|
-
# через `bun run <ім'я>` викликається кожен такий скрипт; рядок завершується
|
|
57
|
-
# на `&& oxfmt .`.
|
|
44
|
+
# ── deny: агрегований lint-скрипт (cross-script aggregation logic) ───────
|
|
58
45
|
|
|
59
46
|
deny contains msg if {
|
|
60
47
|
count(lint_prefixed_scripts) > 0
|
|
@@ -73,22 +60,17 @@ deny contains msg if {
|
|
|
73
60
|
deny contains msg if {
|
|
74
61
|
count(lint_prefixed_scripts) > 0
|
|
75
62
|
lint_script != ""
|
|
76
|
-
|
|
77
|
-
# Перевіряємо, що рядок завершується `&& oxfmt .` (з можливими пробілами/табами).
|
|
78
|
-
# Trim не потрібен — пробіли/таби в кінці допускаємо в самому regex (`[ \t]*$`).
|
|
79
63
|
not regex.match(`&&[ \t]+oxfmt[ \t]+\.[ \t]*$`, lint_script)
|
|
80
64
|
msg := "Скрипт `lint` має закінчуватися на `&& oxfmt .` (bun.mdc)"
|
|
81
65
|
}
|
|
82
66
|
|
|
83
67
|
# ── helpers ────────────────────────────────────────────────────────────────
|
|
84
68
|
|
|
85
|
-
# Ключі скриптів, що починаються з `lint-` (наприклад `lint-js`, `lint-ga`).
|
|
86
69
|
lint_prefixed_scripts := [name |
|
|
87
70
|
some name, _ in object.get(input, "scripts", {})
|
|
88
71
|
startswith(name, "lint-")
|
|
89
72
|
]
|
|
90
73
|
|
|
91
|
-
# Значення `scripts.lint` як рядок (порожній, якщо поля немає або тип не string).
|
|
92
74
|
default lint_script := ""
|
|
93
75
|
|
|
94
76
|
lint_script := input.scripts.lint if is_string(input.scripts.lint)
|