@nitra/cursor 1.29.4 → 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/CHANGELOG.md CHANGED
@@ -4,6 +4,15 @@
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
+
7
16
  ## [1.29.4] - 2026-05-29
8
17
 
9
18
  ### Fixed
@@ -40,7 +49,6 @@
40
49
 
41
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`.
42
51
 
43
-
44
52
  ## [1.28.8] - 2026-05-28
45
53
 
46
54
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "1.29.4",
3
+ "version": "1.29.5",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",
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.0'
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`. Тримати їх **у корені** доводиться у будь-якому монорепо-споживачі, бо правило `test` enabled завжди (`test/auto.md` = `завжди`), а класти ці пакети у workspace-и не можна: published пакети (`npm-module.mdc`) не мають мати `devDependencies`, а інші workspace-и однаково запускають coverage оркестратор з кореня. Якщо в package.json є поля `packageManager`, то прибрати їх, також прибрати всі директорії та файли для yarn.
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.11'
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), **додай поруч короткий коментар** з описом причини.