@nitra/cursor 1.8.160 → 1.8.163

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,27 @@
4
4
 
5
5
  Формат — [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), нумерація — [SemVer](https://semver.org/lang/uk/).
6
6
 
7
+ ## [1.8.163] - 2026-05-01
8
+
9
+ ### Changed
10
+
11
+ - `check-js-lint.mjs`: `ignorePatterns` у `.oxlintrc.json` тепер звіряється як `rules` — канонічні патерни мають бути присутні, додаткові локальні glob-и дозволені (раніше була строга рівність — будь-який зайвий запис зламував перевірку).
12
+ - `check-text.mjs`: `OXFMT_REQUIRED_IGNORE_PATTERNS` доповнено `**/auto-imports.d.ts` (узгоджено з каноном oxlint); перевірка `.oxfmtrc.json` уже працює як subset, тому локальні розширення не падають.
13
+ - `js-lint.mdc` / `n-js-lint.mdc`, `text.mdc` / `n-text.mdc`: документовано, що канон задає мінімум `ignorePatterns`, локальне розширення дозволене.
14
+
15
+ ## [1.8.162] - 2026-05-01
16
+
17
+ ### Changed
18
+
19
+ - `oxlint-canonical-skeleton.json` (та перебудований `oxlint-canonical.json`): `ignorePatterns` тепер містить `["**/schema.graphql", "**/auto-imports.d.ts"]` (узгоджено з `.oxfmtrc.json`). Споживачі мають синхронізувати корінь `.oxlintrc.json` із каноном — `check-js-lint` падатиме, поки масив не збігається.
20
+
21
+ ## [1.8.161] - 2026-05-01
22
+
23
+ ### Added
24
+
25
+ - `js-bun-db.mdc` (v1.5): нова секція «Прибирати pg-leftover виклики (`.connect()`, `.end()`)». У файлах з Bun SQL прапоруються `<obj>.connect(...)` і `<obj>.end(...)` як ручний lifecycle, який Bun SQL робить за тебе. Opt-out — маркер `// allow-pg-leftover: <причина>` (line- або block-коментар на тому ж рядку чи безпосередньо перед викликом).
26
+ - `bun-sql-scan.mjs`: новий сканер `findBunSqlPgLeftoverCallInText` (скоп — лише файли з `import { sql|SQL } from 'bun'`, щоб не давати false-positive на WebSocket/Stream `.end()`). Виділено спільний `hasMarkerCommentNear` для обох opt-in маркерів (`allow-unsafe`, `allow-pg-leftover`).
27
+
7
28
  ## [1.8.160] - 2026-05-01
8
29
 
9
30
  ### Changed
package/mdc/js-bun-db.mdc CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  description: Використання pg / mysql2 / Bun SQL у Node.js та Bun
3
3
  alwaysApply: true
4
- version: '1.4'
4
+ version: '1.5'
5
5
  ---
6
6
 
7
7
  ## Підтримувані версії баз даних
@@ -151,6 +151,34 @@ await sql`SELECT * FROM users WHERE id IN (${ids.join(',')})`
151
151
 
152
152
  Для динамічних списків — `sql([...])` або `sql(rows, 'colA', 'colB')`, **не** `.join(',')`.
153
153
 
154
+ ## Прибирати pg-leftover виклики (`.connect()`, `.end()`)
155
+
156
+ У файлах з Bun SQL (`import { sql, SQL } from 'bun'`) залишки від `pg` — `pool.connect()`, `client.end()`, `pool.end()` — мають бути видалені. Bun SQL пулом керує сам: на першому запиті підключається, idle/lifetime закриває за конфігом — окремий життєвий цикл вручну не потрібен.
157
+
158
+ ```javascript
159
+ // ❌ pg-leftover: ручний lifecycle, який Bun SQL робить за тебе
160
+ const client = await pool.connect()
161
+ try {
162
+ await client.query('...')
163
+ } finally {
164
+ await client.end()
165
+ }
166
+
167
+ // ✅ Bun SQL — без явних .connect()/.end()
168
+ await sql`...`
169
+ ```
170
+
171
+ Якщо виклик дійсно потрібен (наприклад, `sql.end()` у graceful shutdown або `.connect()` на сторонньому об'єкті, що випадково ділить імʼя методу), додай маркер `// allow-pg-leftover: <причина>` на тому ж рядку (trailing) або на рядку безпосередньо перед викликом:
172
+
173
+ ```javascript
174
+ // allow-pg-leftover: graceful shutdown — закриваємо пул перед exit
175
+ await sql.end()
176
+
177
+ ws.connect(url) // allow-pg-leftover: WebSocket, не pg
178
+ ```
179
+
180
+ Формат маркера: `allow-pg-leftover: <непорожня причина>` у line- або block-коментарі. Без маркера й без причини — **fail** перевірки.
181
+
154
182
  ## Що НЕ робити
155
183
 
156
184
  ### Не створювати підключення на кожен запит
package/mdc/js-lint.mdc CHANGED
@@ -30,7 +30,7 @@ version: '1.16'
30
30
  }
31
31
  ```
32
32
 
33
- У корені має бути **`.oxlintrc.json`**, який **збігається з каноном** oxlint з пакета **`@nitra/cursor`**: файл **`npm/scripts/utils/oxlint-canonical.json`** (plugins, jsPlugins з **`@e18e/eslint-plugin`**, categories, повний набір **rules** із канону — додаткові записи в **`rules`** дозволені; також **`settings`**, **`env`**, **`globals`**, **`ignorePatterns`**). Оновити можна з репозиторію пакета або скопіювавши файл після **`bun ./scripts/utils/rebuild-oxlint-canonical.mjs`** (джерело правил — **`oxlint-rules.tsv`** + скелет **`oxlint-canonical-skeleton.json`**). Модуль **`@e18e/eslint-plugin`** не оголошуй окремо в **`package.json`** — він уже в залежностях **`@nitra/eslint-config`** (з **3.8.0**), oxlint підвантажує його з **`node_modules`**.
33
+ У корені має бути **`.oxlintrc.json`**, який **збігається з каноном** oxlint з пакета **`@nitra/cursor`**: файл **`npm/scripts/utils/oxlint-canonical.json`** (plugins, jsPlugins з **`@e18e/eslint-plugin`**, categories, повний набір **rules** із канону — додаткові записи в **`rules`** дозволені; також **`settings`**, **`env`**, **`globals`**). Поле **`ignorePatterns`** працює як **`rules`**: канонічні патерни з **`oxlint-canonical.json`** (наразі **`**/schema.graphql`**, **`**/auto-imports.d.ts`**) мають бути присутні, додаткові локальні glob-и дозволені. Оновити канон можна з репозиторію пакета або скопіювавши файл після **`bun ./scripts/utils/rebuild-oxlint-canonical.mjs`** (джерело правил — **`oxlint-rules.tsv`** + скелет **`oxlint-canonical-skeleton.json`**). Модуль **`@e18e/eslint-plugin`** не оголошуй окремо в **`package.json`** — він уже в залежностях **`@nitra/eslint-config`** (з **3.8.0**), oxlint підвантажує його з **`node_modules`**.
34
34
 
35
35
  Мінімум для розуміння структури (реальний корінь конфігу має збігатися з каноном повністю):
36
36
 
package/mdc/text.mdc CHANGED
@@ -82,7 +82,7 @@ version: '1.25'
82
82
 
83
83
  ```json title=".oxfmtrc.json"
84
84
  {
85
- "ignorePatterns": ["**/hasura/metadata/**", "**/schema.graphql"],
85
+ "ignorePatterns": ["**/hasura/metadata/**", "**/schema.graphql", "**/auto-imports.d.ts"],
86
86
  "arrowParens": "avoid",
87
87
  "printWidth": 120,
88
88
  "bracketSpacing": true,
@@ -104,7 +104,7 @@ version: '1.25'
104
104
  }
105
105
  ```
106
106
 
107
- Поле **`ignorePatterns`** обовʼязкове: у масиві мають бути **`**/hasura/metadata/**`** та **`**/schema.graphql`**; інші glob-и додавай за потреби (згенеровані каталоги тощо).
107
+ Поле **`ignorePatterns`** обовʼязкове: у масиві мають бути **`**/hasura/metadata/**`**, **`**/schema.graphql`** і **`**/auto-imports.d.ts`**; інші glob-и додавай за потреби (згенеровані каталоги тощо) — канон задає мінімум, локальні розширення дозволені.
108
108
 
109
109
  Також потрібно прибрати, якщо є в проєкті, модуль **`@nitra/prettier-config`**, **prettier** та всі виклики prettier і налаштування для нього.
110
110
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "1.8.160",
3
+ "version": "1.8.163",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",
@@ -47,5 +47,8 @@
47
47
  "engines": {
48
48
  "bun": ">=1.3",
49
49
  "node": ">=25"
50
+ },
51
+ "devDependencies": {
52
+ "@nitra/cursor": "^1.8.163"
50
53
  }
51
54
  }
@@ -15,6 +15,9 @@
15
15
  * допустимий лише для підстановки назви таблиці/колонки чи dynamic SQL/DDL,
16
16
  * коли значення контролюється кодом (не user input) — в інших випадках
17
17
  * переробляємо на tagged template `sql\`...\${value}...\``.
18
+ * - pg-leftover виклики `<obj>.connect(...)` / `<obj>.end(...)` у файлах, що
19
+ * імпортують Bun SQL: пулом керує Bun, життєвий цикл вручну не потрібен.
20
+ * Opt-out — маркер `// allow-pg-leftover: <reason>`.
18
21
  * - Динамічні SQL-списки через `.join(',')` у `IN (...)` / `VALUES (...)`
19
22
  * (треба `sql([...])`).
20
23
  */
@@ -25,6 +28,7 @@ import { join, relative, sep } from 'node:path'
25
28
  import { createCheckReporter } from './utils/check-reporter.mjs'
26
29
  import {
27
30
  findBunSqlPerRequestConnectionInText,
31
+ findBunSqlPgLeftoverCallInText,
28
32
  findBunSqlUnsafeUseWithoutAllowMarkerInText,
29
33
  findUnsafeBunSqlDynamicSqlListInText,
30
34
  findUnsafeBunSqlInListMissingEmptyGuardInText,
@@ -128,7 +132,7 @@ async function checkForbiddenDependencies(pkgJsonPaths, repoRoot, reporter) {
128
132
  */
129
133
  async function scanSourcesForBunSqlPatterns(sourcePaths, repoRoot, reporter) {
130
134
  const { fail } = reporter
131
- const counts = { perRequest: 0, unsafeCall: 0, dynamicList: 0, inListGuard: 0 }
135
+ const counts = { perRequest: 0, unsafeCall: 0, dynamicList: 0, inListGuard: 0, pgLeftover: 0 }
132
136
  let hasBunSqlImport = false
133
137
 
134
138
  for (const absPath of sourcePaths) {
@@ -148,7 +152,7 @@ async function scanSourcesForBunSqlPatterns(sourcePaths, repoRoot, reporter) {
148
152
  * @param {string} content вміст файлу
149
153
  * @param {string} rel posix-шлях відносно `repoRoot`
150
154
  * @param {(msg: string) => void} fail callback при помилці
151
- * @param {{ perRequest: number, unsafeCall: number, dynamicList: number, inListGuard: number }} counts акумулятори
155
+ * @param {{ perRequest: number, unsafeCall: number, dynamicList: number, inListGuard: number, pgLeftover: number }} counts акумулятори
152
156
  * @returns {void}
153
157
  */
154
158
  function scanFileForBunSqlPatterns(content, rel, fail, counts) {
@@ -169,6 +173,15 @@ function scanFileForBunSqlPatterns(content, rel, fail, counts) {
169
173
  `(js-bun-db.mdc): ${v.snippet}`
170
174
  )
171
175
  }
176
+ for (const v of findBunSqlPgLeftoverCallInText(content, rel)) {
177
+ counts.pgLeftover++
178
+ fail(
179
+ `js-bun-db: ${rel}:${v.line} — pg-leftover виклик .${v.methodName}(...): Bun SQL пулом керує сам, ` +
180
+ `видали зайвий .connect()/.end() або, якщо випадок легітимний (graceful shutdown тощо), ` +
181
+ `додай маркер "// allow-pg-leftover: <причина>" на тому ж рядку або рядком вище ` +
182
+ `(js-bun-db.mdc): ${v.snippet}`
183
+ )
184
+ }
172
185
  for (const v of findUnsafeBunSqlDynamicSqlListInText(content, rel)) {
173
186
  counts.dynamicList++
174
187
  fail(
@@ -237,11 +250,8 @@ export async function check() {
237
250
  return reporter.getExitCode()
238
251
  }
239
252
 
240
- const { hasBunSqlImport, perRequest, unsafeCall, dynamicList, inListGuard } = await scanSourcesForBunSqlPatterns(
241
- sourcePaths,
242
- repoRoot,
243
- reporter
244
- )
253
+ const { hasBunSqlImport, perRequest, unsafeCall, dynamicList, inListGuard, pgLeftover } =
254
+ await scanSourcesForBunSqlPatterns(sourcePaths, repoRoot, reporter)
245
255
 
246
256
  if (!hasBunSqlImport) {
247
257
  pass("js-bun-db: Bun SQL не використовується в коді (немає import { sql|SQL } from 'bun')")
@@ -254,6 +264,12 @@ export async function check() {
254
264
  if (unsafeCall === 0) {
255
265
  pass('js-bun-db: усі sql.unsafe(...) або відсутні, або супроводжуються маркером "// allow-unsafe: <причина>"')
256
266
  }
267
+ if (pgLeftover === 0) {
268
+ pass(
269
+ 'js-bun-db: немає pg-leftover викликів .connect()/.end() у файлах з Bun SQL ' +
270
+ '(або всі вони мають маркер "// allow-pg-leftover: <причина>")'
271
+ )
272
+ }
257
273
  if (dynamicList === 0) {
258
274
  pass("js-bun-db: немає небезпечних динамічних SQL-списків через .join(',') у IN/VALUES")
259
275
  }
@@ -133,6 +133,30 @@ function compareOxlintRules(expected, actual, failures) {
133
133
  }
134
134
  }
135
135
 
136
+ /**
137
+ * Звіряє блок `ignorePatterns`: кожен патерн із канону має бути присутній в actual; додаткові локальні
138
+ * патерни дозволені (канон задає мінімум, проєкт може розширити).
139
+ * @param {unknown} expected канонічний масив `ignorePatterns`
140
+ * @param {unknown} actual поточний `ignorePatterns` із `.oxlintrc.json`
141
+ * @param {string[]} failures буфер для помилок
142
+ */
143
+ function compareOxlintIgnorePatterns(expected, actual, failures) {
144
+ if (!Array.isArray(expected)) {
145
+ return
146
+ }
147
+ if (!Array.isArray(actual)) {
148
+ failures.push('.oxlintrc.json: поле "ignorePatterns" має бути масивом (канон задає мінімум, додаткові патерни дозволені)')
149
+ return
150
+ }
151
+ const set = new Set(actual)
152
+ const missing = expected.filter(p => !set.has(p))
153
+ if (missing.length > 0) {
154
+ failures.push(
155
+ `.oxlintrc.json: ignorePatterns має містити канонічні патерни — додай: ${missing.map(p => JSON.stringify(p)).join(', ')}`
156
+ )
157
+ }
158
+ }
159
+
136
160
  /**
137
161
  * Перевіряє `.oxlintrc.json` проти канону пакета `@nitra/cursor` (усі правила з канону та інші поля з `oxlint-canonical.json`).
138
162
  * Додаткові ключі лише в `rules` дозволені; інші поля мають збігатися з каноном.
@@ -160,6 +184,11 @@ export function verifyOxlintRcAgainstCanonical(cfg, canonical) {
160
184
  continue
161
185
  }
162
186
 
187
+ if (key === 'ignorePatterns') {
188
+ compareOxlintIgnorePatterns(expected, actual, failures)
189
+ continue
190
+ }
191
+
163
192
  if (!deepEqualOxlintCanonical(actual, expected)) {
164
193
  failures.push(
165
194
  `.oxlintrc.json: поле "${key}" має збігатися з каноном пакета @nitra/cursor (npm/scripts/utils/oxlint-canonical.json)`
@@ -29,8 +29,8 @@ const SEMVER_RE = /^(\d+)\.(\d+)\.(\d+)/
29
29
  /** Заголовок абзацу про апостроф у text.mdc / n-text.mdc. */
30
30
  const UK_APOSTROPHE_HEADING = '**Український апостроф:**'
31
31
 
32
- /** Мінімальні glob-и в `ignorePatterns` у `.oxfmtrc.json` (text.mdc). */
33
- const OXFMT_REQUIRED_IGNORE_PATTERNS = ['**/hasura/metadata/**', '**/schema.graphql']
32
+ /** Мінімальні glob-и в `ignorePatterns` у `.oxfmtrc.json` (text.mdc) — додаткові патерни локально дозволені. */
33
+ const OXFMT_REQUIRED_IGNORE_PATTERNS = ['**/hasura/metadata/**', '**/schema.graphql', '**/auto-imports.d.ts']
34
34
 
35
35
  /** Канонічні записи `ignorePaths` у `.cspell.json` (text.mdc) — кожен має бути присутнім. */
36
36
  const CSPELL_REQUIRED_IGNORE_PATHS = [
@@ -211,7 +211,7 @@ async function checkOxfmtRc(passFn, failFn) {
211
211
  const set = new Set(cfg.ignorePatterns)
212
212
  const missingPatterns = OXFMT_REQUIRED_IGNORE_PATTERNS.filter(p => !set.has(p))
213
213
  if (missingPatterns.length === 0) {
214
- passFn('.oxfmtrc.json: ignorePatterns містить hasura/metadata та schema.graphql')
214
+ passFn('.oxfmtrc.json: ignorePatterns містить hasura/metadata, schema.graphql і auto-imports.d.ts')
215
215
  } else {
216
216
  failFn(
217
217
  `.oxfmtrc.json ignorePatterns: додай відсутні елементи: ${missingPatterns.join(', ')} (канонічний приклад у text.mdc)`
@@ -34,6 +34,14 @@ const IN_PLACEHOLDER_END_RE = /\bin\s*(\(\s*)?$/iu
34
34
  // `// allow-unsafe: <reason>` — `allow-unsafe`, двокрапка, **непорожня** причина.
35
35
  // Без причини маркер не приймається: ціль — лишити слід для ревʼюера, а не «німий» прапорець.
36
36
  const ALLOW_UNSAFE_MARKER_RE = /\ballow-unsafe\s*:\s*\S+/u
37
+ // `// allow-pg-leftover: <reason>` — opt-in для виправданих `.connect()` / `.end()`
38
+ // у файлах з Bun SQL (наприклад, `sql.end()` у graceful shutdown або `.connect()`
39
+ // на сторонньому об'єкті, що випадково ділить імʼя методу з `pg`).
40
+ const ALLOW_PG_LEFTOVER_MARKER_RE = /\ballow-pg-leftover\s*:\s*\S+/u
41
+ // pg-API, які не потрібні з Bun SQL: pool/client життєвий цикл вручну.
42
+ // `release` не входить — Bun SQL такого методу не має, а `.connect()` / `.end()`
43
+ // формально існують і там, тому опт-аут маркером лишається доречним.
44
+ const PG_LEFTOVER_METHOD_NAMES = new Set(['connect', 'end'])
37
45
 
38
46
  /**
39
47
  * @param {unknown} node AST node
@@ -214,24 +222,26 @@ function isUnsafeCall(node) {
214
222
  }
215
223
 
216
224
  /**
217
- * Чи є біля виклику `<obj>.unsafe(...)` маркер-коментар `// allow-unsafe: <reason>`
218
- * (або `/* allow-unsafe: <reason> *\/`) на тому ж рядку, що й початок виклику,
219
- * або на рядку, що передує початку виклику. Це навмисно строга суміжність:
220
- * відірваний коментар через порожній рядок не зараховується — щоб маркер
225
+ * Чи є біля виклику маркер-коментар, що матчиться на `markerRe`, на тому ж рядку,
226
+ * що й початок виклику, або на рядку, що передує початку виклику. Це навмисно строга
227
+ * суміжність: відірваний коментар через порожній рядок не зараховується щоб маркер
221
228
  * стояв саме біля виклику, а не «загубився десь вище».
222
- * @param {{ start: number }} callNode виклик `<obj>.unsafe(...)`
229
+ *
230
+ * Використовується для opt-in маркерів типу `// allow-unsafe: ...` і `// allow-pg-leftover: ...`.
231
+ * @param {{ start: number }} callNode виклик
223
232
  * @param {{ type: 'Line' | 'Block', value: string, start: number, end: number }[]} comments коментарі з парсера
224
233
  * @param {string} content вихідний код
234
+ * @param {RegExp} markerRe регекс, що валідує текст маркера в `comment.value`
225
235
  * @returns {boolean} true, якщо маркер знайдено
226
236
  */
227
- function hasAllowUnsafeMarkerNear(callNode, comments, content) {
237
+ function hasMarkerCommentNear(callNode, comments, content, markerRe) {
228
238
  const callStartLine = offsetToLine(content, callNode.start)
229
239
  for (const c of comments) {
230
240
  if (!c || (c.type !== 'Line' && c.type !== 'Block')) continue
231
- if (typeof c.value !== 'string' || !ALLOW_UNSAFE_MARKER_RE.test(c.value)) continue
241
+ if (typeof c.value !== 'string' || !markerRe.test(c.value)) continue
232
242
  const startLine = offsetToLine(content, c.start)
233
243
  const endLine = offsetToLine(content, c.end)
234
- // trailing-коментар на тому ж рядку (`sql.unsafe(...) // allow-unsafe: ...`)
244
+ // trailing-коментар на тому ж рядку (`<call> // marker: ...`)
235
245
  if (startLine === callStartLine) return true
236
246
  // коментар на рядку, що безпосередньо передує виклику — для блокових
237
247
  // коментарів важливим є саме `endLine`, бо block може займати кілька рядків.
@@ -240,6 +250,23 @@ function hasAllowUnsafeMarkerNear(callNode, comments, content) {
240
250
  return false
241
251
  }
242
252
 
253
+ /**
254
+ * Чи це pg-leftover виклик: `<obj>.connect(...)` або `<obj>.end(...)`. Bun SQL пулом
255
+ * керує сам — і `.connect()`, і `.end()` у файлах з Bun SQL зазвичай зайві, тож такі
256
+ * виклики прапоруються (з opt-in маркером для рідкісних легітимних випадків).
257
+ * @param {unknown} node AST node
258
+ * @returns {{ name: 'connect' | 'end' } | null} ім'я pg-leftover методу або null
259
+ */
260
+ function asPgLeftoverCall(node) {
261
+ if (!node || node.type !== 'CallExpression') return null
262
+ const callee = node.callee
263
+ if (!callee || callee.type !== 'MemberExpression' || callee.computed) return null
264
+ const prop = callee.property
265
+ if (!prop || prop.type !== 'Identifier' || typeof prop.name !== 'string') return null
266
+ if (!PG_LEFTOVER_METHOD_NAMES.has(prop.name)) return null
267
+ return { name: /** @type {'connect' | 'end'} */ (prop.name) }
268
+ }
269
+
243
270
  /**
244
271
  * Знаходить `new SQL(...)` всередині функцій (handler на кожен запит замість singleton).
245
272
  * @param {string} content вихідний код
@@ -286,7 +313,7 @@ export function findBunSqlUnsafeUseWithoutAllowMarkerInText(content, virtualPath
286
313
  const out = []
287
314
  walkAstWithAncestors(program, [], node => {
288
315
  if (!isUnsafeCall(node)) return
289
- if (hasAllowUnsafeMarkerNear(node, comments, content)) return
316
+ if (hasMarkerCommentNear(node, comments, content, ALLOW_UNSAFE_MARKER_RE)) return
290
317
  out.push({
291
318
  line: offsetToLine(content, node.start),
292
319
  snippet: normalizeSnippet(content.slice(node.start, node.end))
@@ -295,6 +322,39 @@ export function findBunSqlUnsafeUseWithoutAllowMarkerInText(content, virtualPath
295
322
  return out
296
323
  }
297
324
 
325
+ /**
326
+ * Знаходить pg-leftover виклики `<obj>.connect(...)` / `<obj>.end(...)` без маркера
327
+ * `// allow-pg-leftover: <reason>` у файлах, де **в цьому ж файлі** є `import { sql|SQL } from 'bun'`.
328
+ *
329
+ * Скоп навмисно вузький: ці метод-імена занадто загальні (WebSocket, Stream, інші бібліотеки),
330
+ * тож сканер обмежений файлами, що вже використовують Bun SQL — там pg-залишок є явним
331
+ * багом міграції. У не-Bun-SQL файлах прапоратися не буде, навіть якщо проєкт у цілому
332
+ * мігрував.
333
+ * @param {string} content вихідний код
334
+ * @param {string} [virtualPath] шлях для вибору `lang`
335
+ * @returns {{ line: number, snippet: string, methodName: 'connect' | 'end' }[]} список порушень
336
+ */
337
+ export function findBunSqlPgLeftoverCallInText(content, virtualPath = 'scan.ts') {
338
+ if (!textHasBunSqlImport(content)) return []
339
+ const parsed = parseProgramAndCommentsOrNull(content, virtualPath)
340
+ if (!parsed) return []
341
+ const { program, comments } = parsed
342
+
343
+ /** @type {{ line: number, snippet: string, methodName: 'connect' | 'end' }[]} */
344
+ const out = []
345
+ walkAstWithAncestors(program, [], node => {
346
+ const m = asPgLeftoverCall(node)
347
+ if (!m) return
348
+ if (hasMarkerCommentNear(node, comments, content, ALLOW_PG_LEFTOVER_MARKER_RE)) return
349
+ out.push({
350
+ line: offsetToLine(content, node.start),
351
+ snippet: normalizeSnippet(content.slice(node.start, node.end)),
352
+ methodName: m.name
353
+ })
354
+ })
355
+ return out
356
+ }
357
+
298
358
  /**
299
359
  * Знаходить динамічні SQL-списки у TaggedTemplateExpression / TemplateLiteral в контексті
300
360
  * `IN (...)` або `VALUES (...)`, де серед expressions є виклик `.join(...)`.
@@ -23,5 +23,5 @@
23
23
  "builtin": true
24
24
  },
25
25
  "globals": {},
26
- "ignorePatterns": []
26
+ "ignorePatterns": ["**/schema.graphql", "**/auto-imports.d.ts"]
27
27
  }
@@ -1,7 +1,17 @@
1
1
  {
2
2
  "$schema": "./node_modules/oxlint/configuration_schema.json",
3
- "plugins": ["unicorn", "oxc", "import", "jsdoc", "promise", "node", "vue"],
4
- "jsPlugins": ["@e18e/eslint-plugin"],
3
+ "plugins": [
4
+ "unicorn",
5
+ "oxc",
6
+ "import",
7
+ "jsdoc",
8
+ "promise",
9
+ "node",
10
+ "vue"
11
+ ],
12
+ "jsPlugins": [
13
+ "@e18e/eslint-plugin"
14
+ ],
5
15
  "categories": {},
6
16
  "rules": {
7
17
  "e18e/prefer-includes": "error",
@@ -383,5 +393,8 @@
383
393
  "builtin": true
384
394
  },
385
395
  "globals": {},
386
- "ignorePatterns": []
396
+ "ignorePatterns": [
397
+ "**/schema.graphql",
398
+ "**/auto-imports.d.ts"
399
+ ]
387
400
  }