@nitra/cursor 1.29.3 → 1.29.5
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/.claude-template/hooks/capture-decisions.sh +1 -1
- package/.claude-template/hooks/normalize-decisions.sh +1 -1
- package/CHANGELOG.md +15 -1
- package/package.json +1 -1
- package/rules/bun/bun.mdc +2 -2
- package/rules/bun/policy/package_json/package_json.rego +1 -1
- package/rules/js-bun-db/js-bun-db.mdc +69 -1
|
@@ -36,7 +36,7 @@ log() { printf '%s %s\n' "$(date -Iseconds)" "$*" >> "$LOG"; }
|
|
|
36
36
|
|
|
37
37
|
# Підвантажуємо спільний helper (sourcing — не sub-shell, функції видимі поточному скрипту).
|
|
38
38
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
39
|
-
# shellcheck source=
|
|
39
|
+
# shellcheck source=lib/tooling-only.sh disable=SC1091
|
|
40
40
|
. "$SCRIPT_DIR/lib/tooling-only.sh"
|
|
41
41
|
|
|
42
42
|
log "fired: $SESSION_ID"
|
|
@@ -41,7 +41,7 @@ log() { printf '%s %s\n' "$(date -Iseconds)" "$*" >> "$LOG"; }
|
|
|
41
41
|
|
|
42
42
|
# Підвантажуємо спільний helper (sourcing — не sub-shell, функції видимі поточному скрипту).
|
|
43
43
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
44
|
-
# shellcheck source=
|
|
44
|
+
# shellcheck source=lib/tooling-only.sh disable=SC1091
|
|
45
45
|
. "$SCRIPT_DIR/lib/tooling-only.sh"
|
|
46
46
|
|
|
47
47
|
# Витягає поле `transcript:` з YAML frontmatter ADR-чернетки.
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,21 @@
|
|
|
4
4
|
|
|
5
5
|
Формат — [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), нумерація — [SemVer](https://semver.org/lang/uk/).
|
|
6
6
|
|
|
7
|
+
## [1.29.5] - 2026-05-29
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **`rules/js-bun-db/js-bun-db.mdc`** (`version` 1.11 → 1.12) і дзеркало `.cursor/rules/n-js-bun-db.mdc` — нова секція **`## sql.array(arr, type) для передачі масивів`**: обов'язкове використання `pgWrite.array(arr, type)` / `pgRead.array(arr, type)` замість прямої підстановки JS-масиву `${arr}` або cast-синтаксису `${arr}::type[]` у Bun SQL template literal. Другий аргумент типу обов'язковий — без нього Bun не може вивести pg-тип. Секція містить таблицю заборонених/дозволених патернів, таблицю відповідності JS↔PostgreSQL типів і повний приклад UNNEST + MERGE. Рядок `| Масив значень у \`unnest(...)\` | \`sql.array(arr, type)\` — обов'язково з типом |` додано до таблиці рішень.
|
|
12
|
+
- **`rules/bun/bun.mdc`** (`version` 2.0 → 2.1) і дзеркало `.cursor/rules/n-bun.mdc` — `@playwright/test` додано до **root-only** винятків у `devDependencies` кореневого `package.json` (поряд із Vitest/Stryker peer/tools для `n-cursor coverage`).
|
|
13
|
+
- **`rules/bun/policy/package_json/package_json.rego`** — `"@playwright/test"` додано до набору `allowed_root_test_deps`; дозволяє споживачам тримати пакет у root `devDependencies` без порушення bun-policy.
|
|
14
|
+
- **`rules/bun/policy/package_json/package_json_test.rego`** — новий тест `test_allow_playwright_test`: перевіряє, що `@playwright/test` у root `devDependencies` не генерує deny.
|
|
15
|
+
|
|
16
|
+
## [1.29.4] - 2026-05-29
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- **`.claude-template/hooks/capture-decisions.sh`**, **`.claude-template/hooks/normalize-decisions.sh`** — директива sourcing-у helper-а змінена з `# shellcheck source=npm/.claude-template/hooks/lib/tooling-only.sh` на `# shellcheck source=lib/tooling-only.sh disable=SC1091`. Без `-x` shellcheck не «заходить» у sourced-файл і видає **SC1091** (info), а `runFinalShellcheck` у `rules/text/lint/run-shellcheck.mjs` валить `lint-text` на будь-якому ненульовому exit (info включно). Відносний `source=lib/tooling-only.sh` узгоджений із фікстурою `scripts/tests/sync-claude-config.test.mjs`; `disable=SC1091` глушить info у фінальному прогоні без `-x`. Проєктні копії в `.claude/hooks/` синкаються звідси.
|
|
21
|
+
|
|
7
22
|
## [1.29.3] - 2026-05-29
|
|
8
23
|
|
|
9
24
|
### Changed
|
|
@@ -34,7 +49,6 @@
|
|
|
34
49
|
|
|
35
50
|
- **`rules/ci4/ci4.mdc`** (`version` 2.0 → 2.1) — додано секцію **«Зв'язок із `.cursor/rules`»**: архітектурна документація у `docs/` не дублює зміст `.cursor/rules/*.mdc` (операційні правила лінту, тестів, CHANGELOG, версіонування), а посилається на потрібне правило через його ім'я у бектиках (наприклад, `див. **`changelog`**`). Дублікати розходяться з оригіналом і ламають automatic-перевірки `npx @nitra/cursor fix`/`check`; правки робляться в одному місці — у самому `.mdc`.
|
|
36
51
|
|
|
37
|
-
|
|
38
52
|
## [1.28.8] - 2026-05-28
|
|
39
53
|
|
|
40
54
|
### Added
|
package/package.json
CHANGED
package/rules/bun/bun.mdc
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
description: Bun як єдиний package manager у монорепо
|
|
3
3
|
globs: "**/package.json,**/bunfig.toml,**/bun.lock,**/bun.lockb"
|
|
4
4
|
alwaysApply: false
|
|
5
|
-
version: '2.
|
|
5
|
+
version: '2.1'
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
Проект використовує тільки Bun для керування залежностями та запуску скриптів.
|
|
@@ -42,7 +42,7 @@ Lockfile у репозиторії: `bun.lock`.
|
|
|
42
42
|
- Якщо залежність потрібна лише одному пакету, додавати її в директорії цього пакета.
|
|
43
43
|
- У CI та локально запускати скрипти через `bun run`.
|
|
44
44
|
|
|
45
|
-
В кореневому `package.json` не повинно бути `dependencies`, а в `devDependencies` — тільки модулі `@nitra/*`. **Виняток (root-only)** — Vitest/Stryker peer/tools для `n-cursor coverage`: `vitest`, `@vitest/coverage-v8`, `@stryker-mutator/vitest-runner
|
|
45
|
+
В кореневому `package.json` не повинно бути `dependencies`, а в `devDependencies` — тільки модулі `@nitra/*`. **Виняток (root-only)** — Vitest/Stryker peer/tools для `n-cursor coverage`: `vitest`, `@vitest/coverage-v8`, `@stryker-mutator/vitest-runner`; а також `@playwright/test` для e2e-тестів. Тримати їх **у корені** доводиться у будь-якому монорепо-споживачі, бо правило `test` enabled завжди (`test/auto.md` = `завжди`), а класти ці пакети у workspace-и не можна: published пакети (`npm-module.mdc`) не мають мати `devDependencies`, а інші workspace-и однаково запускають coverage оркестратор з кореня. Якщо в package.json є поля `packageManager`, то прибрати їх, також прибрати всі директорії та файли для yarn.
|
|
46
46
|
|
|
47
47
|
- Заборонені top-level поля у root `package.json` (з причинами): [package.json.deny.json](./policy/package_json/template/package.json.deny.json)
|
|
48
48
|
|
|
@@ -68,7 +68,7 @@ deny contains msg if {
|
|
|
68
68
|
|
|
69
69
|
# ── helpers ────────────────────────────────────────────────────────────────
|
|
70
70
|
|
|
71
|
-
allowed_root_test_deps := {"vitest", "@vitest/coverage-v8", "@stryker-mutator/vitest-runner"}
|
|
71
|
+
allowed_root_test_deps := {"vitest", "@vitest/coverage-v8", "@stryker-mutator/vitest-runner", "@playwright/test"}
|
|
72
72
|
|
|
73
73
|
allowed_root_dev_dependency(name) if {
|
|
74
74
|
startswith(name, "@nitra/")
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
description: Використання pg / mysql2 / Bun SQL у Node.js та Bun
|
|
3
3
|
globs: "**/package.json,**/src/conn/**"
|
|
4
4
|
alwaysApply: false
|
|
5
|
-
version: '1.
|
|
5
|
+
version: '1.12'
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
## Підтримувані версії баз даних
|
|
@@ -276,6 +276,7 @@ const rows = await sql.unsafe(query, values)
|
|
|
276
276
|
| Динамічний `WHERE` (полів багато) | whitelist + ручні `$N` + `sql.unsafe(text, vals)` |
|
|
277
277
|
| Сирий migration / DDL | `sql.unsafe(text)` з `// allow-unsafe: <причина>` |
|
|
278
278
|
| User input як value | **тільки** Bun parameters / `$N` bind |
|
|
279
|
+
| Масив значень у `unnest(...)` | `sql.array(arr, type)` — обов'язково з типом |
|
|
279
280
|
|
|
280
281
|
Головне правило:
|
|
281
282
|
|
|
@@ -384,6 +385,73 @@ await sql.begin(async tx => {
|
|
|
384
385
|
})
|
|
385
386
|
```
|
|
386
387
|
|
|
388
|
+
## `sql.array(arr, type)` для передачі масивів
|
|
389
|
+
|
|
390
|
+
Коли JS-масив передається як параметр у Bun SQL template literal всередині `unnest(...)` або іншого контексту, де PostgreSQL очікує типізований масив (`int4[]`, `uuid[]` тощо), — обов'язково використовувати `sql.array(arr, type)` (або `pgWrite.array` / `pgRead.array` — вони є екземплярами `SQL`). Другий аргумент (тип елементів) — обов'язковий.
|
|
391
|
+
|
|
392
|
+
### Заборонені патерни
|
|
393
|
+
|
|
394
|
+
```javascript
|
|
395
|
+
// ❌ пряма підстановка масиву — Bun серіалізує як рядок, не як pg-масив
|
|
396
|
+
${ids}
|
|
397
|
+
|
|
398
|
+
// ❌ cast-синтаксис без .array() — працює в деяких версіях, але не гарантований
|
|
399
|
+
${ids}::int8[]
|
|
400
|
+
|
|
401
|
+
// ❌ відсутній тип — Bun не може вивести тип pg, можливий mismatch
|
|
402
|
+
sql.array(ids)
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### Дозволені патерни
|
|
406
|
+
|
|
407
|
+
```javascript
|
|
408
|
+
// ✅ pgWrite.array з явним типом
|
|
409
|
+
${pgWrite.array(ids, 'int8')}
|
|
410
|
+
${pgWrite.array(uuids, 'uuid')}
|
|
411
|
+
${pgWrite.array(flags, 'bool')}
|
|
412
|
+
${pgWrite.array(amounts, 'numeric')}
|
|
413
|
+
${pgWrite.array(names, 'text')}
|
|
414
|
+
${pgWrite.array(dates, 'date')}
|
|
415
|
+
${pgWrite.array(timestamps, 'timestamptz')}
|
|
416
|
+
|
|
417
|
+
// ✅ pgRead.array — те саме правило
|
|
418
|
+
${pgRead.array(ids, 'int4')}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### Таблиця типів
|
|
422
|
+
|
|
423
|
+
| JS-тип | PostgreSQL тип | Аргумент |
|
|
424
|
+
| ------------- | -------------- | --------------- |
|
|
425
|
+
| number (int) | int4 | `'int4'` |
|
|
426
|
+
| bigint / id | int8 | `'int8'` |
|
|
427
|
+
| UUID string | uuid | `'uuid'` |
|
|
428
|
+
| boolean | bool | `'bool'` |
|
|
429
|
+
| decimal/float | numeric | `'numeric'` |
|
|
430
|
+
| string | text | `'text'` |
|
|
431
|
+
| date string | date | `'date'` |
|
|
432
|
+
| ISO datetime | timestamptz | `'timestamptz'` |
|
|
433
|
+
|
|
434
|
+
### Повний приклад (UNNEST + MERGE)
|
|
435
|
+
|
|
436
|
+
```javascript
|
|
437
|
+
await pgWrite`
|
|
438
|
+
MERGE INTO "order".product p
|
|
439
|
+
USING (
|
|
440
|
+
SELECT * FROM unnest(
|
|
441
|
+
${pgWrite.array(rows.map(r => r.order_id), 'uuid')},
|
|
442
|
+
${pgWrite.array(rows.map(r => r.product_id), 'int4')},
|
|
443
|
+
${pgWrite.array(rows.map(r => r.qty), 'numeric')},
|
|
444
|
+
${pgWrite.array(rows.map(r => r.is_refund), 'bool')}
|
|
445
|
+
) AS s(order_id, product_id, qty, is_refund)
|
|
446
|
+
) AS s ON p.order_id = s.order_id AND p.product_id = s.product_id
|
|
447
|
+
WHEN MATCHED THEN
|
|
448
|
+
UPDATE SET qty = s.qty
|
|
449
|
+
WHEN NOT MATCHED THEN
|
|
450
|
+
INSERT (order_id, product_id, qty, is_refund)
|
|
451
|
+
VALUES (s.order_id, s.product_id, s.qty, s.is_refund)
|
|
452
|
+
`
|
|
453
|
+
```
|
|
454
|
+
|
|
387
455
|
## Коментар під час виправлення SQL injection
|
|
388
456
|
|
|
389
457
|
Коли виправляєш місце з потенційним **SQL injection** (наприклад, заміна конкатенації/`.join(',')` на `sql(ids)` або перехід з `sql.unsafe(...)` на tagged template), **додай поруч короткий коментар** з описом причини.
|