@nitra/cursor 12.8.9 → 12.10.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/.claude-template/settings.template.json +1 -23
- package/CHANGELOG.md +14 -1
- package/bin/n-cursor.js +21 -48
- package/docs/stryker.config.md +0 -2
- package/lib/docs/llm.md +16 -21
- package/lib/docs/omlx.md +20 -25
- package/lib/llm.mjs +25 -7
- package/lib/omlx.mjs +10 -3
- package/package.json +5 -5
- package/rules/abie/docs/index.md +0 -1
- package/rules/abie/docs/main.md +0 -2
- package/rules/abie/lib/docs/http-route.md +9 -12
- package/rules/abie/lib/http-route.mjs +3 -0
- package/rules/abie/main.mdc +0 -22
- package/rules/abie/policy/health_check_policy/health_check_policy.mdc +3 -1
- package/rules/abie/policy/health_check_policy/health_check_policy.rego +27 -0
- package/rules/adr/docs/index.md +0 -1
- package/rules/adr/docs/main.md +0 -2
- package/rules/adr/js/madr_format.mdc +13 -1
- package/rules/adr/main.mdc +0 -9
- package/rules/bun/docs/index.md +0 -1
- package/rules/bun/docs/main.md +0 -2
- package/rules/bun/main.mdc +1 -15
- package/rules/bun/policy/package_json/package_json.rego +12 -0
- package/rules/capacitor/docs/index.md +0 -1
- package/rules/capacitor/docs/main.md +0 -2
- package/rules/capacitor/main.mdc +0 -6
- package/rules/changelog/docs/index.md +0 -1
- package/rules/changelog/docs/main.md +0 -2
- package/rules/changelog/js/agent-workflow.mdc +1 -1
- package/rules/changelog/js/consistency.mjs +3 -3
- package/rules/changelog/js/docs/consistency.md +18 -23
- package/rules/changelog/main.mdc +0 -5
- package/rules/ci4/docs/index.md +0 -1
- package/rules/ci4/docs/main.md +0 -2
- package/rules/ci4/main.mdc +0 -5
- package/rules/doc-files/docs/index.md +0 -1
- package/rules/doc-files/docs/main.md +7 -11
- package/rules/doc-files/js/docs/docgen-crc.md +0 -2
- package/rules/doc-files/js/docs/docgen-extract.md +0 -2
- package/rules/doc-files/js/docs/docgen-files-batch.md +0 -2
- package/rules/doc-files/js/docs/docgen-gen.md +0 -2
- package/rules/doc-files/js/docs/docgen-judge-measure.md +0 -2
- package/rules/doc-files/js/docs/docgen-judge.md +0 -2
- package/rules/doc-files/js/docs/docgen-scan.md +0 -2
- package/rules/doc-files/js/docs/run-lint.md +0 -2
- package/rules/doc-files/main.mjs +2 -3
- package/rules/docker/docs/index.md +0 -1
- package/rules/docker/docs/main.md +0 -2
- package/rules/docker/js/docs/lint.md +0 -2
- package/rules/docker/lib/docs/docker-hadolint.md +0 -2
- package/rules/docker/main.mdc +1 -21
- package/rules/efes/docs/index.md +0 -1
- package/rules/efes/docs/main.md +0 -2
- package/rules/efes/main.mdc +0 -1
- package/rules/feedback/docs/index.md +0 -1
- package/rules/feedback/docs/main.md +0 -2
- package/rules/ga/docs/index.md +0 -1
- package/rules/ga/docs/main.md +0 -2
- package/rules/ga/js/docs/index.md +0 -1
- package/rules/ga/main.mdc +1 -31
- package/rules/graphql/docs/index.md +0 -1
- package/rules/graphql/docs/main.md +0 -2
- package/rules/graphql/main.mdc +0 -5
- package/rules/hasura/docs/index.md +0 -1
- package/rules/hasura/docs/main.md +0 -2
- package/rules/hasura/js/docs/index.md +1 -0
- package/rules/hasura/js/docs/migrations.md +28 -0
- package/rules/hasura/js/migrations.mjs +47 -0
- package/rules/hasura/main.mdc +1 -11
- package/rules/image-avif/docs/index.md +0 -1
- package/rules/image-avif/docs/main.md +0 -2
- package/rules/image-avif/main.mdc +1 -9
- package/rules/image-compress/docs/index.md +0 -1
- package/rules/image-compress/docs/main.md +0 -2
- package/rules/image-compress/js/docs/index.md +0 -1
- package/rules/image-compress/main.mdc +1 -9
- package/rules/js/docs/index.md +0 -1
- package/rules/js/docs/main.md +0 -2
- package/rules/js/js/dep-policy.mjs +91 -0
- package/rules/js/js/docs/check.md +0 -2
- package/rules/js/js/docs/dep-policy.md +34 -0
- package/rules/js/js/docs/index.md +5 -4
- package/rules/js/js/docs/tooling.md +0 -2
- package/rules/js/js/docs/utils_imports.md +0 -2
- package/rules/js/main.mdc +0 -31
- package/rules/js/policy/package_json/package_json.rego +16 -0
- package/rules/js-bun-db/docs/index.md +0 -1
- package/rules/js-bun-db/docs/main.md +0 -2
- package/rules/js-bun-db/js/docs/safety.md +18 -23
- package/rules/js-bun-db/js/safety.mjs +31 -3
- package/rules/js-bun-db/lib/bun-sql-scan.mjs +123 -0
- package/rules/js-bun-db/lib/docs/bun-sql-scan.md +37 -331
- package/rules/js-bun-db/main.mdc +1 -23
- package/rules/js-bun-redis/docs/index.md +0 -1
- package/rules/js-bun-redis/docs/main.md +0 -2
- package/rules/js-bun-redis/main.mdc +0 -5
- package/rules/js-mssql/docs/index.md +0 -1
- package/rules/js-mssql/docs/main.md +0 -2
- package/rules/js-mssql/main.mdc +0 -12
- package/rules/js-run/docs/index.md +0 -1
- package/rules/js-run/docs/main.md +0 -2
- package/rules/js-run/js/docs/runtime.md +15 -13
- package/rules/js-run/js/runtime.mjs +48 -4
- package/rules/js-run/main.mdc +0 -25
- package/rules/k8s/docs/index.md +0 -1
- package/rules/k8s/docs/main.md +0 -2
- package/rules/k8s/main.mdc +0 -45
- package/rules/nginx-default-tpl/docs/index.md +0 -1
- package/rules/nginx-default-tpl/docs/main.md +0 -2
- package/rules/nginx-default-tpl/main.mdc +0 -13
- package/rules/npm-module/docs/index.md +0 -1
- package/rules/npm-module/docs/main.md +0 -2
- package/rules/npm-module/js/docs/header_doc_pointer.md +0 -2
- package/rules/npm-module/js/docs/rule_meta.md +0 -2
- package/rules/npm-module/js/docs/skill_meta.md +0 -2
- package/rules/npm-module/main.mdc +1 -15
- package/rules/php/docs/index.md +0 -1
- package/rules/php/docs/main.md +0 -2
- package/rules/php/js/docs/index.md +0 -1
- package/rules/php/main.mdc +1 -9
- package/rules/python/docs/index.md +0 -1
- package/rules/python/docs/main.md +0 -2
- package/rules/python/js/docs/index.md +0 -1
- package/rules/python/main.mdc +1 -13
- package/rules/rego/docs/index.md +0 -1
- package/rules/rego/docs/main.md +0 -2
- package/rules/rego/js/docs/index.md +1 -1
- package/rules/rego/js/docs/tooling.md +26 -0
- package/rules/rego/js/tooling.mdc +14 -0
- package/rules/rego/js/tooling.mjs +24 -0
- package/rules/rego/main.mdc +0 -9
- package/rules/rego/policy/package_json/package_json.mdc +12 -0
- package/rules/rego/policy/package_json/package_json.rego +21 -0
- package/rules/rego/policy/package_json/target.json +4 -0
- package/rules/release/docs/index.md +0 -1
- package/rules/release/docs/main.md +0 -2
- package/rules/release/main.mdc +2 -2
- package/rules/rust/docs/index.md +0 -1
- package/rules/rust/docs/main.md +0 -2
- package/rules/rust/js/docs/index.md +0 -1
- package/rules/rust/main.mdc +1 -11
- package/rules/rust/policy/lint_rust_yml/lint_rust_yml.rego +24 -0
- package/rules/rust/policy/package_json/package_json.mdc +12 -0
- package/rules/rust/policy/package_json/package_json.rego +20 -0
- package/rules/rust/policy/package_json/target.json +4 -0
- package/rules/security/docs/index.md +0 -1
- package/rules/security/docs/main.md +0 -2
- package/rules/security/js/docs/index.md +0 -1
- package/rules/security/main.mdc +0 -13
- package/rules/style/docs/index.md +0 -1
- package/rules/style/docs/main.md +0 -2
- package/rules/style/js/docs/index.md +0 -1
- package/rules/style/js/docs/tooling.md +12 -10
- package/rules/style/js/tooling.mjs +8 -2
- package/rules/style/main.mdc +1 -23
- package/rules/style/policy/lint_style_yml/lint_style_yml.rego +5 -0
- package/rules/tauri/docs/index.md +0 -1
- package/rules/tauri/docs/main.md +0 -2
- package/rules/tauri/main.mdc +1 -11
- package/rules/test/docs/index.md +0 -1
- package/rules/test/docs/main.md +0 -2
- package/rules/test/js/docs/index.md +2 -0
- package/rules/test/js/docs/no-console-store-restore.md +30 -0
- package/rules/test/js/docs/sandbox-aware-test.md +30 -0
- package/rules/test/js/docs/stryker_config.md +0 -2
- package/rules/test/js/docs/vitest-config-pool-forks.md +0 -2
- package/rules/test/js/no-console-store-restore.mjs +88 -0
- package/rules/test/js/sandbox-aware-test.mjs +89 -0
- package/rules/test/main.mdc +1 -21
- package/rules/text/docs/index.md +0 -1
- package/rules/text/docs/main.md +0 -2
- package/rules/text/js/docs/cspell-fix.md +0 -2
- package/rules/text/js/docs/run-dotenv-linter.md +0 -2
- package/rules/text/js/docs/run-shellcheck.md +0 -2
- package/rules/text/js/docs/run-v8r.md +0 -2
- package/rules/text/main.mdc +0 -33
- package/rules/tool-surface/docs/index.md +0 -1
- package/rules/tool-surface/docs/main.md +0 -2
- package/rules/vue/docs/index.md +0 -1
- package/rules/vue/docs/main.md +0 -2
- package/rules/vue/main.mdc +0 -22
- package/rules/worktree/docs/index.md +0 -1
- package/rules/worktree/docs/main.md +0 -2
- package/scripts/docs/auto-rules.md +0 -2
- package/scripts/docs/auto-skills.md +0 -2
- package/scripts/docs/hook.md +30 -0
- package/scripts/docs/index.md +1 -2
- package/scripts/docs/post-tool-use-check.md +0 -2
- package/scripts/docs/sync-claude-config.md +1 -3
- package/scripts/docs/sync-setup-bun-deps-action.md +0 -2
- package/scripts/hook.mjs +71 -0
- package/scripts/lib/docs/check-mdc-template-refs.md +0 -2
- package/scripts/lib/docs/index.md +35 -36
- package/scripts/lib/docs/inline-template-links.md +6 -8
- package/scripts/lib/docs/list-project-rules-mdc.md +0 -2
- package/scripts/lib/docs/list-rule-ids.md +0 -2
- package/scripts/lib/docs/mirror-parity.md +8 -10
- package/scripts/lib/docs/read-n-cursor-config-lite.md +0 -2
- package/scripts/lib/docs/rule-meta.md +0 -2
- package/scripts/lib/docs/run-lint.md +9 -10
- package/scripts/lib/docs/run-rule-cli.md +0 -2
- package/scripts/lib/docs/run-rule.md +7 -9
- package/scripts/lib/docs/run-standard-lint.md +0 -2
- package/scripts/lib/docs/run-standard-rule.md +0 -2
- package/scripts/lib/docs/skill-meta.md +0 -2
- package/scripts/lib/docs/timing-summary.md +0 -2
- package/scripts/lib/docs/worktree-notice.md +0 -2
- package/scripts/lib/fix/docs/analyze-escalation.md +0 -2
- package/scripts/lib/fix/docs/index.md +10 -10
- package/scripts/lib/fix/docs/llm-worker.md +18 -8
- package/scripts/lib/fix/docs/orchestrator.md +44 -20
- package/scripts/lib/fix/docs/run-conformance-check.md +0 -2
- package/scripts/lib/fix/docs/t0.md +0 -2
- package/scripts/lib/fix/docs/verbose-block.md +27 -0
- package/scripts/lib/fix/llm-worker.mjs +75 -22
- package/scripts/lib/fix/orchestrator.mjs +13 -3
- package/scripts/lib/fix/verbose-block.mjs +82 -0
- package/scripts/lib/inline-template-links.mjs +32 -22
- package/scripts/lib/mirror-parity.mjs +2 -2
- package/scripts/lib/run-lint.mjs +15 -2
- package/scripts/lib/run-rule.mjs +1 -2
- package/scripts/sync-claude-config.mjs +7 -4
- package/scripts/utils/docs/resolve-js-root.md +0 -2
- package/skills/adr-normalize/SKILL.md +1 -0
- package/skills/coverage-fix/SKILL.md +1 -0
- package/skills/doc-aggregate/SKILL.md +1 -0
- package/skills/doc-files/SKILL.md +10 -24
- package/skills/lint/SKILL.md +24 -19
- package/skills/llm-patch/SKILL.md +5 -4
- package/skills/publish-telegram/SKILL.md +1 -0
- package/skills/start-check/SKILL.md +1 -0
- package/skills/taze/SKILL.md +3 -2
- package/types/bin/n-cursor.d.ts +1 -1
- package/rules/abie/docs/fix.md +0 -37
- package/rules/adr/docs/fix.md +0 -37
- package/rules/bun/docs/fix.md +0 -30
- package/rules/capacitor/docs/fix.md +0 -36
- package/rules/changelog/docs/fix.md +0 -37
- package/rules/ci4/docs/fix.md +0 -32
- package/rules/doc-files/docs/fix.md +0 -29
- package/rules/docker/docs/fix.md +0 -35
- package/rules/efes/docs/fix.md +0 -37
- package/rules/feedback/docs/fix.md +0 -30
- package/rules/ga/docs/fix.md +0 -30
- package/rules/graphql/docs/fix.md +0 -37
- package/rules/hasura/docs/fix.md +0 -39
- package/rules/image-avif/docs/fix.md +0 -28
- package/rules/image-compress/docs/fix.md +0 -27
- package/rules/js/docs/fix.md +0 -37
- package/rules/js-bun-db/docs/fix.md +0 -30
- package/rules/js-bun-redis/docs/fix.md +0 -32
- package/rules/js-mssql/docs/fix.md +0 -30
- package/rules/js-run/docs/fix.md +0 -36
- package/rules/k8s/docs/fix.md +0 -31
- package/rules/nginx-default-tpl/docs/fix.md +0 -35
- package/rules/npm-module/docs/fix.md +0 -34
- package/rules/php/docs/fix.md +0 -35
- package/rules/python/docs/fix.md +0 -38
- package/rules/rego/docs/fix.md +0 -31
- package/rules/release/docs/fix.md +0 -28
- package/rules/rust/docs/fix.md +0 -32
- package/rules/security/docs/fix.md +0 -33
- package/rules/style/docs/fix.md +0 -28
- package/rules/tauri/docs/fix.md +0 -39
- package/rules/test/docs/fix.md +0 -31
- package/rules/text/docs/fix.md +0 -37
- package/rules/tool-surface/docs/fix.md +0 -32
- package/rules/vue/docs/fix.md +0 -32
- package/rules/worktree/docs/fix.md +0 -40
- package/scripts/docs/post-tool-use-fix.md +0 -32
- package/scripts/docs/worktree-cli.md +0 -27
- package/scripts/lib/docs/worktree.md +0 -42
- package/scripts/lib/fix/docs/run-fix-check.md +0 -33
|
@@ -9,10 +9,12 @@ import {
|
|
|
9
9
|
findBunSqlPgLeftoverCallInText,
|
|
10
10
|
findBunSqlUnsafeUseWithoutAllowMarkerInText,
|
|
11
11
|
findBunSqlUnsafeWithInterpolatedTemplateInText,
|
|
12
|
+
findJsonStringifyBeforeJsonbInText,
|
|
12
13
|
findPgFormatLikeQueryWrapperInText,
|
|
13
14
|
findPgFormatShimDefinitionInText,
|
|
14
15
|
findPgLibImportInText,
|
|
15
16
|
findPgListenNotifyUsageInText,
|
|
17
|
+
findSqlArrayWithoutTypeArgInText,
|
|
16
18
|
findUnsafeBunSqlDynamicSqlListInText,
|
|
17
19
|
findUnsafeBunSqlInListMissingEmptyGuardInText,
|
|
18
20
|
isBunSqlScanSourceFile,
|
|
@@ -83,7 +85,9 @@ async function scanSourcesForBunSqlPatterns(sourcePaths, repoRoot, reporter) {
|
|
|
83
85
|
inListGuard: 0,
|
|
84
86
|
pgLeftover: 0,
|
|
85
87
|
pgFormatShim: 0,
|
|
86
|
-
queryWrapper: 0
|
|
88
|
+
queryWrapper: 0,
|
|
89
|
+
jsonStringifyJsonb: 0,
|
|
90
|
+
sqlArrayNoType: 0
|
|
87
91
|
}
|
|
88
92
|
let hasBunSqlImport = false
|
|
89
93
|
/** @type {{ rel: string, imports: { line: number, snippet: string }[], listenNotify: { line: number, snippet: string, kind: string }[] }[]} */
|
|
@@ -127,7 +131,7 @@ function collectPgUsageForFile(content, rel, pgUsage) {
|
|
|
127
131
|
* @param {string} content вміст файлу
|
|
128
132
|
* @param {string} rel posix-шлях відносно `repoRoot`
|
|
129
133
|
* @param {(msg: string) => void} fail callback при помилці
|
|
130
|
-
* @param {{ perRequest: number, unsafeCall: number, dynamicList: number, inListGuard: number, pgLeftover: number, pgFormatShim: number, queryWrapper: number }} counts акумулятори
|
|
134
|
+
* @param {{ perRequest: number, unsafeCall: number, unsafeTemplateInterp: number, dynamicList: number, inListGuard: number, pgLeftover: number, pgFormatShim: number, queryWrapper: number, jsonStringifyJsonb: number, sqlArrayNoType: number }} counts акумулятори
|
|
131
135
|
* @returns {void}
|
|
132
136
|
*/
|
|
133
137
|
function scanFileForBunSqlPatterns(content, rel, fail, counts) {
|
|
@@ -202,6 +206,22 @@ function scanFileForBunSqlPatterns(content, rel, fail, counts) {
|
|
|
202
206
|
`sql\`...\${value}...\` (js-bun-db.mdc): ${v.snippet}`
|
|
203
207
|
)
|
|
204
208
|
}
|
|
209
|
+
for (const v of findJsonStringifyBeforeJsonbInText(content, rel)) {
|
|
210
|
+
counts.jsonStringifyJsonb++
|
|
211
|
+
fail(
|
|
212
|
+
`js-bun-db: ${rel}:${v.line} — JSON.stringify(...) перед ::jsonb зайвий: Bun SQL серіалізує ` +
|
|
213
|
+
`об'єкти/масиви у JSON автоматично, явний stringify призводить до подвійної серіалізації ` +
|
|
214
|
+
`(js-bun-db.mdc query-safety): ${v.snippet}`
|
|
215
|
+
)
|
|
216
|
+
}
|
|
217
|
+
for (const v of findSqlArrayWithoutTypeArgInText(content, rel)) {
|
|
218
|
+
counts.sqlArrayNoType++
|
|
219
|
+
fail(
|
|
220
|
+
`js-bun-db: ${rel}:${v.line} — sql.array(arr) без другого аргументу типу — ` +
|
|
221
|
+
`вкажи явний pg-тип: sql.array(arr, 'int8') / sql.array(arr, 'uuid') тощо ` +
|
|
222
|
+
`(js-bun-db.mdc sql-array): ${v.snippet}`
|
|
223
|
+
)
|
|
224
|
+
}
|
|
205
225
|
}
|
|
206
226
|
|
|
207
227
|
/**
|
|
@@ -338,7 +358,9 @@ export async function check(cwd = process.cwd()) {
|
|
|
338
358
|
inListGuard,
|
|
339
359
|
pgLeftover,
|
|
340
360
|
pgFormatShim,
|
|
341
|
-
queryWrapper
|
|
361
|
+
queryWrapper,
|
|
362
|
+
jsonStringifyJsonb,
|
|
363
|
+
sqlArrayNoType
|
|
342
364
|
} = await scanSourcesForBunSqlPatterns(sourcePaths, repoRoot, reporter)
|
|
343
365
|
|
|
344
366
|
const { pgDepFails, pgImportFails, pgDepsFound, listenNotifyEvidence } = await checkPgDependencyAndUsage(
|
|
@@ -396,6 +418,12 @@ export async function check(cwd = process.cwd()) {
|
|
|
396
418
|
if (queryWrapper === 0) {
|
|
397
419
|
pass('js-bun-db: немає query(text, params)-обгорток над unsafe(...) у файлах з Bun SQL')
|
|
398
420
|
}
|
|
421
|
+
if (jsonStringifyJsonb === 0) {
|
|
422
|
+
pass('js-bun-db: немає JSON.stringify(...) перед ::jsonb — Bun SQL серіалізує автоматично')
|
|
423
|
+
}
|
|
424
|
+
if (sqlArrayNoType === 0) {
|
|
425
|
+
pass('js-bun-db: усі sql.array() мають явний аргумент типу')
|
|
426
|
+
}
|
|
399
427
|
|
|
400
428
|
return reporter.getExitCode()
|
|
401
429
|
}
|
|
@@ -927,3 +927,126 @@ function kindFromListenNotifyMatch(text) {
|
|
|
927
927
|
export function isBunSqlScanSourceFile(relativePathPosix) {
|
|
928
928
|
return SOURCE_FILE_RE.test(relativePathPosix) && !relativePathPosix.endsWith('.d.ts')
|
|
929
929
|
}
|
|
930
|
+
|
|
931
|
+
// Імена відомих SQL-інстансів, для яких перевіряємо .array() без типу.
|
|
932
|
+
const SQL_INSTANCE_NAMES = new Set(['sql', 'pgWrite', 'pgRead'])
|
|
933
|
+
|
|
934
|
+
/**
|
|
935
|
+
* Чи це виклик `JSON.stringify(...)` (JSON.stringify через MemberExpression).
|
|
936
|
+
* @param {unknown} node AST node
|
|
937
|
+
* @returns {boolean}
|
|
938
|
+
*/
|
|
939
|
+
function isJsonStringifyCall(node) {
|
|
940
|
+
if (!node || typeof node !== 'object' || node.type !== 'CallExpression') return false
|
|
941
|
+
const callee = node.callee
|
|
942
|
+
if (!callee || callee.type !== 'MemberExpression' || callee.computed) return false
|
|
943
|
+
const obj = callee.object
|
|
944
|
+
const prop = callee.property
|
|
945
|
+
return (
|
|
946
|
+
!!obj &&
|
|
947
|
+
obj.type === 'Identifier' &&
|
|
948
|
+
obj.name === 'JSON' &&
|
|
949
|
+
!!prop &&
|
|
950
|
+
prop.type === 'Identifier' &&
|
|
951
|
+
prop.name === 'stringify'
|
|
952
|
+
)
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
/**
|
|
956
|
+
* Знаходить виклики `JSON.stringify(...)::jsonb` всередині SQL template literal-ів.
|
|
957
|
+
* Bun SQL серіалізує об'єкти/масиви у JSON автоматично — явний `JSON.stringify`
|
|
958
|
+
* перед `::jsonb` призводить до подвійної серіалізації (js-bun-db.mdc).
|
|
959
|
+
* @param {string} content вихідний код
|
|
960
|
+
* @param {string} [virtualPath] шлях для вибору lang
|
|
961
|
+
* @returns {{ line: number, snippet: string }[]} список порушень
|
|
962
|
+
*/
|
|
963
|
+
export function findJsonStringifyBeforeJsonbInText(content, virtualPath = 'scan.ts') {
|
|
964
|
+
const program = parseProgramOrNull(content, virtualPath)
|
|
965
|
+
if (!program) return []
|
|
966
|
+
|
|
967
|
+
/** @type {{ line: number, snippet: string }[]} */
|
|
968
|
+
const out = []
|
|
969
|
+
|
|
970
|
+
walkAstWithAncestors(program, [], node => {
|
|
971
|
+
let template = null
|
|
972
|
+
if (node.type === 'TemplateLiteral') template = node
|
|
973
|
+
else if (node.type === 'TaggedTemplateExpression') template = node.quasi
|
|
974
|
+
if (!template || typeof template !== 'object' || template.type !== 'TemplateLiteral') return
|
|
975
|
+
|
|
976
|
+
const expressions = template.expressions
|
|
977
|
+
const quasis = template.quasis
|
|
978
|
+
if (!Array.isArray(expressions) || !Array.isArray(quasis)) return
|
|
979
|
+
|
|
980
|
+
for (const [i, expr] of expressions.entries()) {
|
|
981
|
+
// Перевіряємо прямий JSON.stringify(...) і JSON.stringify всередині sql.array(...)
|
|
982
|
+
const isDirectStringify = isJsonStringifyCall(expr)
|
|
983
|
+
// sql.array(batch.map(r => JSON.stringify(...)), 'jsonb')
|
|
984
|
+
const hasSqlArrayStringify =
|
|
985
|
+
!isDirectStringify &&
|
|
986
|
+
expr.type === 'CallExpression' &&
|
|
987
|
+
Array.isArray(expr.arguments) &&
|
|
988
|
+
expr.arguments.some(arg => {
|
|
989
|
+
if (isJsonStringifyCall(arg)) return true
|
|
990
|
+
// .map(r => JSON.stringify(...))
|
|
991
|
+
if (arg.type === 'CallExpression' && Array.isArray(arg.arguments)) {
|
|
992
|
+
const cb = arg.arguments[0]
|
|
993
|
+
if (!cb) return false
|
|
994
|
+
const body = cb.type === 'ArrowFunctionExpression' || cb.type === 'FunctionExpression' ? cb.body : null
|
|
995
|
+
if (body && isJsonStringifyCall(body)) return true
|
|
996
|
+
}
|
|
997
|
+
return false
|
|
998
|
+
})
|
|
999
|
+
|
|
1000
|
+
if (!isDirectStringify && !hasSqlArrayStringify) continue
|
|
1001
|
+
|
|
1002
|
+
// Quasi після expr (quasi[i+1]) — текст одразу після закриваючого }
|
|
1003
|
+
const nextQuasi = quasis[i + 1]
|
|
1004
|
+
const rawAfter =
|
|
1005
|
+
nextQuasi && typeof nextQuasi === 'object' && nextQuasi.value && typeof nextQuasi.value.raw === 'string'
|
|
1006
|
+
? nextQuasi.value.raw
|
|
1007
|
+
: ''
|
|
1008
|
+
|
|
1009
|
+
if (/^\s*::jsonb/u.test(rawAfter) || hasSqlArrayStringify) {
|
|
1010
|
+
out.push({
|
|
1011
|
+
line: offsetToLine(content, expr.start),
|
|
1012
|
+
snippet: normalizeSnippet(content.slice(expr.start, expr.end))
|
|
1013
|
+
})
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
})
|
|
1017
|
+
return out
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
/**
|
|
1021
|
+
* Знаходить виклики `sql.array(arr)` / `pgWrite.array(arr)` / `pgRead.array(arr)` без
|
|
1022
|
+
* обов'язкового другого аргументу (типу pg-елемента). Без типу Bun не може вивести
|
|
1023
|
+
* pg-тип, що призводить до mismatch (js-bun-db.mdc).
|
|
1024
|
+
* @param {string} content вихідний код
|
|
1025
|
+
* @param {string} [virtualPath] шлях для вибору lang
|
|
1026
|
+
* @returns {{ line: number, snippet: string }[]} список порушень
|
|
1027
|
+
*/
|
|
1028
|
+
export function findSqlArrayWithoutTypeArgInText(content, virtualPath = 'scan.ts') {
|
|
1029
|
+
const program = parseProgramOrNull(content, virtualPath)
|
|
1030
|
+
if (!program) return []
|
|
1031
|
+
|
|
1032
|
+
/** @type {{ line: number, snippet: string }[]} */
|
|
1033
|
+
const out = []
|
|
1034
|
+
|
|
1035
|
+
walkAstWithAncestors(program, [], node => {
|
|
1036
|
+
if (!node || node.type !== 'CallExpression') return
|
|
1037
|
+
const callee = node.callee
|
|
1038
|
+
if (!callee || callee.type !== 'MemberExpression' || callee.computed) return
|
|
1039
|
+
const prop = callee.property
|
|
1040
|
+
if (!prop || prop.type !== 'Identifier' || prop.name !== 'array') return
|
|
1041
|
+
const obj = callee.object
|
|
1042
|
+
if (!obj || obj.type !== 'Identifier' || !SQL_INSTANCE_NAMES.has(obj.name)) return
|
|
1043
|
+
const args = node.arguments
|
|
1044
|
+
if (!Array.isArray(args) || args.length !== 1) return
|
|
1045
|
+
|
|
1046
|
+
out.push({
|
|
1047
|
+
line: offsetToLine(content, node.start),
|
|
1048
|
+
snippet: normalizeSnippet(content.slice(node.start, node.end))
|
|
1049
|
+
})
|
|
1050
|
+
})
|
|
1051
|
+
return out
|
|
1052
|
+
}
|