games-development-ai/scripts/check-scope.mjs
2026-06-04 18:03:58 +08:00

11034 lines
428 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { mkdir, mkdtemp, readdir, readFile, rm, writeFile } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
const scanRoots = ["apps", "packages"];
const ignoredScanSegments = new Set(["node_modules", "dist", ".vite", "coverage", ".next", "generated"]);
const sourceLikeFilePattern = /\.(?:ts|tsx|js|jsx|mjs|cjs|json|sql|prisma)$/;
const prismaMigrationPrefix = "apps/api/prisma/migrations/";
const generatedPrismaPrefix = "apps/api/src/generated/prisma/";
const stateTransitionModulePrefix = "apps/api/src/modules/state-transition/";
const auditModulePrefix = "apps/api/src/modules/audit/";
const jobsModulePrefix = "apps/api/src/modules/jobs/";
const jobExecutionStorePath = "apps/api/src/modules/jobs/index.ts";
const initialAuditLogAppendOnlyFunctionMigration = "apps/api/prisma/migrations/20260601040253_s1_app_foundation/migration.sql";
const historicalPrismaSchemaTest = "apps/api/src/prisma.schema.spec.ts";
const auditTriggerAssertionFiles = new Set([historicalPrismaSchemaTest, "apps/api/src/modules/audit/audit.spec.ts"]);
const stateTransitionImportFixtureTests = new Set([
"apps/api/src/modules/jobs/jobs.spec.ts",
"apps/api/src/modules/jobs/jobs.api.spec.ts",
"apps/api/src/modules/projects/api-runtime.spec.ts",
"apps/api/src/modules/projects/projects.api.spec.ts"
]);
const laterPhaseDeniedTerms = [
"creation-agent/messages",
"compile-game-ir",
"compile-game-config",
"validate-game-logic",
"web-runtime",
"publish-review-feed",
"channel-readiness",
"deploy-readiness",
"GameConfig",
"GameLogicModule",
"RuntimeSdkContract",
"SimulationRuleSet",
"NpcBehaviorGraph",
"game-config",
"game-logic",
"mini-game-conversion",
"GamePackage",
"PromptRecord",
"AIGameDesignDraft",
"DesignSectionPatch",
"DesignSection",
"GameIRArtifact",
"GameIR",
"ConversionReport",
"ValidationReport",
"MiniGameProject",
"MiniGameCodeConversion",
"FeedItem",
"GameDailyStats",
"CreatorDailyStats",
"/events/batch"
];
const supportedPhases = new Set(["S1", "S2", "S3"]);
const s2ApprovedTerms = new Set([
"creation-agent/messages",
"compile-game-ir",
"PromptRecord",
"AIGameDesignDraft",
"DesignSectionPatch",
"DesignSection",
"GameIRArtifact",
"GameIR"
]);
const s3ApprovedTerms = new Set([
"compile-game-config",
"validate-game-logic",
"GameConfig",
"GameLogicModule",
"RuntimeSdkContract",
"SimulationRuleSet",
"NpcBehaviorGraph",
"game-config",
"game-logic",
"simulation",
"ValidationReport"
]);
const s3InheritedS2Terms = new Set(["GameIRArtifact", "GameIR"]);
const s2LegacyS3TermAllowExactPaths = new Map([
[
"game-config",
new Set([
"apps/api/prisma/migrations/20260604102042_s2_task2_design_models/migration.sql",
"apps/api/src/modules/creation-agent/creation-agent-ai-design-workbench.api.spec.ts",
"apps/api/src/modules/creation-agent/index.ts",
"apps/api/src/prisma.schema.spec.ts"
])
],
[
"game-logic",
new Set(["apps/api/src/modules/harness-gate/harness-gate.spec.ts", "packages/harness-client/src/index.spec.ts"])
],
[
"NpcBehaviorGraph",
new Set([
"apps/api/src/modules/game-ir/index.ts",
"apps/web/src/components/workbench/MainCreationAgentChat.tsx",
"packages/shared-contracts/src/index.ts",
"packages/shared-contracts/src/index.spec.ts",
"packages/shared-contracts/src/game-ir.ts",
"packages/shared-contracts/src/game-ir.spec.ts"
])
]
]);
const s2ApprovedTermDenyPathSegments = new Set([
"simulation",
"web-runtime",
"minigame-conversion",
"mini_game_conversion",
"publish-review-feed",
"publish_review_feed",
"mini-game-conversion",
"conversion",
"feed",
"runtime",
"publish",
"review"
]);
const s2ApprovedTermDenyPathFragments = ["/events/batch/"];
const s2ApprovedTermAllowPathPrefixes = [
"apps/api/src/modules/creation-agent/",
"apps/api/src/modules/ai-design/",
"apps/api/src/modules/workbench/",
"apps/api/src/modules/game-ir/",
"apps/api/src/modules/compile-game-ir/",
"apps/api/prisma/migrations/",
"apps/web/src/app/api/workbench/",
"apps/web/src/app/workbench/",
"apps/web/src/components/workbench/"
];
const s2ApprovedTermAllowExactPaths = new Set([
"apps/api/prisma/schema.prisma",
// Prisma schema contract tests需要直接断言 S2 设计模型及其 lineage 约束;
// 仍保持精确路径放行,避免普通 API 测试借用 S2 术语绕过阶段门禁。
"apps/api/src/prisma.schema.spec.ts",
"packages/shared-contracts/src/index.ts",
"packages/shared-contracts/src/index.spec.ts",
"packages/shared-contracts/src/workflow.ts",
"packages/shared-contracts/src/workflow.spec.ts",
"packages/shared-contracts/src/ai-design.ts",
"packages/shared-contracts/src/ai-design.spec.ts",
"packages/shared-contracts/src/game-ir.ts",
"packages/shared-contracts/src/game-ir.spec.ts"
]);
const s3ApprovedTermAllowPathPrefixes = [
"apps/api/src/modules/game-config/",
"apps/api/src/modules/game-logic/",
"apps/api/src/modules/simulation/"
];
const s3ApprovedTermAllowExactPaths = new Set([
"packages/shared-contracts/src/index.ts",
"packages/shared-contracts/src/index.spec.ts",
"packages/shared-contracts/src/game-config.ts",
"packages/shared-contracts/src/game-config.spec.ts",
"packages/shared-contracts/src/game-logic-module.ts",
"packages/shared-contracts/src/game-logic-module.spec.ts",
"packages/shared-contracts/src/runtime-sdk-contract.ts",
"packages/shared-contracts/src/runtime-sdk-contract.spec.ts"
]);
const s3OwnedPathPrefixes = [
"apps/api/src/modules/game-config/",
"apps/api/src/modules/game-logic/",
"apps/api/src/modules/simulation/"
];
const harnessValidationContractTerms = new Set([
"AIGameDesignDraft",
"GameIR",
"ConversionReport",
"ValidationReport",
"MiniGameProject",
"MiniGameCodeConversion"
]);
const harnessClientContractWatchTerms = new Set(harnessValidationContractTerms);
const jobRuntimeFields = [
"status",
"attempts",
"nextRetryAt",
"errorCode",
"leaseToken",
"leasedBy",
"leaseExpiresAt",
"lockVersion",
"timeoutAt"
];
const rawSqlVariableResolutionMaxDepth = 6;
const dynamicSqlIdentifierMarker = "__HUJING_DYNAMIC_SQL_IDENTIFIER__";
const auditLogAppendOnlyFunctionName = "app_reject_audit_log_mutation";
const auditPrismaMutationMethods = ["update", "updateMany", "updateManyAndReturn", "upsert", "delete", "deleteMany"];
const guardedPrismaWriteMethods = ["create", "update", "upsert", "createMany", "updateMany", "delete", "deleteMany"];
const guardedPrismaWriteModels = ["gameVersion", "reviewRecord", "lifecycleEvent"];
const guardedStateSqlTables = ["GameVersion", "ReviewRecord", "LifecycleEvent"];
const jobPrismaWriteMethods = ["create", "createMany", "createManyAndReturn", "update", "updateMany", "updateManyAndReturn", "upsert"];
const jobPrismaDeleteMethods = ["delete", "deleteMany"];
const s1AnchorTerms = ["MainCreationAgentSession", "AgentTask", "ReviewRecord", "LifecycleEvent"];
const s2ApprovedS1AnchorTerms = new Set(["MainCreationAgentSession", "AgentTask"]);
const allowedStateTransitionImportPaths = new Set(["apps/api/src/modules/projects/index.ts"]);
const allowedStateTransitionImportPrefixes = [stateTransitionModulePrefix];
function normalizePath(filePath) {
return filePath.split(path.sep).join("/");
}
function isIgnoredScanPath(filePath) {
return filePath.split("/").some((segment) => ignoredScanSegments.has(segment));
}
function isHarnessClientTestPath(filePath) {
return /^packages\/harness-client\/.*\.(?:spec|test)\.(?:ts|tsx|js|jsx|mjs|cjs)$/.test(filePath);
}
function escapeRegExp(value) {
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function sqlBareIdentifierPattern() {
return String.raw`[A-Za-z_][\w$]*`;
}
function sqlTriggerIdentifierPattern() {
// SQL 扫描会去掉双引号quoted trigger name 里的连字符需作为同一 token fail-closed。
return String.raw`[^\s(),;]+`;
}
function validationContractReferenceSpans(content, term) {
const spans = [];
const pattern = new RegExp(`\\bvalidateContract\\s*\\(\\s*(["'])${escapeRegExp(term)}\\1`, "g");
for (const match of content.matchAll(pattern)) {
const termOffset = match[0].lastIndexOf(term);
if (typeof match.index === "number" && termOffset >= 0) {
spans.push({ start: match.index + termOffset, end: match.index + termOffset + term.length });
}
}
return spans;
}
function enclosingBraceSpan(content, index) {
const structure = maskCommentsOutsideStringLiterals(content);
let smallestSpan = null;
for (let cursor = 0; cursor < structure.length; cursor += 1) {
if (structure[cursor] !== "{") continue;
const closeBraceIndex = findMatchingBrace(structure, cursor);
if (closeBraceIndex === -1 || index < cursor || index > closeBraceIndex) continue;
const span = { start: cursor, end: closeBraceIndex + 1 };
if (!smallestSpan || span.end - span.start < smallestSpan.end - smallestSpan.start) {
smallestSpan = span;
}
}
return smallestSpan;
}
function hasHarnessCliContractArgumentContext(content, assertionIndex) {
const span = enclosingBraceSpan(content, assertionIndex);
const contextStart = Math.max(span?.start ?? 0, assertionIndex - 400);
const contextEnd = Math.min(span?.end ?? content.length, assertionIndex + 220);
const context = content.slice(contextStart, contextEnd);
// args[2] 只有在同一 runner 小窗口内已证明 args[0..1] 指向真实 harness CLI contract 调用时才可作为合同名断言。
return /\bexpect\s*\(\s*args\s*\.\s*slice\s*\(\s*0\s*,\s*2\s*\)\s*\)\s*\.\s*to(?:Strict)?Equal\s*\(\s*\[\s*(?:realHarnessCli|[^\]]*validate-harness\.mjs[^\]]*)\s*,\s*(["'])--contract\1\s*\]\s*\)/s.test(
context
);
}
function harnessCliContractArgumentAssertionSpans(content, term) {
const spans = [];
const pattern = new RegExp(
`\\bexpect\\s*\\(\\s*args\\s*\\[\\s*2\\s*\\]\\s*\\)\\s*\\.\\s*toBe\\s*\\(\\s*(["'])${escapeRegExp(term)}\\1\\s*\\)`,
"g"
);
for (const match of content.matchAll(pattern)) {
const termOffset = match[0].lastIndexOf(term);
if (typeof match.index === "number" && termOffset >= 0 && hasHarnessCliContractArgumentContext(content, match.index)) {
spans.push({ start: match.index + termOffset, end: match.index + termOffset + term.length });
}
}
return spans;
}
function isInSpan(index, spans) {
return spans.some((span) => index >= span.start && index < span.end);
}
function isHarnessGateValidationTestPath(filePath) {
return /^apps\/api\/src\/modules\/harness-gate\/.*\.(?:spec|test)\.(?:ts|tsx|js|jsx|mjs|cjs)$/.test(filePath);
}
function isAllowedHarnessValidationContractReferenceAt(filePath, content, term, index) {
if (!harnessValidationContractTerms.has(term)) return false;
if (!isHarnessClientTestPath(filePath) && !isHarnessGateValidationTestPath(filePath)) return false;
if (isInSpan(index, validationContractReferenceSpans(content, term))) return true;
// harness-client 的 runner 断言 `args[2]` 是 `--contract` 的合同名;这是 CLI validation 证据。
return isHarnessClientTestPath(filePath) && isInSpan(index, harnessCliContractArgumentAssertionSpans(content, term));
}
function isAllowedHarnessClientContractReferenceAt(filePath, content, term, index) {
if (!isHarnessClientTestPath(filePath)) return false;
return isAllowedHarnessValidationContractReferenceAt(filePath, content, term, index);
}
function normalizeDeniedPathTerm(term) {
return term.split(path.sep).join("/").replace(/^\/+|\/+$/g, "");
}
function kebabCaseContractTerm(term) {
return term
.replace(/([a-z0-9])([A-Z])/g, "$1-$2")
.replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2")
.toLowerCase();
}
function pathContainsLaterPhaseTerm(filePath, term) {
const deniedPathTerm = normalizeDeniedPathTerm(term);
if (!deniedPathTerm) return false;
const normalizedFilePath = normalizePath(filePath).replace(/^\/+|\/+$/g, "");
const pathWithBoundaries = `/${normalizedFilePath}/`;
// route/module fragment 要按路径边界匹配,同时覆盖同名单文件模块。
if (deniedPathTerm.includes("/")) {
const deniedSourceFilePattern = new RegExp(`(?:^|/)${escapeRegExp(deniedPathTerm)}${sourceLikeFilePattern.source}`);
return pathWithBoundaries.includes(`/${deniedPathTerm}/`) || deniedSourceFilePattern.test(normalizedFilePath);
}
// symbol-like term 在路径中按 segment/filename 命中,覆盖 AIGameDesignDraft.ts 这类文件名。
const pathTermVariants = new Set([deniedPathTerm, kebabCaseContractTerm(deniedPathTerm)]);
return normalizedFilePath
.split("/")
.some((segment) => [...pathTermVariants].some((pathTermVariant) => segment.includes(pathTermVariant)));
}
function contentDeniedTermVariants(term) {
if (!term.startsWith("/")) return [term];
// 内容扫描把同一路由片段的绝对/相对写法视为等价,避免 "/events/batch" 漏掉 "events/batch"。
const normalizedTerm = term.replace(/^\/+/, "");
return normalizedTerm ? [term, normalizedTerm] : [term];
}
function isTestFile(filePath) {
return /\.(?:spec|test)\.(?:ts|tsx|js|jsx|mjs|cjs)$/.test(filePath);
}
function isHistoricalTestFile(filePath) {
return filePath === historicalPrismaSchemaTest;
}
function isGeneratedPrismaFile(filePath) {
return filePath.startsWith(generatedPrismaPrefix);
}
function isPrismaMigrationSqlPath(filePath) {
return filePath.startsWith(prismaMigrationPrefix) && filePath.endsWith("/migration.sql");
}
function auditLogAppendOnlyFunctionCreatePattern() {
const auditLogAppendOnlyFunction = sqlFunctionReferencePattern(auditLogAppendOnlyFunctionName);
return new RegExp(String.raw`\bCREATE\s+(OR\s+REPLACE\s+)?FUNCTION\s+${auditLogAppendOnlyFunction}\b`, "gi");
}
function auditLogAppendOnlyFunctionInstallPattern() {
const auditLogAppendOnlyFunction = sqlFunctionReferencePattern(auditLogAppendOnlyFunctionName);
const dollarQuote = String.raw`(?:\$\$|\$[A-Za-z_][\w$]*\$)`;
return new RegExp(
String.raw`\bCREATE\s+(OR\s+REPLACE\s+)?FUNCTION\s+${auditLogAppendOnlyFunction}\s+RETURNS\s+trigger\s+AS\s+(${dollarQuote})([\s\S]*?)\2\s+LANGUAGE\s+plpgsql\b`,
"gi"
);
}
function auditLogAppendOnlyFunctionCreateStatements(sqlSource) {
const sql = stripSqlComments(sqlSource);
return [...sql.matchAll(auditLogAppendOnlyFunctionCreatePattern())];
}
function auditLogAppendOnlyFunctionDefinitions(sqlSource) {
const sql = stripSqlComments(sqlSource);
return [...sql.matchAll(auditLogAppendOnlyFunctionInstallPattern())].map((match) => ({
isCreateOrReplace: Boolean(match[1]),
body: match[3]
}));
}
function isCanonicalAuditLogAppendOnlyFunctionBody(functionBody) {
// 允许 SQL 关键字大小写与空白变化,但函数体只能安装固定 append-only guard不能夹带 RETURN/IF/EXECUTE 等语句。
return /^\s*BEGIN\s+RAISE\s+EXCEPTION\s+'AuditLog is append-only'\s+USING\s+ERRCODE\s*=\s*'42501'\s*;\s*END\s*;?\s*$/i.test(
functionBody
);
}
function hasAllowedAuditLogAppendOnlyFunctionInstall(sqlSource, options = {}) {
const createStatements = auditLogAppendOnlyFunctionCreateStatements(sqlSource);
if (createStatements.length !== 1) return false;
const definitions = auditLogAppendOnlyFunctionDefinitions(sqlSource);
if (definitions.length !== 1) return false;
const [definition] = definitions;
if (definition.isCreateOrReplace && !options.allowOrReplace) return false;
return isCanonicalAuditLogAppendOnlyFunctionBody(definition.body);
}
function auditLogAppendOnlyFunctionCreateOptions(filePath, content) {
return {
// 后续 migration 只允许 plain CREATE FUNCTION 安装固定 append-only guard不允许替换已有函数。
allowAuditLogAppendOnlyFunctionPlainCreate:
isPrismaMigrationSqlPath(filePath) && hasAllowedAuditLogAppendOnlyFunctionInstall(content),
// 真实 S1 初始 migration 历史上使用 CREATE OR REPLACE仅该文件按同一函数体语义兼容放行。
allowAuditLogAppendOnlyFunctionCreateOrReplace:
filePath === initialAuditLogAppendOnlyFunctionMigration &&
hasAllowedAuditLogAppendOnlyFunctionInstall(content, { allowOrReplace: true })
};
}
function isAllowedAuditTriggerAssertion(filePath, content, structure, mutationIndex) {
if (!auditTriggerAssertionFiles.has(filePath)) return false;
let searchCursor = mutationIndex;
while (searchCursor >= 0) {
const assertionIndex = structure.lastIndexOf("expectPrismaRejectedInSavepoint", searchCursor);
if (assertionIndex === -1) return false;
const openParenIndex = structure.indexOf("(", assertionIndex);
if (openParenIndex === -1 || openParenIndex > mutationIndex) {
searchCursor = assertionIndex - 1;
continue;
}
const closeParenIndex = findMatchingParen(structure, openParenIndex);
if (closeParenIndex === -1 || closeParenIndex < mutationIndex) {
searchCursor = assertionIndex - 1;
continue;
}
const assertionSource = content.slice(openParenIndex, closeParenIndex + 1);
return /AuditLog is append-only/.test(assertionSource);
}
return false;
}
function hasForbiddenAuditMutation(filePath, content) {
const structure = stripStringLiteralsExceptPropertyKeys(maskCommentsOutsideStringLiterals(content));
for (const match of prismaModelMethodCallMatchesInStructure(content, structure, ["auditLog"], auditPrismaMutationMethods)) {
if (typeof match.index === "number" && isAllowedAuditTriggerAssertion(filePath, content, structure, match.index)) continue;
return true;
}
return hasForbiddenRawAuditLogMutation(filePath, content, structure);
}
function guardedStateSqlTableReferencePattern() {
return String.raw`(?:${guardedStateSqlTables.map(sqlTableReferencePattern).join("|")})`;
}
function hasRawGuardedStateWrite(sqlSource) {
const sql = normalizeRawSqlScanSource(sqlSource);
const guardedTable = guardedStateSqlTableReferencePattern();
const identifier = sqlBareIdentifierPattern();
// guarded state 的 raw SQL 必须跟 Prisma raw invocation 同源扫描覆盖转义字符串、SQL 注释和 Postgres U& identifier。
return (
new RegExp(String.raw`\bUPDATE\s+(?:ONLY\s+)?${guardedTable}(?:\s*\*)?(?:\s+(?:AS\s+)?${identifier})?\s+SET\b`, "i").test(
sql
) ||
new RegExp(String.raw`\bINSERT\s+INTO\s+(?:ONLY\s+)?${guardedTable}\b`, "i").test(sql) ||
new RegExp(String.raw`\bDELETE\s+FROM\s+(?:ONLY\s+)?${guardedTable}(?:\s*\*)?(?:\s+(?:AS\s+)?${identifier})?\b`, "i").test(
sql
) ||
new RegExp(String.raw`\bMERGE\s+INTO\s+(?:ONLY\s+)?${guardedTable}(?:\s*\*)?(?:\s+(?:AS\s+)?${identifier})?\b`, "i").test(
sql
) ||
new RegExp(sqlTruncateTableListPattern(guardedTable), "i").test(sql)
);
}
function hasGuardedPrismaWrite(filePath, content) {
const hasPrismaWrite = prismaModelMethodCallMatches(content, guardedPrismaWriteModels, guardedPrismaWriteMethods).length > 0;
const hasRawSqlWrite = filePath.endsWith(".sql")
? hasRawGuardedStateWrite(content)
: hasRawSqlInvocationMatching(content, hasRawGuardedStateWrite);
return hasPrismaWrite || hasRawSqlWrite;
}
function hasRuntimeGuardSetter(content) {
return /app\.state_transition_guard|set_config\s*\(|SET\s+LOCAL/i.test(content);
}
function isHexDigits(value) {
return /^[0-9a-f]+$/i.test(value);
}
function decodeJavaScriptStringLiteralBody(body) {
let output = "";
for (let index = 0; index < body.length; index += 1) {
const char = body[index];
if (char !== "\\") {
output += char;
continue;
}
const next = body[index + 1];
if (typeof next === "undefined") {
output += char;
continue;
}
if (next === "\r" && body[index + 2] === "\n") {
index += 2;
continue;
}
if (next === "\n" || next === "\r") {
index += 1;
continue;
}
if (next === "x") {
const hexValue = body.slice(index + 2, index + 4);
if (hexValue.length === 2 && isHexDigits(hexValue)) {
output += String.fromCharCode(Number.parseInt(hexValue, 16));
index += 3;
continue;
}
}
if (next === "u") {
if (body[index + 2] === "{") {
const closeBraceIndex = body.indexOf("}", index + 3);
const hexValue = closeBraceIndex === -1 ? "" : body.slice(index + 3, closeBraceIndex);
const codePoint = hexValue.length > 0 && isHexDigits(hexValue) ? Number.parseInt(hexValue, 16) : Number.NaN;
if (codePoint >= 0 && codePoint <= 0x10ffff) {
output += String.fromCodePoint(codePoint);
index = closeBraceIndex;
continue;
}
}
const hexValue = body.slice(index + 2, index + 6);
if (hexValue.length === 4 && isHexDigits(hexValue)) {
output += String.fromCharCode(Number.parseInt(hexValue, 16));
index += 5;
continue;
}
}
const escapedCharacters = {
"\\": "\\",
'"': '"',
"'": "'",
"`": "`",
n: "\n",
r: "\r",
t: "\t",
b: "\b",
f: "\f",
v: "\v",
0: "\0"
};
output += Object.hasOwn(escapedCharacters, next) ? escapedCharacters[next] : next;
index += 1;
}
return output;
}
function decodeStaticJavaScriptStringLiteralSource(source) {
const literalStart = skipWhitespaceForward(source, 0);
const literalEndBoundary = skipWhitespaceBackward(source, source.length - 1);
if (literalStart > literalEndBoundary) return null;
const quote = source[literalStart];
if (quote !== '"' && quote !== "'" && quote !== "`") return null;
const literalEnd = findStringLiteralEnd(source, literalStart);
if (literalEnd !== literalEndBoundary) return null;
const body = source.slice(literalStart + 1, literalEnd);
// 带表达式的 template literal 继续走原有动态 SQL 归一化路径,避免把参数表达式误解成 SQL 文本。
if (quote === "`" && /\$\{/.test(body)) return null;
return decodeJavaScriptStringLiteralBody(body);
}
function decodeStaticJavaScriptStringLiteralsInSource(source) {
let output = "";
let index = 0;
while (index < source.length) {
const quote = source[index];
if (
quote === '"' &&
source[index - 1] === "&" &&
(source[index - 2] === "U" || source[index - 2] === "u")
) {
let cursor = index + 1;
while (cursor < source.length) {
if (source[cursor] === '"' && source[cursor + 1] === '"') {
cursor += 2;
continue;
}
cursor += 1;
if (source[cursor - 1] === '"') break;
}
output += source.slice(index, cursor);
index = cursor;
continue;
}
if (quote !== '"' && quote !== "'" && quote !== "`") {
output += quote;
index += 1;
continue;
}
const literalEnd = findStringLiteralEnd(source, index);
if (literalEnd === -1) {
output += source.slice(index);
break;
}
const body = source.slice(index + 1, literalEnd);
// 动态 template literal 的表达式仍交给 dynamic SQL marker不能在这里静态展开。
output += quote === "`" && /\$\{/.test(body) ? source.slice(index, literalEnd + 1) : `${quote}${decodeJavaScriptStringLiteralBody(body)}${quote}`;
index = literalEnd + 1;
}
return output;
}
function sqlDollarQuoteTagAt(content, index) {
return /^(?:\$\$|\$[A-Za-z_][\w$]*\$)/.exec(content.slice(index))?.[0] ?? null;
}
function decodePostgresUnicodeQuotedIdentifierBody(body, escapeChar = "\\") {
let output = "";
for (let index = 0; index < body.length; index += 1) {
const char = body[index];
if (char !== escapeChar) {
output += char;
continue;
}
if (body[index + 1] === "+") {
const hexValue = body.slice(index + 2, index + 8);
const codePoint = hexValue.length === 6 && isHexDigits(hexValue) ? Number.parseInt(hexValue, 16) : Number.NaN;
if (codePoint >= 0 && codePoint <= 0x10ffff) {
output += String.fromCodePoint(codePoint);
index += 7;
continue;
}
}
const hexValue = body.slice(index + 1, index + 5);
if (hexValue.length === 4 && isHexDigits(hexValue)) {
output += String.fromCharCode(Number.parseInt(hexValue, 16));
index += 4;
continue;
}
output += char;
}
return output;
}
function postgresUnicodeQuotedIdentifierEscapeClauseAt(content, index) {
const keywordStart = skipWhitespaceForward(content, index);
const keywordMatch = /^UESCAPE\b/i.exec(content.slice(keywordStart));
if (!keywordMatch) return null;
const quoteStart = skipWhitespaceForward(content, keywordStart + keywordMatch[0].length);
if (content[quoteStart] !== "'") return null;
const escapeChar = content[quoteStart + 1];
if (typeof escapeChar === "undefined" || content[quoteStart + 2] !== "'") return null;
return { escapeChar, end: quoteStart + 3 };
}
function normalizePostgresUnicodeQuotedIdentifiers(content) {
let output = "";
let index = 0;
while (index < content.length) {
if ((content[index] !== "U" && content[index] !== "u") || content[index + 1] !== "&" || content[index + 2] !== '"') {
output += content[index];
index += 1;
continue;
}
let body = "";
let cursor = index + 3;
let closeQuoteIndex = -1;
while (cursor < content.length) {
const char = content[cursor];
const next = content[cursor + 1];
if (char === '"' && next === '"') {
body += '""';
cursor += 2;
continue;
}
if (char === '"') {
closeQuoteIndex = cursor;
break;
}
body += char;
cursor += 1;
}
if (closeQuoteIndex === -1) {
output += content[index];
index += 1;
continue;
}
const escapeClause = postgresUnicodeQuotedIdentifierEscapeClauseAt(content, closeQuoteIndex + 1);
// UESCAPE 紧跟 U& 标识符时会改变转义字符;归一化后必须移除该子句,避免污染表名模式。
output += `"${decodePostgresUnicodeQuotedIdentifierBody(body, escapeClause?.escapeChar ?? "\\")}"`;
index = escapeClause?.end ?? closeQuoteIndex + 1;
}
return output;
}
function stripSqlComments(content) {
let output = "";
let index = 0;
while (index < content.length) {
const char = content[index];
const next = content[index + 1];
if (char === "-" && next === "-") {
// 真实行注释替换为空白但保留换行,避免擦掉下一条 SQL 语句的行边界。
while (index < content.length && content[index] !== "\n" && content[index] !== "\r") {
output += " ";
index += 1;
}
continue;
}
if (char === "/" && next === "*") {
// 注释可能夹在关键字之间,必须保留 token separator避免 DELETE/*x*/FROM 被拼成 DELETEFROM。
let depth = 1;
output += " ";
index += 2;
while (index < content.length && depth > 0) {
const blockChar = content[index];
const blockNext = content[index + 1];
if (blockChar === "/" && blockNext === "*") {
output += " ";
index += 2;
depth += 1;
continue;
}
if (blockChar === "*" && blockNext === "/") {
output += " ";
index += 2;
depth -= 1;
continue;
}
output += blockChar === "\n" || blockChar === "\r" ? blockChar : " ";
index += 1;
}
continue;
}
if (char === "'") {
output += char;
index += 1;
while (index < content.length) {
const stringChar = content[index];
const stringNext = content[index + 1];
output += stringChar;
if (stringChar === "\\") {
if (typeof stringNext !== "undefined") output += stringNext;
index += typeof stringNext === "undefined" ? 1 : 2;
continue;
}
if (stringChar === "'" && stringNext === "'") {
output += stringNext;
index += 2;
continue;
}
index += 1;
if (stringChar === "'") break;
}
continue;
}
if (char === '"') {
output += char;
index += 1;
while (index < content.length) {
const identifierChar = content[index];
const identifierNext = content[index + 1];
output += identifierChar;
if (identifierChar === "\\") {
if (typeof identifierNext !== "undefined") output += identifierNext;
index += typeof identifierNext === "undefined" ? 1 : 2;
continue;
}
if (identifierChar === '"' && identifierNext === '"') {
output += identifierNext;
index += 2;
continue;
}
index += 1;
if (identifierChar === '"') break;
}
continue;
}
const dollarQuoteTag = char === "$" ? sqlDollarQuoteTagAt(content, index) : null;
if (dollarQuoteTag) {
const dollarQuoteEnd = content.indexOf(dollarQuoteTag, index + dollarQuoteTag.length);
if (dollarQuoteEnd === -1) {
output += content.slice(index);
break;
}
output += content.slice(index, dollarQuoteEnd + dollarQuoteTag.length);
index = dollarQuoteEnd + dollarQuoteTag.length;
continue;
}
output += char;
index += 1;
}
return output;
}
function normalizeRawSqlScanSource(content) {
const sqlSource = decodeStaticJavaScriptStringLiteralSource(content) ?? decodeStaticJavaScriptStringLiteralsInSource(content);
return normalizePostgresUnicodeQuotedIdentifiers(stripSqlComments(sqlSource))
.replace(/\\(["'`])/g, "$1")
.replace(/\\n/g, "\n")
.replace(/\\r/g, "\r")
.replace(/\\t/g, "\t")
.replace(
/\$\{\s*Prisma\s*\.\s*raw\s*\(\s*((?:"(?:\\.|[^"\\])*")|'(?:\\.|[^'\\])*'|`(?:\\.|[^`\\])*`)\s*\)\s*\}/gs,
(_match, literalSource) => {
// Prisma.raw(...) 的静态 identifier 片段也必须按 JS 运行时字符串解码后再进入 SQL 归一化。
return ` ${decodeStaticJavaScriptStringLiteralSource(literalSource) ?? literalSource} `;
}
)
.replace(/\$\{[\s\S]*?\}/g, " ? ")
.replace(/["'`]/g, " ")
.replace(/\s*\+\s*/g, " ")
.replace(/\s+/g, " ");
}
function sqlTableReferencePattern(tableName) {
const identifier = sqlBareIdentifierPattern();
return String.raw`(?:${identifier}\s*\.\s*)?${escapeRegExp(tableName)}`;
}
function sqlAnyTableReferencePattern() {
const identifier = sqlBareIdentifierPattern();
return String.raw`(?:${identifier}\s*\.\s*)?${identifier}`;
}
function sqlFunctionReferencePattern(functionName) {
const identifier = sqlBareIdentifierPattern();
return String.raw`(?:${identifier}\s*\.\s*)?${escapeRegExp(functionName)}\s*(?:\([^)]*\))?`;
}
function sqlSpecificFunctionReferencePattern(functionName) {
const identifier = sqlBareIdentifierPattern();
return String.raw`(?:${identifier}\s*\.\s*)?${escapeRegExp(functionName)}\b\s*(?:\([^)]*\))?`;
}
function sqlAnyFunctionReferencePattern() {
const identifier = sqlBareIdentifierPattern();
return String.raw`(?:${identifier}\s*\.\s*)?${identifier}\s*(?:\([^)]*\))?`;
}
function sqlFunctionListContainingPattern(targetFunctionReference) {
const functionReference = sqlAnyFunctionReferencePattern();
return String.raw`(?:${functionReference}\s*,\s*)*${targetFunctionReference}(?:\s*,\s*${functionReference})*`;
}
function staticStringLiteralAlternatives(values) {
return values.flatMap((value) => {
const escapedValue = escapeRegExp(value);
return [`"${escapedValue}"`, `'${escapedValue}'`, `\`${escapedValue}\``];
});
}
function staticPrismaPropertyAccessPattern(values) {
const dotNames = values.map(escapeRegExp).join("|");
const bracketNames = staticStringLiteralAlternatives(values).join("|");
return String.raw`(?:(?:\.|\?\.)\s*(?:${dotNames})|(?:\?\.\s*)?\[\s*(?:${bracketNames})\s*\])`;
}
function staticPrismaPropertyAccessMatchAt(content, structure, index, values, beforeIndex = index) {
const allowedValues = new Set(values);
let propertyStart = index;
if (structure.startsWith("?.", index)) {
propertyStart = skipWhitespaceForward(structure, index + 2);
} else if (structure[index] === ".") {
propertyStart = skipWhitespaceForward(structure, index + 1);
}
if (structure[propertyStart] === "[") {
const closeBracketIndex = findMatchingBracket(structure, propertyStart);
if (closeBracketIndex === -1) return null;
const expressionStart = propertyStart + 1;
const expressionEnd = closeBracketIndex;
const resolvedValues = staticJavaScriptExpressionValues(
content,
structure,
content.slice(expressionStart, expressionEnd),
expressionStart,
beforeIndex,
new Set(),
0
);
const value = resolvedValues.find((candidate) => allowedValues.has(candidate));
return value ? { index, end: closeBracketIndex + 1, value } : null;
}
if (propertyStart === index && structure[index] !== "." && !structure.startsWith("?.", index)) return null;
const nameMatch = /^[A-Za-z_$][\w$]*/.exec(structure.slice(propertyStart));
if (!nameMatch || !allowedValues.has(nameMatch[0])) return null;
return { index, end: propertyStart + nameMatch[0].length, value: nameMatch[0] };
}
function staticPrismaPropertyAccessMatches(content, structure, values, startIndex = 0, endIndex = structure.length) {
const matches = [];
for (let cursor = startIndex; cursor < endIndex; cursor += 1) {
if (structure[cursor] !== "." && structure[cursor] !== "[" && !structure.startsWith("?.", cursor)) continue;
const match = staticPrismaPropertyAccessMatchAt(content, structure, cursor, values, cursor);
if (!match || match.end > endIndex) continue;
// computed key 统一走静态 JS 表达式解析,覆盖多段拼接、变量 alias 和完整括号表达式。
matches.push(match);
cursor = match.end - 1;
}
return matches;
}
function staticPrismaDelegateExpressionMatchAt(content, structure, index, models) {
const receiverMatch = /^(?:[A-Za-z_$][\w$]*|this)\b/.exec(structure.slice(index));
if (!receiverMatch) return null;
const modelAccessStart = skipWhitespaceForward(structure, index + receiverMatch[0].length);
const modelAccess = staticPrismaPropertyAccessMatchAt(content, structure, modelAccessStart, models, modelAccessStart);
return modelAccess ? { index, end: modelAccess.end, model: modelAccess.value } : null;
}
function hasOptionalCallOperatorAt(structure, index) {
const callStart = skipWhitespaceForward(structure, index);
if (structure.startsWith("?.", callStart)) {
return skipWhitespaceForward(structure, callStart + 2);
}
return callStart;
}
function functionPrototypeMethodKind(structure, sourceStart, sourceEnd) {
const start = skipWhitespaceForward(structure, sourceStart);
const end = skipWhitespaceBackward(structure, sourceEnd - 1) + 1;
if (start >= end) return null;
const normalizedSource = structure.slice(start, end).replace(/\s+/g, "");
if (normalizedSource === "Function.prototype.call") return "call";
if (normalizedSource === "Function.prototype.apply") return "apply";
return null;
}
function staticBuiltinInvokerKind(structure, sourceStart, sourceEnd) {
const functionPrototypeKind = functionPrototypeMethodKind(structure, sourceStart, sourceEnd);
if (functionPrototypeKind) return functionPrototypeKind;
const start = skipWhitespaceForward(structure, sourceStart);
const end = skipWhitespaceBackward(structure, sourceEnd - 1) + 1;
if (start >= end) return null;
const normalizedSource = structure.slice(start, end).replace(/\s+/g, "");
return normalizedSource === "Reflect.apply" ? "reflectApply" : null;
}
function staticBuiltinInvokerBaseExpressionMatchAt(structure, index) {
return staticBuiltinInvokerBaseExpressionMatchAtWithContent("", structure, index);
}
function staticBuiltinInvokerBaseExpressionMatchAtWithContent(content, structure, index) {
const expressionStart = skipWhitespaceForward(structure, index);
const rootMatch = /^(?:Function|Reflect)\b/.exec(structure.slice(expressionStart));
if (!rootMatch) return null;
const firstAccessStart = skipWhitespaceForward(structure, expressionStart + rootMatch[0].length);
const firstAccess = staticStaticMemberPropertyAccessMatchAt(content, structure, firstAccessStart);
if (!firstAccess) return null;
if (rootMatch[0] === "Reflect") {
return firstAccess.value === "apply" ? { kind: "reflectApply", end: firstAccess.end } : null;
}
if (firstAccess.value !== "prototype") return null;
const secondAccessStart = skipWhitespaceForward(structure, firstAccess.end);
const secondAccess = staticStaticMemberPropertyAccessMatchAt(content, structure, secondAccessStart);
if (!secondAccess || (secondAccess.value !== "call" && secondAccess.value !== "apply")) return null;
return { kind: secondAccess.value, end: secondAccess.end };
}
function staticMemberOperationMatchAt(content, structure, index, values) {
const accessStart = skipWhitespaceForward(structure, index);
const access = staticStaticMemberPropertyAccessMatchAt(content, structure, accessStart);
if (!access || !values.includes(access.value)) return null;
return { value: access.value, end: access.end };
}
function staticBuiltinMemberOperationMatchAt(content, structure, index, values) {
return staticMemberOperationMatchAt(content, structure, index, values);
}
function staticCallableMemberOperationMatchAt(content, structure, index, values) {
const operation = staticMemberOperationMatchAt(content, structure, index, values);
if (!operation) return null;
const openParenIndex = skipOptionalTypeArguments(structure, operation.end);
if (structure[openParenIndex] !== "(") return null;
const closeParenIndex = findMatchingParen(structure, openParenIndex);
if (closeParenIndex === -1) return null;
return { kind: operation.value, methodNameEnd: operation.end, openParenIndex, closeParenIndex };
}
function isStaticBuiltinInvokerBindReceiver(content, structure, kind, receiverArgument) {
if (!receiverArgument) return false;
const receiverStart = skipWhitespaceForward(structure, receiverArgument.index);
const receiverEnd = skipWhitespaceBackward(structure, receiverArgument.end - 1) + 1;
if (receiverStart >= receiverEnd) return false;
if (kind === "reflectApply") {
// Reflect.apply 的 this 不参与语义,`Reflect.apply.bind(null|undefined|Reflect)` 都等价;只要 target 是静态 Reflect.apply 就登记 alias。
return true;
}
const receiverMatch = staticBuiltinInvokerBaseExpressionMatchAtWithContent(content, structure, receiverStart);
return receiverMatch?.kind === kind && skipWhitespaceForward(structure, receiverMatch.end) === receiverEnd;
}
function staticBuiltinInvokerExpressionMatchAt(content, structure, index) {
const expressionStart = skipWhitespaceForward(structure, index);
const baseMatch = staticBuiltinInvokerBaseExpressionMatchAtWithContent(content, structure, expressionStart);
if (!baseMatch) return null;
const bindMethodStart = skipWhitespaceForward(structure, baseMatch.end);
const bindMethodMatch = staticBuiltinMemberOperationMatchAt(content, structure, bindMethodStart, ["bind"]);
if (!bindMethodMatch) return { kind: baseMatch.kind, end: baseMatch.end, bound: false };
const bindCallStart = skipWhitespaceForward(structure, bindMethodMatch.end);
if (structure[bindCallStart] !== "(") return null;
const bindCallEnd = findMatchingParen(structure, bindCallStart);
if (bindCallEnd === -1) return null;
const receiverArgument = nthCallArgumentSource(content, structure, bindCallStart, bindCallEnd, 0);
if (!isStaticBuiltinInvokerBindReceiver(content, structure, baseMatch.kind, receiverArgument)) return null;
return { kind: baseMatch.kind, end: bindCallEnd + 1, bound: true };
}
function findStaticBuiltinInvokerAliases(content, structure) {
const aliases = [];
const declarationPattern = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\b(?:\s*:[^=;]+)?\s*=/g;
const assignmentPattern = /\b([A-Za-z_$][\w$]*)\b\s*=/g;
for (const declarationMatch of structure.matchAll(declarationPattern)) {
if (typeof declarationMatch.index !== "number") continue;
const equalsIndex = structure.indexOf("=", declarationMatch.index);
if (equalsIndex === -1) continue;
const initializerStart = skipWhitespaceForward(structure, equalsIndex + 1);
const statementEnd = findStatementEnd(structure, initializerStart);
const invokerMatch = staticBuiltinInvokerExpressionMatchAt(content, structure, initializerStart);
if (!invokerMatch) continue;
const aliasEnd = simpleStaticAliasInitializerEnd(structure, invokerMatch.end, statementEnd);
if (aliasEnd === -1) continue;
// 只接受同文件静态内建调用器 alias复杂表达式不进入 scope gate 语义展开。
aliases.push({
name: declarationMatch[1],
kind: invokerMatch.kind,
bound: invokerMatch.bound,
index: declarationMatch.index,
end: aliasEnd
});
}
for (const assignmentMatch of structure.matchAll(assignmentPattern)) {
if (typeof assignmentMatch.index !== "number") continue;
const variableName = assignmentMatch[1];
const variableIndex = assignmentMatch.index + assignmentMatch[0].indexOf(variableName);
if (isMemberIdentifierReference(structure, variableIndex)) continue;
if (/\b(?:const|let|var)\s*$/.test(structure.slice(Math.max(0, assignmentMatch.index - 16), variableIndex))) continue;
const equalsIndex = structure.indexOf("=", assignmentMatch.index + assignmentMatch[0].lastIndexOf("="));
if (equalsIndex === -1) continue;
if ("=><!".includes(structure[equalsIndex - 1] ?? "") || ["=", ">"].includes(structure[equalsIndex + 1] ?? "")) continue;
const initializerStart = skipWhitespaceForward(structure, equalsIndex + 1);
const statementEnd = findStatementEnd(structure, initializerStart);
const invokerMatch = staticBuiltinInvokerExpressionMatchAt(content, structure, initializerStart);
if (!invokerMatch) continue;
const aliasEnd = simpleStaticAliasInitializerEnd(structure, invokerMatch.end, statementEnd);
if (aliasEnd === -1) continue;
// 赋值 alias 覆盖 `let c; c = Function.prototype.call` 这类后续绑定。
aliases.push({ name: variableName, kind: invokerMatch.kind, bound: invokerMatch.bound, index: assignmentMatch.index, end: aliasEnd });
}
return propagateStaticIdentifierAliases(structure, aliases, (sourceAlias, initializer) => ({
name: initializer.name,
kind: sourceAlias.kind,
bound: sourceAlias.bound,
index: initializer.index,
end: initializer.end
}));
}
function hasSimpleIdentifierAssignmentBetween(structure, variableName, startIndex, beforeIndex) {
const assignmentPattern = new RegExp(String.raw`\b${escapeRegExp(variableName)}\b\s*=`, "g");
for (const assignmentMatch of structure.matchAll(assignmentPattern)) {
if (typeof assignmentMatch.index !== "number" || assignmentMatch.index <= startIndex || assignmentMatch.index >= beforeIndex) continue;
const variableIndex = assignmentMatch.index + assignmentMatch[0].indexOf(variableName);
if (isMemberIdentifierReference(structure, variableIndex)) continue;
const equalsIndex = structure.indexOf("=", assignmentMatch.index + assignmentMatch[0].lastIndexOf("="));
if (equalsIndex === -1 || equalsIndex >= beforeIndex) continue;
if ("=><!".includes(structure[equalsIndex - 1] ?? "") || ["=", ">"].includes(structure[equalsIndex + 1] ?? "")) continue;
return true;
}
return false;
}
function latestStaticAliasRecordAt(structure, aliases, aliasName, beforeIndex) {
const latestAlias = aliases
.filter((alias) => alias.name === aliasName && (alias.end ?? alias.index) < beforeIndex)
.toSorted((left, right) => (right.end ?? right.index) - (left.end ?? left.index))[0];
if (!latestAlias) return null;
if (hasSimpleIdentifierAssignmentBetween(structure, aliasName, latestAlias.end ?? latestAlias.index, beforeIndex)) return null;
return latestAlias;
}
function simpleIdentifierAliasInitializers(structure) {
const aliases = [];
const declarationPattern = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\b(?:\s*:[^=;]+)?\s*=/g;
const assignmentPattern = /\b([A-Za-z_$][\w$]*)\b\s*=/g;
for (const declarationMatch of structure.matchAll(declarationPattern)) {
if (typeof declarationMatch.index !== "number") continue;
const equalsIndex = structure.indexOf("=", declarationMatch.index);
if (equalsIndex === -1) continue;
const initializerStart = skipWhitespaceForward(structure, equalsIndex + 1);
const initializerMatch = /^[A-Za-z_$][\w$]*/.exec(structure.slice(initializerStart));
if (!initializerMatch) continue;
const initializerEnd = initializerStart + initializerMatch[0].length;
const statementEnd = findStatementEnd(structure, initializerStart);
const aliasEnd = simpleStaticAliasInitializerEnd(structure, initializerEnd, statementEnd);
if (aliasEnd === -1) continue;
aliases.push({
name: declarationMatch[1],
initializerName: initializerMatch[0],
index: declarationMatch.index,
initializerStart,
end: aliasEnd
});
}
for (const assignmentMatch of structure.matchAll(assignmentPattern)) {
if (typeof assignmentMatch.index !== "number") continue;
const variableName = assignmentMatch[1];
const variableIndex = assignmentMatch.index + assignmentMatch[0].indexOf(variableName);
if (isMemberIdentifierReference(structure, variableIndex)) continue;
if (/\b(?:const|let|var)\s*$/.test(structure.slice(Math.max(0, assignmentMatch.index - 16), variableIndex))) continue;
const equalsIndex = structure.indexOf("=", assignmentMatch.index + assignmentMatch[0].lastIndexOf("="));
if (equalsIndex === -1) continue;
if ("=><!".includes(structure[equalsIndex - 1] ?? "") || ["=", ">"].includes(structure[equalsIndex + 1] ?? "")) continue;
const initializerStart = skipWhitespaceForward(structure, equalsIndex + 1);
const initializerMatch = /^[A-Za-z_$][\w$]*/.exec(structure.slice(initializerStart));
if (!initializerMatch) continue;
const initializerEnd = initializerStart + initializerMatch[0].length;
const statementEnd = findStatementEnd(structure, initializerStart);
const aliasEnd = simpleStaticAliasInitializerEnd(structure, initializerEnd, statementEnd);
if (aliasEnd === -1) continue;
aliases.push({
name: variableName,
initializerName: initializerMatch[0],
index: assignmentMatch.index,
initializerStart,
end: aliasEnd
});
}
return aliases;
}
function propagateStaticIdentifierAliases(structure, aliases, copyAlias) {
const propagated = [...aliases];
const initializers = simpleIdentifierAliasInitializers(structure);
for (let depth = 0; depth < rawSqlVariableResolutionMaxDepth; depth += 1) {
let changed = false;
for (const initializer of initializers) {
if (initializer.name === initializer.initializerName) continue;
if (propagated.some((alias) => alias.name === initializer.name && (alias.end ?? alias.index) === initializer.end)) continue;
const sourceAlias = latestStaticAliasRecordAt(structure, propagated, initializer.initializerName, initializer.initializerStart);
if (!sourceAlias) continue;
propagated.push(copyAlias(sourceAlias, initializer));
changed = true;
}
if (!changed) break;
}
return propagated.toSorted((left, right) => (left.end ?? left.index) - (right.end ?? right.index));
}
function completeStaticWrapperSourceMatch(content, structure, sourceMatcher, sourceStart, sourceEnd, beforeIndex) {
const expressionStart = skipWhitespaceForward(structure, sourceStart);
const expressionEnd = skipWhitespaceBackward(structure, sourceEnd - 1) + 1;
if (expressionStart >= expressionEnd) return null;
const match = sourceMatcher(content, structure, expressionStart, expressionEnd, beforeIndex);
if (!match) return null;
return isAtStaticWrapperExpressionEnd(structure, match.end, expressionEnd) ? match : null;
}
function staticIdentifierAliasSourceMatchAt(structure, aliases, sourceStart, sourceEnd, beforeIndex) {
const aliasName = simpleIdentifierSourceName(structure, sourceStart, sourceEnd);
if (!aliasName) return null;
const alias = latestStaticAliasRecordAt(structure, aliases, aliasName, beforeIndex);
return alias ? { index: sourceStart, end: sourceEnd, alias } : null;
}
function isAtStaticWrapperExpressionEnd(structure, cursor, expressionEnd) {
// wrapper source 已按表达式边界 trim结束判断不能继续越过 `}`、`]` 或 `;`。
return cursor === expressionEnd || (cursor < expressionEnd && skipWhitespaceForward(structure, cursor) === expressionEnd);
}
function staticWrapperSourcesInExpression(content, structure, sourceMatcher, expressionStart, expressionEnd, beforeIndex, basePath, depth) {
if (depth >= rawSqlVariableResolutionMaxDepth) return [];
const wrappers = [];
const unwrapped = unwrapCompleteParenthesizedJavaScriptExpression(structure, expressionStart, expressionEnd);
const valueStart = skipWhitespaceForward(structure, unwrapped.start);
const valueEnd = skipWhitespaceBackward(structure, unwrapped.end - 1) + 1;
if (valueStart >= valueEnd) return wrappers;
const sourceMatch = completeStaticWrapperSourceMatch(content, structure, sourceMatcher, valueStart, valueEnd, beforeIndex);
if (sourceMatch) {
wrappers.push({ path: basePath, source: sourceMatch });
return wrappers;
}
const objectAssignSources = staticObjectAssignArgumentSources(content, structure, valueStart, valueEnd);
if (objectAssignSources.length > 0) {
for (const objectAssignSource of objectAssignSources) {
wrappers.push(
...staticWrapperSourcesInExpression(
content,
structure,
sourceMatcher,
objectAssignSource.index,
objectAssignSource.end,
beforeIndex,
basePath,
depth + 1
)
);
}
return wrappers;
}
if (structure[valueStart] === "{") {
const objectEnd = findMatchingBrace(structure, valueStart);
if (objectEnd === -1 || objectEnd !== valueEnd - 1) return wrappers;
for (const part of destructuringParts(structure, valueStart + 1, objectEnd)) {
const propertyName = staticDestructuringPropertyKeyValue(content, structure, part.start, part.end, beforeIndex);
if (!propertyName) continue;
const keyStart = skipWhitespaceForward(structure, part.start);
const keyEnd = destructuringPropertyKeyEnd(structure, keyStart, part.end);
if (keyEnd === -1) continue;
const colonIndex = skipWhitespaceForward(structure, keyEnd);
if (structure[colonIndex] !== ":") {
const shorthandMatch = completeStaticWrapperSourceMatch(content, structure, sourceMatcher, keyStart, keyEnd, beforeIndex);
if (shorthandMatch) wrappers.push({ path: basePath.concat(propertyName), source: shorthandMatch });
continue;
}
wrappers.push(
...staticWrapperSourcesInExpression(
content,
structure,
sourceMatcher,
skipWhitespaceForward(structure, colonIndex + 1),
part.end,
beforeIndex,
basePath.concat(propertyName),
depth + 1
)
);
}
return wrappers;
}
if (structure[valueStart] === "[") {
const arrayEnd = findMatchingBracket(structure, valueStart);
if (arrayEnd === -1 || arrayEnd !== valueEnd - 1) return wrappers;
const parts = destructuringParts(structure, valueStart + 1, arrayEnd);
for (const [elementIndex, part] of parts.entries()) {
wrappers.push(
...staticWrapperSourcesInExpression(
content,
structure,
sourceMatcher,
part.start,
part.end,
beforeIndex,
basePath.concat(elementIndex),
depth + 1
)
);
}
}
return wrappers;
}
function staticObjectAssignArgumentSources(content, structure, sourceStart, sourceEnd) {
if (!structure.startsWith("Object", sourceStart)) return [];
const assignAccess = staticStaticMemberPropertyAccessMatchAt(content, structure, sourceStart + "Object".length);
if (!assignAccess || assignAccess.value !== "assign") return [];
const openParenIndex = skipWhitespaceForward(structure, assignAccess.end);
if (structure[openParenIndex] !== "(") return [];
const closeParenIndex = findMatchingParen(structure, openParenIndex);
if (closeParenIndex === -1 || skipWhitespaceForward(structure, closeParenIndex + 1) !== sourceEnd) return [];
const sources = [];
let argumentIndex = 1;
while (true) {
const argument = nthCallArgumentSource(content, structure, openParenIndex, closeParenIndex, argumentIndex);
if (!argument) break;
sources.push(argument);
argumentIndex += 1;
}
return sources;
}
function staticObjectWrapperSources(content, structure, sourceMatcher) {
const wrappers = [];
const declarationPattern = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\b(?:\s*:[^=;]+)?\s*=/g;
for (const declarationMatch of structure.matchAll(declarationPattern)) {
if (typeof declarationMatch.index !== "number") continue;
const equalsIndex = structure.indexOf("=", declarationMatch.index);
if (equalsIndex === -1) continue;
const initializerStart = skipWhitespaceForward(structure, equalsIndex + 1);
const statementEnd = findStatementEnd(structure, initializerStart);
const expressionEnd = skipWhitespaceBackward(structure, statementEnd - 1) + 1;
for (const wrapper of staticWrapperSourcesInExpression(
content,
structure,
sourceMatcher,
initializerStart,
expressionEnd,
declarationMatch.index,
[],
0
)) {
if (wrapper.path.length === 0) continue;
wrappers.push({
name: declarationMatch[1],
path: wrapper.path,
kind: "path",
index: declarationMatch.index,
end: statementEnd,
source: wrapper.source
});
}
}
return wrappers;
}
function staticArrayWrapperSources(_content, _structure, _sourceMatcher) {
// 数组 wrapper 已由 staticObjectWrapperSources 的 path-source 递归统一覆盖。
return [];
}
function staticFunctionReturnWrapperSources(content, structure, sourceMatcher) {
const wrappers = [];
const functionPattern = /\bfunction\s+([A-Za-z_$][\w$]*)\s*\([^)]*\)\s*\{/g;
const declarationPattern = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\b(?:\s*:[^=;]+)?\s*=/g;
for (const functionMatch of structure.matchAll(functionPattern)) {
if (typeof functionMatch.index !== "number") continue;
const openBraceIndex = structure.indexOf("{", functionMatch.index);
const closeBraceIndex = openBraceIndex === -1 ? -1 : findMatchingBrace(structure, openBraceIndex);
if (closeBraceIndex === -1) continue;
const bodyStart = openBraceIndex + 1;
const bodyEnd = closeBraceIndex;
const returnPattern = /\breturn\b/g;
returnPattern.lastIndex = bodyStart;
const returnMatch = returnPattern.exec(structure);
if (!returnMatch || returnMatch.index >= bodyEnd) continue;
const expressionStart = skipWhitespaceForward(structure, returnMatch.index + returnMatch[0].length);
const statementEnd = Math.min(findStatementEnd(structure, expressionStart), bodyEnd);
wrappers.push(
...staticFunctionReturnExpressionWrapperSources(
content,
structure,
sourceMatcher,
functionMatch[1],
functionMatch.index,
closeBraceIndex + 1,
expressionStart,
statementEnd
)
);
}
for (const declarationMatch of structure.matchAll(declarationPattern)) {
if (typeof declarationMatch.index !== "number") continue;
const equalsIndex = structure.indexOf("=", declarationMatch.index);
if (equalsIndex === -1) continue;
const initializerStart = skipWhitespaceForward(structure, equalsIndex + 1);
const functionExpressionMatch = /^function\b\s*(?:[A-Za-z_$][\w$]*)?\s*\([^)]*\)\s*\{/.exec(structure.slice(initializerStart));
if (functionExpressionMatch) {
const openBraceIndex = structure.indexOf("{", initializerStart + functionExpressionMatch[0].length - 1);
const closeBraceIndex = openBraceIndex === -1 ? -1 : findMatchingBrace(structure, openBraceIndex);
const statementEnd = closeBraceIndex === -1 ? -1 : findStatementEnd(structure, closeBraceIndex + 1);
if (closeBraceIndex !== -1 && statementEnd !== -1) {
const returnPattern = /\breturn\b/g;
returnPattern.lastIndex = openBraceIndex + 1;
const returnMatch = returnPattern.exec(structure);
if (returnMatch && returnMatch.index < closeBraceIndex) {
const expressionStart = skipWhitespaceForward(structure, returnMatch.index + returnMatch[0].length);
const expressionEnd = Math.min(findStatementEnd(structure, expressionStart), closeBraceIndex);
const aliasEnd = simpleStaticAliasInitializerEnd(structure, closeBraceIndex + 1, statementEnd);
if (aliasEnd !== -1) {
wrappers.push(
...staticFunctionReturnExpressionWrapperSources(
content,
structure,
sourceMatcher,
declarationMatch[1],
declarationMatch.index,
aliasEnd,
expressionStart,
expressionEnd
)
);
}
}
}
continue;
}
let statementEnd = findStatementEnd(structure, initializerStart);
const afterArrow = staticArrowExpressionBodyStart(structure, initializerStart, statementEnd);
if (afterArrow === -1) continue;
if (structure[afterArrow] === "{") {
const closeBraceIndex = findMatchingBrace(structure, afterArrow);
if (closeBraceIndex !== -1) statementEnd = findStatementEnd(structure, closeBraceIndex + 1);
const aliasEnd = closeBraceIndex === -1 ? -1 : simpleStaticAliasInitializerEnd(structure, closeBraceIndex + 1, statementEnd);
if (closeBraceIndex === -1 || aliasEnd === -1) continue;
wrappers.push(
...staticBlockReturnWrapperSources(
content,
structure,
sourceMatcher,
declarationMatch[1],
declarationMatch.index,
aliasEnd,
afterArrow
)
);
continue;
}
const expressionEnd = skipWhitespaceBackward(structure, statementEnd - 1) + 1;
const aliasEnd = simpleStaticAliasInitializerEnd(structure, expressionEnd, statementEnd);
if (aliasEnd === -1) continue;
wrappers.push(
...staticFunctionReturnExpressionWrapperSources(
content,
structure,
sourceMatcher,
declarationMatch[1],
declarationMatch.index,
aliasEnd,
afterArrow,
expressionEnd
)
);
}
return wrappers;
}
function staticBlockReturnWrapperSources(content, structure, sourceMatcher, functionName, functionIndex, functionEnd, openBraceIndex) {
const closeBraceIndex = findMatchingBrace(structure, openBraceIndex);
if (closeBraceIndex === -1) return [];
const bodyStart = openBraceIndex + 1;
const bodyEnd = closeBraceIndex;
const returnPattern = /\breturn\b/g;
returnPattern.lastIndex = bodyStart;
const returnMatch = returnPattern.exec(structure);
if (!returnMatch || returnMatch.index >= bodyEnd) return [];
const nextReturn = returnPattern.exec(structure);
if (nextReturn && nextReturn.index < bodyEnd) return [];
const expressionStart = skipWhitespaceForward(structure, returnMatch.index + returnMatch[0].length);
const statementEnd = Math.min(findStatementEnd(structure, expressionStart), bodyEnd);
return staticFunctionReturnExpressionWrapperSources(
content,
structure,
sourceMatcher,
functionName,
functionIndex,
functionEnd,
expressionStart,
statementEnd
);
}
function staticArrowExpressionBodyStart(structure, initializerStart, statementEnd) {
const arrowStart = skipWhitespaceForward(structure, initializerStart);
let cursor = arrowStart;
if (structure[cursor] === "(") {
const paramsEnd = findMatchingParen(structure, cursor);
if (paramsEnd === -1 || paramsEnd >= statementEnd) return -1;
cursor = skipWhitespaceForward(structure, paramsEnd + 1);
} else {
const identifierMatch = /^[A-Za-z_$][\w$]*/.exec(structure.slice(cursor));
if (!identifierMatch) return -1;
cursor = skipWhitespaceForward(structure, cursor + identifierMatch[0].length);
}
if (structure[cursor] === ":") {
cursor = staticTypeAnnotationArrowIndex(structure, cursor + 1, statementEnd);
if (cursor === -1) return -1;
}
cursor = skipWhitespaceForward(structure, cursor);
if (!structure.startsWith("=>", cursor)) return -1;
return skipWhitespaceForward(structure, cursor + 2);
}
function staticTypeAnnotationArrowIndex(structure, sourceStart, sourceEnd) {
let parenDepth = 0;
let braceDepth = 0;
let bracketDepth = 0;
for (let cursor = sourceStart; cursor < sourceEnd - 1; cursor += 1) {
const char = structure[cursor];
if (char === "(") parenDepth += 1;
if (char === ")") parenDepth = Math.max(0, parenDepth - 1);
if (char === "{") braceDepth += 1;
if (char === "}") braceDepth = Math.max(0, braceDepth - 1);
if (char === "[") bracketDepth += 1;
if (char === "]") bracketDepth = Math.max(0, bracketDepth - 1);
if (structure.startsWith("=>", cursor) && parenDepth === 0 && braceDepth === 0 && bracketDepth === 0) {
return cursor;
}
}
return -1;
}
function staticFunctionReturnExpressionWrapperSources(
content,
structure,
sourceMatcher,
functionName,
functionIndex,
functionEnd,
expressionStart,
expressionEnd
) {
// 函数返回 object/array 时保留内部静态 path供 getBox().raw 与 const { raw } = getBox() 共用。
return staticWrapperSourcesInExpression(content, structure, sourceMatcher, expressionStart, expressionEnd, functionIndex, [], 0).map(
(wrapper) => ({
kind: wrapper.path.length === 0 ? "functionReturn" : "functionReturnPath",
name: functionName,
path: wrapper.path,
index: functionIndex,
end: functionEnd,
source: wrapper.source
})
);
}
function staticWrapperSources(content, structure, sourceMatcher) {
return staticObjectWrapperSources(content, structure, sourceMatcher)
.concat(staticArrayWrapperSources(content, structure, sourceMatcher))
.concat(staticFunctionReturnWrapperSources(content, structure, sourceMatcher))
.toSorted((left, right) => left.end - right.end);
}
function isStaticWrapperActiveAt(structure, wrapper, beforeIndex) {
if (wrapper.end >= beforeIndex) return false;
return !hasSimpleIdentifierAssignmentBetween(structure, wrapper.name, wrapper.end, beforeIndex);
}
function staticArrayIndexAccessMatchAt(structure, index, expectedIndex) {
let bracketStart = skipWhitespaceForward(structure, index);
if (structure.startsWith("?.", bracketStart)) {
bracketStart = skipWhitespaceForward(structure, bracketStart + 2);
}
if (structure[bracketStart] !== "[") return null;
const bracketEnd = findMatchingBracket(structure, bracketStart);
if (bracketEnd === -1) return null;
const indexSource = structure.slice(bracketStart + 1, bracketEnd).trim();
if (!/^\d+$/.test(indexSource) || Number.parseInt(indexSource, 10) !== expectedIndex) return null;
return { index, end: bracketEnd + 1 };
}
function staticWrapperPathAccessMatchAt(content, structure, index, path) {
let cursor = index;
for (const segment of path) {
const accessStart = skipWhitespaceForward(structure, cursor);
if (typeof segment === "number") {
const arrayAccess = staticArrayIndexAccessMatchAt(structure, accessStart, segment);
if (!arrayAccess) return null;
cursor = arrayAccess.end;
continue;
}
const propertyAccess = staticPrismaPropertyAccessMatchAt(content, structure, accessStart, [segment], accessStart);
if (!propertyAccess) return null;
cursor = propertyAccess.end;
}
return { index, end: cursor };
}
function staticWrapperSourceUsageMatchAt(content, structure, wrappers, index, beforeIndex = index) {
const usageStart = skipWhitespaceForward(structure, index);
const nameMatch = /^[A-Za-z_$][\w$]*/.exec(structure.slice(usageStart));
if (!nameMatch || isMemberIdentifierReference(structure, usageStart)) return null;
const name = nameMatch[0];
const afterName = skipWhitespaceForward(structure, usageStart + name.length);
const candidates = wrappers.filter((wrapper) => wrapper.name === name && isStaticWrapperActiveAt(structure, wrapper, beforeIndex));
for (const wrapper of candidates.toSorted((left, right) => right.end - left.end)) {
if (wrapper.kind === "path") {
const pathAccess = staticWrapperPathAccessMatchAt(content, structure, afterName, wrapper.path);
if (pathAccess) return { index: usageStart, end: pathAccess.end, wrapper };
continue;
}
if (wrapper.kind === "functionReturn" || wrapper.kind === "functionReturnPath") {
const callEnd = staticZeroArgumentCallEndAt(content, structure, afterName);
if (callEnd === -1) continue;
if (wrapper.kind === "functionReturn") return { index: usageStart, end: callEnd, wrapper };
const pathAccess = staticWrapperPathAccessMatchAt(content, structure, callEnd, wrapper.path);
if (pathAccess) return { index: usageStart, end: pathAccess.end, wrapper };
}
}
return null;
}
function staticZeroArgumentCallEndAt(content, structure, index) {
let openParenIndex = skipWhitespaceForward(structure, index);
if (structure.startsWith("?.", openParenIndex)) {
openParenIndex = skipWhitespaceForward(structure, openParenIndex + 2);
}
if (structure[openParenIndex] !== "(") return -1;
const closeParenIndex = findMatchingParen(structure, openParenIndex);
if (closeParenIndex === -1) return -1;
return nthCallArgumentSource(content, structure, openParenIndex, closeParenIndex, 0) ? -1 : closeParenIndex + 1;
}
function staticWrapperSourceUsageMatches(content, structure, wrappers) {
const matches = [];
const names = [...new Set(wrappers.map((wrapper) => wrapper.name))];
if (names.length === 0) return matches;
const namePattern = new RegExp(String.raw`\b(${names.map(escapeRegExp).join("|")})\b`, "g");
for (const match of structure.matchAll(namePattern)) {
if (typeof match.index !== "number") continue;
const usage = staticWrapperSourceUsageMatchAt(content, structure, wrappers, match.index, match.index);
if (usage) matches.push(usage);
}
return matches.toSorted((left, right) => left.index - right.index);
}
function findStaticWrapperUsageAliases(content, structure, wrappers) {
const aliases = [];
const declarationPattern = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\b(?:\s*:[^=;]+)?\s*=/g;
const assignmentPattern = /\b([A-Za-z_$][\w$]*)\b\s*=/g;
for (const declarationMatch of structure.matchAll(declarationPattern)) {
if (typeof declarationMatch.index !== "number") continue;
const equalsIndex = structure.indexOf("=", declarationMatch.index);
if (equalsIndex === -1) continue;
const initializerStart = skipWhitespaceForward(structure, equalsIndex + 1);
const statementEnd = findStatementEnd(structure, initializerStart);
const usage = staticWrapperSourceUsageMatchAt(content, structure, wrappers, initializerStart, declarationMatch.index);
if (!usage) continue;
const aliasEnd = simpleStaticAliasInitializerEnd(structure, usage.end, statementEnd);
if (aliasEnd === -1) continue;
aliases.push({ name: declarationMatch[1], index: declarationMatch.index, end: aliasEnd });
}
for (const assignmentMatch of structure.matchAll(assignmentPattern)) {
if (typeof assignmentMatch.index !== "number") continue;
const variableName = assignmentMatch[1];
const variableIndex = assignmentMatch.index + assignmentMatch[0].indexOf(variableName);
if (isMemberIdentifierReference(structure, variableIndex)) continue;
if (/\b(?:const|let|var)\s*$/.test(structure.slice(Math.max(0, assignmentMatch.index - 16), variableIndex))) continue;
const equalsIndex = structure.indexOf("=", assignmentMatch.index + assignmentMatch[0].lastIndexOf("="));
if (equalsIndex === -1) continue;
if ("=><!".includes(structure[equalsIndex - 1] ?? "") || ["=", ">"].includes(structure[equalsIndex + 1] ?? "")) continue;
const initializerStart = skipWhitespaceForward(structure, equalsIndex + 1);
const statementEnd = findStatementEnd(structure, initializerStart);
const usage = staticWrapperSourceUsageMatchAt(content, structure, wrappers, initializerStart, assignmentMatch.index);
if (!usage) continue;
const aliasEnd = simpleStaticAliasInitializerEnd(structure, usage.end, statementEnd);
if (aliasEnd === -1) continue;
aliases.push({ name: variableName, index: assignmentMatch.index, end: aliasEnd });
}
return aliases;
}
function pathsEqual(left, right) {
return left.length === right.length && left.every((value, index) => value === right[index]);
}
function activeWrapperForPath(structure, wrappers, objectName, path, beforeIndex) {
return wrappers
.filter(
(wrapper) =>
wrapper.kind === "path" &&
wrapper.name === objectName &&
pathsEqual(wrapper.path, path) &&
isStaticWrapperActiveAt(structure, wrapper, beforeIndex)
)
.toSorted((left, right) => right.end - left.end)[0];
}
function activeFunctionReturnWrapperForPath(structure, wrappers, functionName, path, beforeIndex) {
return wrappers
.filter(
(wrapper) =>
wrapper.kind === "functionReturnPath" &&
wrapper.name === functionName &&
pathsEqual(wrapper.path, path) &&
isStaticWrapperActiveAt(structure, wrapper, beforeIndex)
)
.toSorted((left, right) => right.end - left.end)[0];
}
function activeWrapperForDestructuringSource(content, structure, wrappers, sourceStart, path, beforeIndex) {
const nameMatch = /^[A-Za-z_$][\w$]*/.exec(structure.slice(sourceStart));
if (!nameMatch || isMemberIdentifierReference(structure, sourceStart)) return null;
const sourceName = nameMatch[0];
const afterName = skipWhitespaceForward(structure, sourceStart + sourceName.length);
const callEnd = staticZeroArgumentCallEndAt(content, structure, afterName);
if (callEnd !== -1) {
const wrapper = activeFunctionReturnWrapperForPath(structure, wrappers, sourceName, path, beforeIndex);
return wrapper ? { wrapper, end: callEnd } : null;
}
const wrapper = activeWrapperForPath(structure, wrappers, sourceName, path, beforeIndex);
return wrapper ? { wrapper, end: sourceStart + sourceName.length } : null;
}
function destructuringAliasBindingsForWrapperPaths(content, structure, bindingStart, bindingEnd, beforeIndex, basePath = []) {
const bindings = [];
for (const part of destructuringParts(structure, bindingStart, bindingEnd)) {
const propertyName = staticDestructuringPropertyKeyValue(content, structure, part.start, part.end, beforeIndex);
if (!propertyName) continue;
const keyStart = skipWhitespaceForward(structure, part.start);
const keyEnd = destructuringPropertyKeyEnd(structure, keyStart, part.end);
if (keyEnd === -1) continue;
const path = basePath.concat(propertyName);
const afterKey = skipWhitespaceForward(structure, keyEnd);
if (structure[afterKey] === ":") {
const valueStart = skipWhitespaceForward(structure, afterKey + 1);
if (structure[valueStart] === "{") {
const nestedEnd = findMatchingBrace(structure, valueStart);
if (nestedEnd !== -1 && nestedEnd < part.end) {
bindings.push(...destructuringAliasBindingsForWrapperPaths(content, structure, valueStart + 1, nestedEnd, beforeIndex, path));
}
continue;
}
const aliasName = destructuringAliasName(structure, part.start, part.end);
if (aliasName) bindings.push({ aliasName, path });
continue;
}
const aliasName = destructuringAliasName(structure, part.start, part.end);
if (aliasName) bindings.push({ aliasName, path });
}
return bindings;
}
function arrayDestructuringAliasBindingsForWrapperPaths(structure, bindingStart, bindingEnd, basePath = []) {
const bindings = [];
const parts = destructuringParts(structure, bindingStart, bindingEnd);
for (const [elementIndex, part] of parts.entries()) {
const valueStart = skipWhitespaceForward(structure, part.start);
const valueEnd = skipWhitespaceBackward(structure, part.end - 1) + 1;
if (valueStart >= valueEnd) continue;
const path = basePath.concat(elementIndex);
if (structure[valueStart] === "[") {
const nestedEnd = findMatchingBracket(structure, valueStart);
if (nestedEnd !== -1 && nestedEnd === valueEnd - 1) {
bindings.push(...arrayDestructuringAliasBindingsForWrapperPaths(structure, valueStart + 1, nestedEnd, path));
}
continue;
}
const aliasMatch = /^[A-Za-z_$][\w$]*/.exec(structure.slice(valueStart, valueEnd));
if (!aliasMatch) continue;
const aliasEnd = valueStart + aliasMatch[0].length;
if (skipWhitespaceForward(structure, aliasEnd) !== valueEnd) continue;
bindings.push({ aliasName: aliasMatch[0], path });
}
return bindings;
}
function findStaticWrapperDestructuredAliases(content, structure, wrappers) {
const aliases = [];
const declarationPattern = /\b(?:const|let|var)\s*\{/g;
for (const declarationMatch of structure.matchAll(declarationPattern)) {
if (typeof declarationMatch.index !== "number") continue;
const openBraceIndex = structure.indexOf("{", declarationMatch.index);
if (openBraceIndex === -1) continue;
const closeBraceIndex = findMatchingBrace(structure, openBraceIndex);
if (closeBraceIndex === -1) continue;
let cursor = skipWhitespaceForward(structure, closeBraceIndex + 1);
if (structure[cursor] === ":") {
const equalsAfterType = structure.indexOf("=", cursor + 1);
const semicolonAfterType = structure.indexOf(";", cursor + 1);
if (equalsAfterType === -1 || (semicolonAfterType !== -1 && semicolonAfterType < equalsAfterType)) continue;
cursor = skipWhitespaceForward(structure, equalsAfterType + 1);
} else {
if (structure[cursor] !== "=") continue;
cursor = skipWhitespaceForward(structure, cursor + 1);
}
const statementEnd = findStatementEnd(structure, cursor);
for (const binding of destructuringAliasBindingsForWrapperPaths(content, structure, openBraceIndex + 1, closeBraceIndex, declarationMatch.index)) {
const source = activeWrapperForDestructuringSource(content, structure, wrappers, cursor, binding.path, declarationMatch.index);
if (!source) continue;
const aliasEnd = simpleStaticAliasInitializerEnd(structure, source.end, statementEnd);
if (aliasEnd === -1) continue;
aliases.push({ name: binding.aliasName, index: declarationMatch.index, end: aliasEnd });
}
}
return aliases;
}
function findStaticWrapperArrayDestructuredAliases(content, structure, wrappers) {
const aliases = [];
const declarationPattern = /\b(?:const|let|var)\s*\[/g;
for (const declarationMatch of structure.matchAll(declarationPattern)) {
if (typeof declarationMatch.index !== "number") continue;
const openBracketIndex = structure.indexOf("[", declarationMatch.index);
if (openBracketIndex === -1) continue;
const closeBracketIndex = findMatchingBracket(structure, openBracketIndex);
if (closeBracketIndex === -1) continue;
let cursor = skipWhitespaceForward(structure, closeBracketIndex + 1);
if (structure[cursor] === ":") {
const equalsAfterType = structure.indexOf("=", cursor + 1);
const semicolonAfterType = structure.indexOf(";", cursor + 1);
if (equalsAfterType === -1 || (semicolonAfterType !== -1 && semicolonAfterType < equalsAfterType)) continue;
cursor = skipWhitespaceForward(structure, equalsAfterType + 1);
} else {
if (structure[cursor] !== "=") continue;
cursor = skipWhitespaceForward(structure, cursor + 1);
}
const statementEnd = findStatementEnd(structure, cursor);
for (const binding of arrayDestructuringAliasBindingsForWrapperPaths(structure, openBracketIndex + 1, closeBracketIndex)) {
const source = activeWrapperForDestructuringSource(content, structure, wrappers, cursor, binding.path, declarationMatch.index);
if (!source) continue;
const aliasEnd = simpleStaticAliasInitializerEnd(structure, source.end, statementEnd);
if (aliasEnd === -1) continue;
aliases.push({ name: binding.aliasName, index: declarationMatch.index, end: aliasEnd });
}
}
return aliases;
}
function findStaticWrapperAssignedDestructuredAliases(content, structure, wrappers) {
const aliases = [];
const assignmentPattern = /\(\s*\{/g;
for (const assignmentMatch of structure.matchAll(assignmentPattern)) {
if (typeof assignmentMatch.index !== "number") continue;
const openParenIndex = structure.indexOf("(", assignmentMatch.index);
if (openParenIndex === -1 || !isParenthesizedExpressionOpen(structure, openParenIndex)) continue;
const openBraceIndex = structure.indexOf("{", openParenIndex);
if (openBraceIndex === -1) continue;
const closeBraceIndex = findMatchingBrace(structure, openBraceIndex);
const closeParenIndex = findMatchingParen(structure, openParenIndex);
if (closeBraceIndex === -1 || closeParenIndex === -1 || closeBraceIndex > closeParenIndex) continue;
let cursor = skipWhitespaceForward(structure, closeBraceIndex + 1);
if (structure[cursor] !== "=") continue;
cursor = skipWhitespaceForward(structure, cursor + 1);
const statementEnd = findStatementEnd(structure, openParenIndex);
for (const binding of destructuringAliasBindingsForWrapperPaths(content, structure, openBraceIndex + 1, closeBraceIndex, assignmentMatch.index)) {
const source = activeWrapperForDestructuringSource(content, structure, wrappers, cursor, binding.path, assignmentMatch.index);
if (!source || skipWhitespaceForward(structure, source.end) !== closeParenIndex) continue;
const aliasEnd = simpleStaticAliasInitializerEnd(structure, closeParenIndex + 1, statementEnd);
if (aliasEnd === -1) continue;
aliases.push({ name: binding.aliasName, index: assignmentMatch.index, end: aliasEnd });
}
}
return aliases;
}
function findStaticWrapperAssignedArrayDestructuredAliases(content, structure, wrappers) {
const aliases = [];
const assignmentPattern = /\(\s*\[/g;
for (const assignmentMatch of structure.matchAll(assignmentPattern)) {
if (typeof assignmentMatch.index !== "number") continue;
const openParenIndex = structure.indexOf("(", assignmentMatch.index);
if (openParenIndex === -1 || !isParenthesizedExpressionOpen(structure, openParenIndex)) continue;
const openBracketIndex = structure.indexOf("[", openParenIndex);
if (openBracketIndex === -1) continue;
const closeBracketIndex = findMatchingBracket(structure, openBracketIndex);
const closeParenIndex = findMatchingParen(structure, openParenIndex);
if (closeBracketIndex === -1 || closeParenIndex === -1 || closeBracketIndex > closeParenIndex) continue;
let cursor = skipWhitespaceForward(structure, closeBracketIndex + 1);
if (structure[cursor] !== "=") continue;
cursor = skipWhitespaceForward(structure, cursor + 1);
const statementEnd = findStatementEnd(structure, openParenIndex);
for (const binding of arrayDestructuringAliasBindingsForWrapperPaths(structure, openBracketIndex + 1, closeBracketIndex)) {
const source = activeWrapperForDestructuringSource(content, structure, wrappers, cursor, binding.path, assignmentMatch.index);
if (!source || skipWhitespaceForward(structure, source.end) !== closeParenIndex) continue;
const aliasEnd = simpleStaticAliasInitializerEnd(structure, closeParenIndex + 1, statementEnd);
if (aliasEnd === -1) continue;
aliases.push({ name: binding.aliasName, index: assignmentMatch.index, end: aliasEnd });
}
}
return aliases;
}
function findStaticWrapperExtractionAliases(content, structure, wrappers) {
return findStaticWrapperUsageAliases(content, structure, wrappers)
.concat(findStaticWrapperDestructuredAliases(content, structure, wrappers))
.concat(findStaticWrapperArrayDestructuredAliases(content, structure, wrappers))
.concat(findStaticWrapperAssignedDestructuredAliases(content, structure, wrappers))
.concat(findStaticWrapperAssignedArrayDestructuredAliases(content, structure, wrappers))
.toSorted((left, right) => left.end - right.end);
}
function staticBuiltinInvokerAliasAt(structure, aliases, aliasName, beforeIndex) {
return latestStaticAliasRecordAt(structure, aliases, aliasName, beforeIndex);
}
function staticBuiltinInvokerAliasKindAt(structure, aliases, aliasName, beforeIndex) {
return staticBuiltinInvokerAliasAt(structure, aliases, aliasName, beforeIndex)?.kind ?? null;
}
function aliasDirectInvocationCallMatches(structure, aliasNames) {
const uniqueAliasNames = [...new Set(aliasNames)];
if (uniqueAliasNames.length === 0) return [];
const matches = [];
const aliasPattern = new RegExp(String.raw`\b(${uniqueAliasNames.map(escapeRegExp).join("|")})\b`, "g");
for (const match of structure.matchAll(aliasPattern)) {
if (typeof match.index !== "number") continue;
const aliasName = match[1];
const aliasIndex = match.index + match[0].indexOf(aliasName);
if (isMemberIdentifierReference(structure, aliasIndex)) continue;
let openParenIndex = skipWhitespaceForward(structure, aliasIndex + aliasName.length);
if (structure.startsWith("?.", openParenIndex)) {
openParenIndex = skipWhitespaceForward(structure, openParenIndex + 2);
}
if (structure[openParenIndex] !== "(") continue;
const closeParenIndex = findMatchingParen(structure, openParenIndex);
if (closeParenIndex === -1) continue;
matches.push({ aliasName, aliasIndex, openParenIndex, closeParenIndex });
}
return matches;
}
function functionPrototypeMethodKindFromArgument(content, structure, argument, aliases) {
if (!argument) return null;
const directKind = functionPrototypeMethodKind(structure, argument.index, argument.end);
if (directKind) return directKind;
const aliasName = simpleIdentifierSourceName(structure, argument.index, argument.end);
if (!aliasName) return null;
const aliasKind = staticBuiltinInvokerAliasKindAt(structure, aliases, aliasName, argument.index);
return aliasKind === "call" || aliasKind === "apply" ? aliasKind : null;
}
function functionPrototypeCallInvocationMatches(content, structure, aliases) {
const matches = [];
const directCallPattern = /\bFunction\b/g;
for (const match of structure.matchAll(directCallPattern)) {
if (typeof match.index !== "number") continue;
const baseMatch = staticBuiltinInvokerBaseExpressionMatchAtWithContent(content, structure, match.index);
if (!baseMatch || (baseMatch.kind !== "call" && baseMatch.kind !== "apply")) continue;
const operation = staticBuiltinMemberOperationMatchAt(content, structure, baseMatch.end, ["call", "apply"]);
if (!operation) continue;
const openParenIndex = skipWhitespaceForward(structure, operation.end);
if (structure[openParenIndex] !== "(") continue;
const closeParenIndex = openParenIndex === -1 ? -1 : findMatchingParen(structure, openParenIndex);
if (closeParenIndex === -1) continue;
matches.push({ index: match.index, methodKind: baseMatch.kind, mode: operation.value, openParenIndex, closeParenIndex });
}
const aliasNames = [...new Set(aliases.filter((alias) => (alias.kind === "call" || alias.kind === "apply") && !alias.bound).map((alias) => alias.name))];
if (aliasNames.length > 0) {
const aliasCallPattern = new RegExp(String.raw`\b(${aliasNames.map(escapeRegExp).join("|")})\b`, "g");
for (const match of structure.matchAll(aliasCallPattern)) {
if (typeof match.index !== "number") continue;
const aliasName = match[1];
const aliasIndex = match.index + match[0].indexOf(aliasName);
if (isMemberIdentifierReference(structure, aliasIndex)) continue;
const aliasKind = staticBuiltinInvokerAliasKindAt(structure, aliases, aliasName, aliasIndex);
if (aliasKind !== "call" && aliasKind !== "apply") continue;
const operation = staticBuiltinMemberOperationMatchAt(content, structure, aliasIndex + aliasName.length, ["call", "apply"]);
if (!operation) continue;
const openParenIndex = skipWhitespaceForward(structure, operation.end);
if (structure[openParenIndex] !== "(") continue;
const closeParenIndex = openParenIndex === -1 ? -1 : findMatchingParen(structure, openParenIndex);
if (closeParenIndex === -1) continue;
matches.push({ index: aliasIndex, methodKind: aliasKind, mode: operation.value, openParenIndex, closeParenIndex });
}
}
const boundAliasNames = [...new Set(aliases.filter((alias) => (alias.kind === "call" || alias.kind === "apply") && alias.bound).map((alias) => alias.name))];
if (boundAliasNames.length === 0) return matches.toSorted((left, right) => left.index - right.index);
for (const match of aliasDirectInvocationCallMatches(structure, boundAliasNames)) {
const { aliasName, aliasIndex, openParenIndex, closeParenIndex } = match;
const alias = staticBuiltinInvokerAliasAt(structure, aliases, aliasName, aliasIndex);
if (!alias?.bound || (alias.kind !== "call" && alias.kind !== "apply")) continue;
matches.push({ index: aliasIndex, methodKind: alias.kind, mode: "call", openParenIndex, closeParenIndex });
}
return matches.toSorted((left, right) => left.index - right.index);
}
function functionPrototypeEquivalentArgumentSource(content, structure, invocation, argumentIndex) {
if (invocation.mode === "call") {
return nthCallArgumentSource(content, structure, invocation.openParenIndex, invocation.closeParenIndex, argumentIndex);
}
if (invocation.mode === "apply") {
if (argumentIndex === 0) {
return nthCallArgumentSource(content, structure, invocation.openParenIndex, invocation.closeParenIndex, 0);
}
const arrayArgument = nthCallArgumentSource(content, structure, invocation.openParenIndex, invocation.closeParenIndex, 1);
return nthArrayElementSource(content, structure, arrayArgument, argumentIndex - 1);
}
return null;
}
function functionPrototypeBoundTargetExpressionMatchAt(content, structure, index) {
const expressionStart = skipWhitespaceForward(structure, index);
const baseMatch = staticBuiltinInvokerBaseExpressionMatchAtWithContent(content, structure, expressionStart);
if (!baseMatch || (baseMatch.kind !== "call" && baseMatch.kind !== "apply")) return null;
const bindMethodStart = skipWhitespaceForward(structure, baseMatch.end);
const bindMethodMatch = staticBuiltinMemberOperationMatchAt(content, structure, bindMethodStart, ["bind"]);
if (!bindMethodMatch) return null;
const bindCallStart = skipWhitespaceForward(structure, bindMethodMatch.end);
if (structure[bindCallStart] !== "(") return null;
const bindCallEnd = findMatchingParen(structure, bindCallStart);
if (bindCallEnd === -1) return null;
const targetArgument = nthCallArgumentSource(content, structure, bindCallStart, bindCallEnd, 0);
if (!targetArgument) return null;
return { methodKind: baseMatch.kind, targetArgument, end: bindCallEnd + 1 };
}
function findFunctionPrototypeBoundTargetAliases(content, structure) {
const aliases = [];
const declarationPattern = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\b(?:\s*:[^=;]+)?\s*=/g;
const assignmentPattern = /\b([A-Za-z_$][\w$]*)\b\s*=/g;
for (const declarationMatch of structure.matchAll(declarationPattern)) {
if (typeof declarationMatch.index !== "number") continue;
const equalsIndex = structure.indexOf("=", declarationMatch.index);
if (equalsIndex === -1) continue;
const initializerStart = skipWhitespaceForward(structure, equalsIndex + 1);
const statementEnd = findStatementEnd(structure, initializerStart);
const boundTargetMatch = functionPrototypeBoundTargetExpressionMatchAt(content, structure, initializerStart);
if (!boundTargetMatch) continue;
const aliasEnd = simpleStaticAliasInitializerEnd(structure, boundTargetMatch.end, statementEnd);
if (aliasEnd === -1) continue;
// 绑定 forbidden target 的 wrapper alias 只跟踪简单同文件声明,复杂重赋值保持未知。
aliases.push({
name: declarationMatch[1],
methodKind: boundTargetMatch.methodKind,
targetArgument: boundTargetMatch.targetArgument,
index: declarationMatch.index,
end: aliasEnd
});
}
for (const assignmentMatch of structure.matchAll(assignmentPattern)) {
if (typeof assignmentMatch.index !== "number") continue;
const variableName = assignmentMatch[1];
const variableIndex = assignmentMatch.index + assignmentMatch[0].indexOf(variableName);
if (isMemberIdentifierReference(structure, variableIndex)) continue;
if (/\b(?:const|let|var)\s*$/.test(structure.slice(Math.max(0, assignmentMatch.index - 16), variableIndex))) continue;
const equalsIndex = structure.indexOf("=", assignmentMatch.index + assignmentMatch[0].lastIndexOf("="));
if (equalsIndex === -1) continue;
if ("=><!".includes(structure[equalsIndex - 1] ?? "") || ["=", ">"].includes(structure[equalsIndex + 1] ?? "")) continue;
const initializerStart = skipWhitespaceForward(structure, equalsIndex + 1);
const statementEnd = findStatementEnd(structure, initializerStart);
const boundTargetMatch = functionPrototypeBoundTargetExpressionMatchAt(content, structure, initializerStart);
if (!boundTargetMatch) continue;
const aliasEnd = simpleStaticAliasInitializerEnd(structure, boundTargetMatch.end, statementEnd);
if (aliasEnd === -1) continue;
aliases.push({
name: variableName,
methodKind: boundTargetMatch.methodKind,
targetArgument: boundTargetMatch.targetArgument,
index: assignmentMatch.index,
end: aliasEnd
});
}
return propagateStaticIdentifierAliases(structure, aliases, (sourceAlias, initializer) => ({
name: initializer.name,
methodKind: sourceAlias.methodKind,
targetArgument: sourceAlias.targetArgument,
index: initializer.index,
end: initializer.end
}));
}
function functionPrototypeBoundTargetAliasAt(structure, aliases, aliasName, beforeIndex) {
return latestStaticAliasRecordAt(structure, aliases, aliasName, beforeIndex);
}
function functionPrototypeBoundTargetInvocationMatches(content, structure, aliases) {
const matches = [];
const directPattern = /\bFunction\b/g;
for (const match of structure.matchAll(directPattern)) {
if (typeof match.index !== "number") continue;
const boundTargetMatch = functionPrototypeBoundTargetExpressionMatchAt(content, structure, match.index);
if (!boundTargetMatch) continue;
const openParenIndex = skipWhitespaceForward(structure, boundTargetMatch.end);
if (structure[openParenIndex] !== "(") continue;
const closeParenIndex = findMatchingParen(structure, openParenIndex);
if (closeParenIndex === -1) continue;
matches.push({
index: match.index,
methodKind: boundTargetMatch.methodKind,
targetArgument: boundTargetMatch.targetArgument,
openParenIndex,
closeParenIndex
});
}
const aliasNames = [...new Set(aliases.map((alias) => alias.name))];
if (aliasNames.length === 0) return matches;
for (const match of aliasDirectInvocationCallMatches(structure, aliasNames)) {
const { aliasName, aliasIndex, openParenIndex, closeParenIndex } = match;
const alias = functionPrototypeBoundTargetAliasAt(structure, aliases, aliasName, aliasIndex);
if (!alias) continue;
matches.push({
index: aliasIndex,
methodKind: alias.methodKind,
targetArgument: alias.targetArgument,
openParenIndex,
closeParenIndex
});
}
return matches.toSorted((left, right) => left.index - right.index);
}
function reflectApplyInvocationMatches(content, structure, aliases) {
const matches = [];
const directReflectApplyPattern = /\bReflect\b/g;
for (const match of structure.matchAll(directReflectApplyPattern)) {
if (typeof match.index !== "number") continue;
const baseMatch = staticBuiltinInvokerBaseExpressionMatchAtWithContent(content, structure, match.index);
if (!baseMatch || baseMatch.kind !== "reflectApply") continue;
const operation = staticBuiltinMemberOperationMatchAt(content, structure, baseMatch.end, ["call", "apply"]);
if (operation) {
const openParenIndex = skipWhitespaceForward(structure, operation.end);
if (structure[openParenIndex] !== "(") continue;
const closeParenIndex = findMatchingParen(structure, openParenIndex);
if (closeParenIndex === -1) continue;
matches.push({ index: match.index, mode: operation.value, openParenIndex, closeParenIndex });
continue;
}
const openParenIndex = skipWhitespaceForward(structure, baseMatch.end);
if (structure[openParenIndex] !== "(") continue;
const closeParenIndex = findMatchingParen(structure, openParenIndex);
if (closeParenIndex === -1) continue;
matches.push({ index: match.index, mode: "direct", openParenIndex, closeParenIndex });
}
const aliasNames = [...new Set(aliases.filter((alias) => alias.kind === "reflectApply").map((alias) => alias.name))];
if (aliasNames.length === 0) return matches;
const secondOrderAliasNames = [...new Set(aliases.filter((alias) => alias.kind === "reflectApply" && !alias.bound).map((alias) => alias.name))];
const aliasSecondOrderPattern =
secondOrderAliasNames.length > 0
? new RegExp(String.raw`\b(${secondOrderAliasNames.map(escapeRegExp).join("|")})\b\s*(?:\.|\?\.)\s*(call|apply)\s*\(`, "g")
: null;
for (const match of aliasDirectInvocationCallMatches(structure, aliasNames)) {
const { aliasName, aliasIndex, openParenIndex, closeParenIndex } = match;
const aliasKind = staticBuiltinInvokerAliasKindAt(structure, aliases, aliasName, aliasIndex);
if (aliasKind !== "reflectApply") continue;
matches.push({ index: aliasIndex, mode: "direct", openParenIndex, closeParenIndex });
}
if (aliasSecondOrderPattern) {
const aliasSecondOrderNamePattern = new RegExp(String.raw`\b(${secondOrderAliasNames.map(escapeRegExp).join("|")})\b`, "g");
for (const match of structure.matchAll(aliasSecondOrderNamePattern)) {
if (typeof match.index !== "number") continue;
const aliasName = match[1];
const aliasIndex = match.index + match[0].indexOf(aliasName);
if (isMemberIdentifierReference(structure, aliasIndex)) continue;
const aliasKind = staticBuiltinInvokerAliasKindAt(structure, aliases, aliasName, aliasIndex);
if (aliasKind !== "reflectApply") continue;
const operation = staticBuiltinMemberOperationMatchAt(content, structure, aliasIndex + aliasName.length, ["call", "apply"]);
if (!operation) continue;
const openParenIndex = skipWhitespaceForward(structure, operation.end);
if (structure[openParenIndex] !== "(") continue;
const closeParenIndex = openParenIndex === -1 ? -1 : findMatchingParen(structure, openParenIndex);
if (closeParenIndex === -1) continue;
matches.push({ index: aliasIndex, mode: operation.value, openParenIndex, closeParenIndex });
}
}
return matches.toSorted((left, right) => left.index - right.index);
}
function reflectApplyEquivalentArgumentSource(content, structure, invocation, argumentIndex) {
if (invocation.mode === "direct") {
return nthCallArgumentSource(content, structure, invocation.openParenIndex, invocation.closeParenIndex, argumentIndex);
}
if (invocation.mode === "call") {
return nthCallArgumentSource(content, structure, invocation.openParenIndex, invocation.closeParenIndex, argumentIndex + 1);
}
if (invocation.mode === "apply") {
const arrayArgument = nthCallArgumentSource(content, structure, invocation.openParenIndex, invocation.closeParenIndex, 1);
return nthArrayElementSource(content, structure, arrayArgument, argumentIndex);
}
return null;
}
function prismaModelMethodCallPattern(models, methods) {
return new RegExp(
`${staticPrismaPropertyAccessPattern(models)}\\s*${staticPrismaPropertyAccessPattern(methods)}\\s*(?:\\?\\.)?\\s*\\(`,
"g"
);
}
function findStaticPrismaDelegateAliases(content, structure, models) {
const aliases = [];
const declarationPattern = new RegExp(
String.raw`\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\b(?:\s*:[^=;]+)?\s*=`,
"g"
);
for (const match of structure.matchAll(declarationPattern)) {
if (typeof match.index !== "number") continue;
const equalsIndex = structure.indexOf("=", match.index);
if (equalsIndex === -1) continue;
const initializerStart = skipWhitespaceForward(structure, equalsIndex + 1);
const statementEnd = findStatementEnd(structure, initializerStart);
const branchDelegateMatch = staticInitializerBranchMatch(
content,
structure,
initializerStart,
statementEnd,
(scanContent, scanStructure, sourceStart, sourceEnd) =>
completeStaticPrismaDelegateAliasExpressionMatch(scanContent, scanStructure, sourceStart, sourceEnd, models)
);
if (branchDelegateMatch?.branch) {
// 条件/逻辑分支里只要出现受保护 Prisma delegate后续 alias.method(...) 必须按真实 delegate 写入扫描。
aliases.push({ name: match[1], index: match.index, end: statementEnd });
continue;
}
const delegateMatch = staticPrismaDelegateExpressionMatchAt(content, structure, initializerStart, models);
if (!delegateMatch) continue;
const nextIndex = skipWhitespaceForward(structure, delegateMatch.end);
// 只接受 `const jobs = db.job;` 这类完整 delegate alias避免把 `db.job.update` 误判成 alias。
if (structure[nextIndex] === "." || structure[nextIndex] === "[" || structure[nextIndex] === "(" || structure[nextIndex] === "?") {
continue;
}
const aliasEnd = simpleStaticAliasInitializerEnd(structure, delegateMatch.end, statementEnd);
if (aliasEnd === -1) continue;
aliases.push({ name: match[1], index: match.index, end: aliasEnd });
}
aliases.push(...findStaticPrismaDestructuredDelegateAliases(content, structure, models));
aliases.push(...findStaticPrismaAssignedDestructuredDelegateAliases(content, structure, models));
aliases.push(...findStaticPrismaAssignedDelegateAliases(content, structure, models));
const directAliases = propagateStaticIdentifierAliases(structure, aliases, (_sourceAlias, initializer) => ({
name: initializer.name,
index: initializer.index,
end: initializer.end
}));
const wrappers = prismaDelegateWrapperSources(content, structure, models, directAliases);
directAliases.push(...findStaticWrapperExtractionAliases(content, structure, wrappers));
return propagateStaticIdentifierAliases(structure, directAliases, (_sourceAlias, initializer) => ({
name: initializer.name,
index: initializer.index,
end: initializer.end
}));
}
function findStaticPrismaDestructuredDelegateAliases(content, structure, models) {
const aliases = [];
const declarationPattern = /\b(?:const|let|var)\s*\{/g;
for (const declarationMatch of structure.matchAll(declarationPattern)) {
if (typeof declarationMatch.index !== "number") continue;
const openBraceIndex = structure.indexOf("{", declarationMatch.index);
if (openBraceIndex === -1) continue;
const closeBraceIndex = findMatchingBrace(structure, openBraceIndex);
if (closeBraceIndex === -1) continue;
const names = destructuredAliasNamesForStaticKeys(content, structure, openBraceIndex + 1, closeBraceIndex, models, declarationMatch.index);
if (names.length === 0) continue;
let cursor = skipWhitespaceForward(structure, closeBraceIndex + 1);
if (structure[cursor] === ":") {
const equalsAfterType = structure.indexOf("=", cursor + 1);
const semicolonAfterType = structure.indexOf(";", cursor + 1);
if (equalsAfterType === -1 || (semicolonAfterType !== -1 && semicolonAfterType < equalsAfterType)) continue;
cursor = skipWhitespaceForward(structure, equalsAfterType + 1);
} else {
if (structure[cursor] !== "=") continue;
cursor = skipWhitespaceForward(structure, cursor + 1);
}
const receiverMatch = simpleStaticMemberChainMatchAt(content, structure, cursor);
if (!receiverMatch) continue;
// 解构 alias 只跟踪静态 Prisma client receiver避免把运行时对象结构误判成 delegate。
const expressionEnd = cursor + receiverMatch[0].length;
const statementEnd = findStatementEnd(structure, cursor);
const aliasEnd = simpleStaticAliasInitializerEnd(structure, expressionEnd, statementEnd);
if (aliasEnd === -1) continue;
for (const name of names) {
aliases.push({ name, index: declarationMatch.index, end: aliasEnd });
}
}
return aliases;
}
function findStaticPrismaAssignedDestructuredDelegateAliases(content, structure, models) {
const aliases = [];
const assignmentPattern = /\(\s*\{/g;
for (const assignmentMatch of structure.matchAll(assignmentPattern)) {
if (typeof assignmentMatch.index !== "number") continue;
const openParenIndex = structure.indexOf("(", assignmentMatch.index);
if (openParenIndex === -1 || !isParenthesizedExpressionOpen(structure, openParenIndex)) continue;
const openBraceIndex = structure.indexOf("{", openParenIndex);
if (openBraceIndex === -1) continue;
const closeBraceIndex = findMatchingBrace(structure, openBraceIndex);
const closeParenIndex = findMatchingParen(structure, openParenIndex);
if (closeBraceIndex === -1 || closeParenIndex === -1 || closeBraceIndex > closeParenIndex) continue;
const names = destructuredAliasNamesForStaticKeys(content, structure, openBraceIndex + 1, closeBraceIndex, models, assignmentMatch.index);
if (names.length === 0) continue;
let cursor = skipWhitespaceForward(structure, closeBraceIndex + 1);
if (structure[cursor] !== "=") continue;
cursor = skipWhitespaceForward(structure, cursor + 1);
const receiverMatch = simpleStaticMemberChainMatchAt(content, structure, cursor);
if (!receiverMatch) continue;
const expressionEnd = cursor + receiverMatch[0].length;
if (skipWhitespaceForward(structure, expressionEnd) !== closeParenIndex) continue;
// 赋值式解构 alias 覆盖 `let versions; ({ gameVersion: versions } = db)`。
const statementEnd = findStatementEnd(structure, openParenIndex);
const aliasEnd = simpleStaticAliasInitializerEnd(structure, closeParenIndex + 1, statementEnd);
if (aliasEnd === -1) continue;
for (const name of names) {
aliases.push({ name, index: assignmentMatch.index, end: aliasEnd });
}
}
return aliases;
}
function isStaticAliasExpressionContinuation(structure, index) {
if (index >= structure.length) return false;
if (structure.startsWith("?.", index)) return true;
return "([.`+-/*%,?:&|<>=!".includes(structure[index]);
}
function simpleStaticAliasInitializerEnd(structure, expressionEnd, statementEnd) {
const nextTokenIndex = skipWhitespaceForward(structure, expressionEnd);
if (nextTokenIndex === statementEnd) return statementEnd;
const gapAfterExpression = structure.slice(expressionEnd, nextTokenIndex);
if (!hasLineTerminator(gapAfterExpression)) return -1;
if (isStaticAliasExpressionContinuation(structure, nextTokenIndex)) return -1;
return expressionEnd;
}
function completeStaticPrismaDelegateAliasExpressionMatch(content, structure, sourceStart, sourceEnd, models) {
const unwrapped = unwrapCompleteParenthesizedJavaScriptExpression(structure, sourceStart, sourceEnd);
const expressionStart = skipWhitespaceForward(structure, unwrapped.start);
const expressionEnd = skipWhitespaceBackward(structure, unwrapped.end - 1) + 1;
if (expressionStart >= expressionEnd) return null;
const delegateMatch = staticPrismaDelegateExpressionMatchAt(content, structure, expressionStart, models);
if (!delegateMatch || delegateMatch.end > expressionEnd) return null;
if (!isAtStaticWrapperExpressionEnd(structure, delegateMatch.end, expressionEnd)) return null;
return { index: expressionStart, end: expressionEnd };
}
function findStaticPrismaAssignedDelegateAliases(content, structure, models) {
const aliases = [];
const assignmentPattern = /\b([A-Za-z_$][\w$]*)\b\s*=/g;
for (const assignmentMatch of structure.matchAll(assignmentPattern)) {
if (typeof assignmentMatch.index !== "number") continue;
if (isMemberIdentifierReference(structure, assignmentMatch.index)) continue;
const equalsIndex = structure.indexOf("=", assignmentMatch.index + assignmentMatch[0].lastIndexOf("="));
if (equalsIndex === -1) continue;
if ("=><!".includes(structure[equalsIndex - 1] ?? "") || ["=", ">"].includes(structure[equalsIndex + 1] ?? "")) continue;
const initializerStart = skipWhitespaceForward(structure, equalsIndex + 1);
const statementEnd = findStatementEnd(structure, initializerStart);
const branchDelegateMatch = staticInitializerBranchMatch(
content,
structure,
initializerStart,
statementEnd,
(scanContent, scanStructure, sourceStart, sourceEnd) =>
completeStaticPrismaDelegateAliasExpressionMatch(scanContent, scanStructure, sourceStart, sourceEnd, models)
);
if (branchDelegateMatch?.branch) {
// 后续赋值同样按 fail-closed 处理 mixed ternary/logical delegate source。
aliases.push({ name: assignmentMatch[1], index: assignmentMatch.index, end: statementEnd });
continue;
}
const delegateMatch = staticPrismaDelegateExpressionMatchAt(content, structure, initializerStart, models);
if (!delegateMatch) continue;
if (delegateMatch.end > statementEnd) continue;
const nextIndex = skipWhitespaceForward(structure, delegateMatch.end);
if (structure[nextIndex] === "." || structure[nextIndex] === "[" || structure[nextIndex] === "(" || structure[nextIndex] === "?") {
continue;
}
const aliasEnd = simpleStaticAliasInitializerEnd(structure, delegateMatch.end, statementEnd);
if (aliasEnd === -1) continue;
// 后置赋值 alias 只接受 `jobs = db.job` 这种完整 delegate避免比较、箭头或复杂表达式误判。
aliases.push({ name: assignmentMatch[1], index: assignmentMatch.index, end: aliasEnd });
}
return aliases;
}
function prismaDelegateSourceMatcher(models, aliasResolver = null) {
return (content, structure, sourceStart, sourceEnd) => {
const delegateMatch = staticPrismaDelegateExpressionMatchAt(content, structure, sourceStart, models);
if (delegateMatch && isAtStaticWrapperExpressionEnd(structure, delegateMatch.end, sourceEnd)) return delegateMatch;
return aliasResolver ? aliasResolver(structure, sourceStart, sourceEnd) : null;
};
}
function prismaDelegateWrapperSources(content, structure, models, aliases = []) {
const aliasResolver = (scanStructure, sourceStart, sourceEnd) =>
staticIdentifierAliasSourceMatchAt(scanStructure, aliases, sourceStart, sourceEnd, sourceStart);
return staticWrapperSources(content, structure, prismaDelegateSourceMatcher(models, aliasResolver));
}
function prismaModelMethodSourceMatcher(models, methods, aliases, methodAliases = []) {
return (content, structure, sourceStart, sourceEnd) => {
const methodAccess = staticPrismaModelMethodAccessMatchAt(content, structure, sourceStart, models, methods, aliases);
if (methodAccess) {
if (isAtStaticWrapperExpressionEnd(structure, methodAccess.end, sourceEnd)) return methodAccess;
const bindCloseParenIndex = prismaMethodBindCloseParenIndex(content, structure, methodAccess.end, sourceEnd);
// 函数返回或 Object.assign wrapper 里出现 `db.model.method.bind(db.model)` 时,后续调用仍等价于直接 Prisma 写。
if (bindCloseParenIndex !== -1 && isAtStaticWrapperExpressionEnd(structure, bindCloseParenIndex + 1, sourceEnd)) {
return { index: methodAccess.index, end: bindCloseParenIndex + 1 };
}
}
return staticIdentifierAliasSourceMatchAt(structure, methodAliases, sourceStart, sourceEnd, sourceStart);
};
}
function prismaModelMethodWrapperSources(content, structure, models, methods, aliases, methodAliases = []) {
return staticWrapperSources(content, structure, prismaModelMethodSourceMatcher(models, methods, aliases, methodAliases));
}
function prismaModelMethodWrapperAliases(content, structure, methodWrappers) {
return propagateStaticIdentifierAliases(
structure,
findStaticWrapperExtractionAliases(content, structure, methodWrappers),
(_sourceAlias, initializer) => ({
name: initializer.name,
index: initializer.index,
end: initializer.end
})
);
}
function staticPrismaDelegateExpressionOrAliasMatchAt(content, structure, index, models, aliases = []) {
const delegateMatch = staticPrismaDelegateExpressionMatchAt(content, structure, index, models);
if (delegateMatch) return delegateMatch;
const aliasMatch = /^[A-Za-z_$][\w$]*/.exec(structure.slice(index));
if (!aliasMatch || isMemberIdentifierReference(structure, index)) return null;
const aliasName = aliasMatch[0];
if (!latestStaticAliasRecordAt(structure, aliases, aliasName, index)) return null;
return { index, end: index + aliasName.length };
}
function findStaticPrismaDestructuredModelMethodAliases(content, structure, models, methods, aliases) {
const methodAliases = [];
const declarationPattern = /\b(?:const|let|var)\s*\{/g;
for (const declarationMatch of structure.matchAll(declarationPattern)) {
if (typeof declarationMatch.index !== "number") continue;
const openBraceIndex = structure.indexOf("{", declarationMatch.index);
if (openBraceIndex === -1) continue;
const closeBraceIndex = findMatchingBrace(structure, openBraceIndex);
if (closeBraceIndex === -1) continue;
const names = destructuredAliasNamesForStaticKeys(content, structure, openBraceIndex + 1, closeBraceIndex, methods, declarationMatch.index);
if (names.length === 0) continue;
let cursor = skipWhitespaceForward(structure, closeBraceIndex + 1);
if (structure[cursor] === ":") {
const equalsAfterType = structure.indexOf("=", cursor + 1);
const semicolonAfterType = structure.indexOf(";", cursor + 1);
if (equalsAfterType === -1 || (semicolonAfterType !== -1 && semicolonAfterType < equalsAfterType)) continue;
cursor = skipWhitespaceForward(structure, equalsAfterType + 1);
} else {
if (structure[cursor] !== "=") continue;
cursor = skipWhitespaceForward(structure, cursor + 1);
}
const delegateMatch = staticPrismaDelegateExpressionOrAliasMatchAt(content, structure, cursor, models, aliases);
if (!delegateMatch) continue;
const statementEnd = findStatementEnd(structure, cursor);
const aliasEnd = simpleStaticAliasInitializerEnd(structure, delegateMatch.end, statementEnd);
if (aliasEnd === -1) continue;
// Prisma delegate method destructuring例如 `const { delete: del } = db.auditLog`。
for (const name of names) {
methodAliases.push({ name, index: declarationMatch.index, end: aliasEnd });
}
}
return methodAliases;
}
function findStaticPrismaAssignedDestructuredModelMethodAliases(content, structure, models, methods, aliases) {
const methodAliases = [];
const assignmentPattern = /\(\s*\{/g;
for (const assignmentMatch of structure.matchAll(assignmentPattern)) {
if (typeof assignmentMatch.index !== "number") continue;
const openParenIndex = structure.indexOf("(", assignmentMatch.index);
if (openParenIndex === -1 || !isParenthesizedExpressionOpen(structure, openParenIndex)) continue;
const openBraceIndex = structure.indexOf("{", openParenIndex);
if (openBraceIndex === -1) continue;
const closeBraceIndex = findMatchingBrace(structure, openBraceIndex);
const closeParenIndex = findMatchingParen(structure, openParenIndex);
if (closeBraceIndex === -1 || closeParenIndex === -1 || closeBraceIndex > closeParenIndex) continue;
const names = destructuredAliasNamesForStaticKeys(content, structure, openBraceIndex + 1, closeBraceIndex, methods, assignmentMatch.index);
if (names.length === 0) continue;
let cursor = skipWhitespaceForward(structure, closeBraceIndex + 1);
if (structure[cursor] !== "=") continue;
cursor = skipWhitespaceForward(structure, cursor + 1);
const delegateMatch = staticPrismaDelegateExpressionOrAliasMatchAt(content, structure, cursor, models, aliases);
if (!delegateMatch || skipWhitespaceForward(structure, delegateMatch.end) !== closeParenIndex) continue;
const statementEnd = findStatementEnd(structure, openParenIndex);
const aliasEnd = simpleStaticAliasInitializerEnd(structure, closeParenIndex + 1, statementEnd);
if (aliasEnd === -1) continue;
// 赋值式 method destructuring例如 `let del; ({ delete: del } = db.auditLog)`。
for (const name of names) {
methodAliases.push({ name, index: assignmentMatch.index, end: aliasEnd });
}
}
return methodAliases;
}
function findStaticPrismaModelMethodBoundAliases(content, structure, models, methods, aliases, methodWrappers = [], methodAliases = []) {
const boundAliases = [];
const declarationPattern = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\b(?:\s*:[^=;]+)?\s*=/g;
const assignmentPattern = /\b([A-Za-z_$][\w$]*)\b\s*=/g;
for (const declarationMatch of structure.matchAll(declarationPattern)) {
if (typeof declarationMatch.index !== "number") continue;
const equalsIndex = structure.indexOf("=", declarationMatch.index);
if (equalsIndex === -1) continue;
const initializerStart = skipWhitespaceForward(structure, equalsIndex + 1);
const statementEnd = findStatementEnd(structure, initializerStart);
const bindAliasEnd = staticPrismaModelMethodBoundAliasInitializerEnd(
content,
structure,
initializerStart,
statementEnd,
models,
methods,
aliases,
methodWrappers,
methodAliases
);
if (bindAliasEnd === -1) continue;
const aliasEnd = simpleStaticAliasInitializerEnd(structure, bindAliasEnd, statementEnd);
if (aliasEnd === -1) continue;
// Prisma method `.bind(delegate)` alias 后续等价于直接 method invocation。
boundAliases.push({ name: declarationMatch[1], index: declarationMatch.index, end: aliasEnd });
}
for (const assignmentMatch of structure.matchAll(assignmentPattern)) {
if (typeof assignmentMatch.index !== "number") continue;
const variableName = assignmentMatch[1];
const variableIndex = assignmentMatch.index + assignmentMatch[0].indexOf(variableName);
if (isMemberIdentifierReference(structure, variableIndex)) continue;
if (/\b(?:const|let|var)\s*$/.test(structure.slice(Math.max(0, assignmentMatch.index - 16), variableIndex))) continue;
const equalsIndex = structure.indexOf("=", assignmentMatch.index + assignmentMatch[0].lastIndexOf("="));
if (equalsIndex === -1) continue;
if ("=><!".includes(structure[equalsIndex - 1] ?? "") || ["=", ">"].includes(structure[equalsIndex + 1] ?? "")) continue;
const initializerStart = skipWhitespaceForward(structure, equalsIndex + 1);
const statementEnd = findStatementEnd(structure, initializerStart);
const bindAliasEnd = staticPrismaModelMethodBoundAliasInitializerEnd(
content,
structure,
initializerStart,
statementEnd,
models,
methods,
aliases,
methodWrappers,
methodAliases
);
if (bindAliasEnd === -1) continue;
const aliasEnd = simpleStaticAliasInitializerEnd(structure, bindAliasEnd, statementEnd);
if (aliasEnd === -1) continue;
// 后续赋值 alias 覆盖 `let update; update = db.model.method.bind(db.model)`,保持和声明式 bound alias 同一语义。
boundAliases.push({ name: variableName, index: assignmentMatch.index, end: aliasEnd });
}
return boundAliases;
}
function staticPrismaModelMethodBoundAliasInitializerEnd(
content,
structure,
sourceStart,
sourceEnd,
models,
methods,
aliases,
methodWrappers,
methodAliases
) {
const completeMatch = completeStaticPrismaModelMethodBoundAliasExpressionMatch(
content,
structure,
sourceStart,
sourceEnd,
models,
methods,
aliases,
methodWrappers,
methodAliases
);
if (completeMatch) return completeMatch.end;
const branchMatches = staticExpressionBranchSources(structure, sourceStart, sourceEnd)
.map((branch) =>
completeStaticPrismaModelMethodBoundAliasExpressionMatch(
content,
structure,
branch.index,
branch.end,
models,
methods,
aliases,
methodWrappers,
methodAliases
)
)
.filter(Boolean);
// 条件/逻辑表达式中任一静态分支是 guarded/audit/job method bind后续 callable alias 按违规路径扫描。
return branchMatches.length > 0 ? sourceEnd : -1;
}
function completeStaticPrismaModelMethodBoundAliasExpressionMatch(
content,
structure,
sourceStart,
sourceEnd,
models,
methods,
aliases,
methodWrappers,
methodAliases
) {
const unwrapped = unwrapCompleteParenthesizedJavaScriptExpression(structure, sourceStart, sourceEnd);
const expressionStart = skipWhitespaceForward(structure, unwrapped.start);
const expressionEnd = skipWhitespaceBackward(structure, unwrapped.end - 1) + 1;
if (expressionStart >= expressionEnd) return null;
const methodAccess = staticPrismaModelMethodAccessMatchAt(
content,
structure,
expressionStart,
models,
methods,
aliases,
methodWrappers,
methodAliases
);
if (!methodAccess || methodAccess.end > expressionEnd) return null;
const bindCloseParenIndex = prismaMethodBindCloseParenIndex(content, structure, methodAccess.end, expressionEnd);
if (bindCloseParenIndex === -1) return null;
// bind helper 返回的是右括号下标;这里用 exclusive end 严格校验,避免越过 ternary 分支边界的空白继续扫到 `:`。
const bindExpressionEnd = bindCloseParenIndex + 1;
if (bindExpressionEnd !== expressionEnd) return null;
return { index: expressionStart, end: expressionEnd };
}
function completeStaticPrismaModelMethodPlainAliasExpressionMatch(
content,
structure,
sourceStart,
sourceEnd,
models,
methods,
aliases,
methodWrappers,
methodAliases
) {
const unwrapped = unwrapCompleteParenthesizedJavaScriptExpression(structure, sourceStart, sourceEnd);
const expressionStart = skipWhitespaceForward(structure, unwrapped.start);
const expressionEnd = skipWhitespaceBackward(structure, unwrapped.end - 1) + 1;
if (expressionStart >= expressionEnd) return null;
const methodAccess = staticPrismaModelMethodAccessMatchAt(
content,
structure,
expressionStart,
models,
methods,
aliases,
methodWrappers,
methodAliases
);
if (!methodAccess || methodAccess.end > expressionEnd) return null;
if (!isAtStaticWrapperExpressionEnd(structure, methodAccess.end, expressionEnd)) return null;
return { index: expressionStart, end: expressionEnd };
}
function staticPrismaModelMethodPlainAliasInitializerEnd(
content,
structure,
sourceStart,
sourceEnd,
models,
methods,
aliases,
methodWrappers,
methodAliases
) {
const plainMatch = staticInitializerBranchMatch(
content,
structure,
sourceStart,
sourceEnd,
(scanContent, scanStructure, branchStart, branchEnd) =>
completeStaticPrismaModelMethodPlainAliasExpressionMatch(
scanContent,
scanStructure,
branchStart,
branchEnd,
models,
methods,
aliases,
methodWrappers,
methodAliases
)
);
// 条件/逻辑表达式中任一静态分支是 Prisma plain method后续 callable alias 仍按 Prisma 写入扫描。
return plainMatch ? (plainMatch.branch ? sourceEnd : plainMatch.end) : -1;
}
function findStaticPrismaModelMethodPlainAliases(content, structure, models, methods, aliases, methodWrappers = [], methodAliases = []) {
const plainAliases = [];
const declarationPattern = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\b(?:\s*:[^=;]+)?\s*=/g;
const assignmentPattern = /\b([A-Za-z_$][\w$]*)\b\s*=/g;
for (const declarationMatch of structure.matchAll(declarationPattern)) {
if (typeof declarationMatch.index !== "number") continue;
const equalsIndex = structure.indexOf("=", declarationMatch.index);
if (equalsIndex === -1) continue;
const initializerStart = skipWhitespaceForward(structure, equalsIndex + 1);
const statementEnd = findStatementEnd(structure, initializerStart);
const plainAliasEnd = staticPrismaModelMethodPlainAliasInitializerEnd(
content,
structure,
initializerStart,
statementEnd,
models,
methods,
aliases,
methodWrappers,
methodAliases
);
if (plainAliasEnd === -1) continue;
const aliasEnd = simpleStaticAliasInitializerEnd(structure, plainAliasEnd, statementEnd);
if (aliasEnd === -1) continue;
// `const del = db.job.delete` 这类 plain method alias 后续通过 direct/call/apply/bind 都等价于 Prisma method invocation。
plainAliases.push({ name: declarationMatch[1], index: declarationMatch.index, end: aliasEnd });
}
for (const assignmentMatch of structure.matchAll(assignmentPattern)) {
if (typeof assignmentMatch.index !== "number") continue;
const variableName = assignmentMatch[1];
const variableIndex = assignmentMatch.index + assignmentMatch[0].indexOf(variableName);
if (isMemberIdentifierReference(structure, variableIndex)) continue;
if (/\b(?:const|let|var)\s*$/.test(structure.slice(Math.max(0, assignmentMatch.index - 16), variableIndex))) continue;
const equalsIndex = structure.indexOf("=", assignmentMatch.index + assignmentMatch[0].lastIndexOf("="));
if (equalsIndex === -1) continue;
if ("=><!".includes(structure[equalsIndex - 1] ?? "") || ["=", ">"].includes(structure[equalsIndex + 1] ?? "")) continue;
const initializerStart = skipWhitespaceForward(structure, equalsIndex + 1);
const statementEnd = findStatementEnd(structure, initializerStart);
const plainAliasEnd = staticPrismaModelMethodPlainAliasInitializerEnd(
content,
structure,
initializerStart,
statementEnd,
models,
methods,
aliases,
methodWrappers,
methodAliases
);
if (plainAliasEnd === -1) continue;
const aliasEnd = simpleStaticAliasInitializerEnd(structure, plainAliasEnd, statementEnd);
if (aliasEnd === -1) continue;
// 后续赋值 alias 覆盖 `let update; update = db.gameVersion.update`,保持和声明式 plain alias 同一语义。
plainAliases.push({ name: variableName, index: assignmentMatch.index, end: aliasEnd });
}
return plainAliases;
}
function staticExpressionBranchSources(structure, sourceStart, sourceEnd) {
const unwrapped = unwrapCompleteParenthesizedJavaScriptExpression(structure, sourceStart, sourceEnd);
const expressionStart = skipWhitespaceForward(structure, unwrapped.start);
const expressionEnd = skipWhitespaceBackward(structure, unwrapped.end - 1) + 1;
if (expressionStart >= expressionEnd) return [];
const ternary = topLevelTernaryExpressionParts(structure, expressionStart, expressionEnd);
if (ternary) return [ternary.whenTrue, ternary.whenFalse];
const logical = topLevelLogicalExpressionParts(structure, expressionStart, expressionEnd);
return logical ?? [];
}
function staticInitializerBranchMatch(content, structure, sourceStart, sourceEnd, matcher) {
const completeMatch = matcher(content, structure, sourceStart, sourceEnd);
if (completeMatch) return completeMatch;
for (const branch of staticExpressionBranchSources(structure, sourceStart, sourceEnd)) {
// 分支表达式递归展开,确保 `a ? safe : (b || db.model.method)` 这类嵌套静态分支不会 fail-open。
const branchMatch = staticInitializerBranchMatch(content, structure, branch.index, branch.end, matcher);
if (branchMatch) return { ...branchMatch, branch };
}
return null;
}
function topLevelTernaryExpressionParts(structure, sourceStart, sourceEnd) {
let parenDepth = 0;
let braceDepth = 0;
let bracketDepth = 0;
let questionIndex = -1;
for (let cursor = sourceStart; cursor < sourceEnd; cursor += 1) {
const char = structure[cursor];
if (char === "(") parenDepth += 1;
if (char === ")") parenDepth -= 1;
if (char === "{") braceDepth += 1;
if (char === "}") braceDepth -= 1;
if (char === "[") bracketDepth += 1;
if (char === "]") bracketDepth -= 1;
if (parenDepth !== 0 || braceDepth !== 0 || bracketDepth !== 0) continue;
if (char === "?" && structure[cursor + 1] !== ".") {
questionIndex = cursor;
break;
}
}
if (questionIndex === -1) return null;
parenDepth = 0;
braceDepth = 0;
bracketDepth = 0;
let nestedQuestionDepth = 0;
for (let cursor = questionIndex + 1; cursor < sourceEnd; cursor += 1) {
const char = structure[cursor];
if (char === "(") parenDepth += 1;
if (char === ")") parenDepth -= 1;
if (char === "{") braceDepth += 1;
if (char === "}") braceDepth -= 1;
if (char === "[") bracketDepth += 1;
if (char === "]") bracketDepth -= 1;
if (parenDepth !== 0 || braceDepth !== 0 || bracketDepth !== 0) continue;
if (char === "?" && structure[cursor + 1] !== ".") {
nestedQuestionDepth += 1;
continue;
}
if (char !== ":") continue;
if (nestedQuestionDepth > 0) {
nestedQuestionDepth -= 1;
continue;
}
return {
whenTrue: { index: skipWhitespaceForward(structure, questionIndex + 1), end: skipWhitespaceBackward(structure, cursor - 1) + 1 },
whenFalse: { index: skipWhitespaceForward(structure, cursor + 1), end: sourceEnd }
};
}
return null;
}
function topLevelLogicalExpressionParts(structure, sourceStart, sourceEnd) {
let parenDepth = 0;
let braceDepth = 0;
let bracketDepth = 0;
for (let cursor = sourceStart; cursor < sourceEnd - 1; cursor += 1) {
const char = structure[cursor];
if (char === "(") parenDepth += 1;
if (char === ")") parenDepth -= 1;
if (char === "{") braceDepth += 1;
if (char === "}") braceDepth -= 1;
if (char === "[") bracketDepth += 1;
if (char === "]") bracketDepth -= 1;
if (parenDepth !== 0 || braceDepth !== 0 || bracketDepth !== 0) continue;
const operator = structure.slice(cursor, cursor + 2);
if (operator !== "||" && operator !== "&&") continue;
return [
{ index: sourceStart, end: skipWhitespaceBackward(structure, cursor - 1) + 1 },
{ index: skipWhitespaceForward(structure, cursor + 2), end: sourceEnd }
];
}
return null;
}
function prismaMethodBindCloseParenIndex(content, structure, methodAccessEnd, statementEnd) {
const operation = staticCallableMemberOperationMatchAt(content, structure, methodAccessEnd, ["bind"]);
if (!operation) return -1;
if (operation.closeParenIndex === -1 || operation.closeParenIndex > statementEnd) return -1;
return operation.closeParenIndex;
}
function prismaDelegateAliasMethodCallPattern(aliasNames, methods) {
const aliases = aliasNames.map(escapeRegExp).join("|");
return new RegExp(String.raw`\b(${aliases})\b\s*${staticPrismaPropertyAccessPattern(methods)}\s*(?:\?\.)?\s*\(`, "g");
}
function staticPrismaModelMethodAccessMatchAt(content, structure, index, models, methods, aliases = [], methodWrappers = [], methodAliases = []) {
const accessStart = skipWhitespaceForward(structure, index);
const receiverMatch = /^(?:[A-Za-z_$][\w$]*|this)\b/.exec(structure.slice(accessStart));
if (receiverMatch) {
const modelAccessStart = skipWhitespaceForward(structure, accessStart + receiverMatch[0].length);
const modelAccess = staticPrismaPropertyAccessMatchAt(content, structure, modelAccessStart, models, modelAccessStart);
if (modelAccess) {
const methodAccessStart = skipWhitespaceForward(structure, modelAccess.end);
const methodAccess = staticPrismaPropertyAccessMatchAt(content, structure, methodAccessStart, methods, methodAccessStart);
if (methodAccess) return { index: modelAccess.index, end: methodAccess.end };
}
}
const wrapperUsage = staticWrapperSourceUsageMatchAt(content, structure, methodWrappers, accessStart, accessStart);
if (wrapperUsage) return { index: wrapperUsage.index, end: wrapperUsage.end };
const methodAliasMatch = /^[A-Za-z_$][\w$]*/.exec(structure.slice(accessStart));
if (methodAliasMatch && !isMemberIdentifierReference(structure, accessStart)) {
const aliasName = methodAliasMatch[0];
if (latestStaticAliasRecordAt(structure, methodAliases, aliasName, accessStart)) {
return { index: accessStart, end: accessStart + aliasName.length };
}
}
const aliasMatch = /^[A-Za-z_$][\w$]*/.exec(structure.slice(accessStart));
if (!aliasMatch || aliases.length === 0) return null;
const aliasName = aliasMatch[0];
if (!latestStaticAliasRecordAt(structure, aliases, aliasName, accessStart)) return null;
if (isMemberIdentifierReference(structure, accessStart)) return null;
const methodAccessStart = skipWhitespaceForward(structure, accessStart + aliasName.length);
const methodAccess = staticPrismaPropertyAccessMatchAt(content, structure, methodAccessStart, methods, methodAccessStart);
return methodAccess ? { index: accessStart, end: methodAccess.end } : null;
}
function staticPrismaModelMethodAccessArgumentMatch(content, structure, argument, models, methods, aliases, methodWrappers = [], methodAliases = []) {
if (!argument) return null;
const targetStart = skipWhitespaceForward(structure, argument.index);
const targetEnd = skipWhitespaceBackward(structure, argument.end - 1) + 1;
if (targetStart >= targetEnd) return null;
const accessMatch = staticPrismaModelMethodAccessMatchAt(
content,
structure,
targetStart,
models,
methods,
aliases,
methodWrappers,
methodAliases
);
if (!accessMatch) return null;
return skipWhitespaceForward(structure, accessMatch.end) === targetEnd ? accessMatch : null;
}
function prismaMethodEquivalentInvocationMatch(content, structure, methodAccessEnd) {
const callStart = hasOptionalCallOperatorAt(structure, methodAccessEnd);
if (structure[callStart] === "(") return { callStart };
const operation = staticCallableMemberOperationMatchAt(content, structure, methodAccessEnd, ["call", "apply", "bind"]);
if (!operation) return null;
if (operation.kind === "call" || operation.kind === "apply") {
return { callStart: operation.openParenIndex };
}
let boundCallStart = skipWhitespaceForward(structure, operation.closeParenIndex + 1);
if (structure.startsWith("?.", boundCallStart)) {
boundCallStart = skipWhitespaceForward(structure, boundCallStart + 2);
}
if (structure[boundCallStart] !== "(") return null;
const boundCallEnd = findMatchingParen(structure, boundCallStart);
return boundCallEnd === -1 ? null : { callStart: boundCallStart };
}
function prismaFunctionPrototypeMethodCallMatchesInStructure(content, structure, models, methods, aliases, builtinInvokerAliases, methodWrappers, methodAliases) {
const matches = [];
const boundTargetAliases = findFunctionPrototypeBoundTargetAliases(content, structure);
for (const invocation of functionPrototypeCallInvocationMatches(content, structure, builtinInvokerAliases)) {
const targetArgument = functionPrototypeEquivalentArgumentSource(content, structure, invocation, 0);
const targetAccess = staticPrismaModelMethodAccessArgumentMatch(content, structure, targetArgument, models, methods, aliases, methodWrappers, methodAliases);
if (!targetAccess) continue;
// Function.prototype.call/apply 的目标函数在参数位;命中后复用外层调用体做 Prisma 写入扫描。
matches.push({ index: targetAccess.index, callStart: invocation.openParenIndex });
}
for (const invocation of reflectApplyInvocationMatches(content, structure, builtinInvokerAliases)) {
const reflectTargetArgument = reflectApplyEquivalentArgumentSource(content, structure, invocation, 0);
if (!functionPrototypeMethodKindFromArgument(content, structure, reflectTargetArgument, builtinInvokerAliases)) {
const reflectTargetAccess = staticPrismaModelMethodAccessArgumentMatch(
content,
structure,
reflectTargetArgument,
models,
methods,
aliases,
methodWrappers,
methodAliases
);
if (reflectTargetAccess) {
// Reflect.apply(method, receiver, [args]) 等价执行 Prisma method target。
matches.push({ index: reflectTargetAccess.index, callStart: invocation.openParenIndex });
}
continue;
}
const targetArgument = reflectApplyEquivalentArgumentSource(content, structure, invocation, 1);
const targetAccess = staticPrismaModelMethodAccessArgumentMatch(content, structure, targetArgument, models, methods, aliases, methodWrappers, methodAliases);
if (!targetAccess) continue;
matches.push({ index: targetAccess.index, callStart: invocation.openParenIndex });
}
for (const invocation of functionPrototypeBoundTargetInvocationMatches(content, structure, boundTargetAliases)) {
const targetAccess = staticPrismaModelMethodAccessArgumentMatch(
content,
structure,
invocation.targetArgument,
models,
methods,
aliases,
methodWrappers,
methodAliases
);
if (!targetAccess) continue;
// Function.prototype.call/apply.bind(method)(...) 绑定的是真实 Prisma method target。
matches.push({ index: targetAccess.index, callStart: invocation.openParenIndex });
}
return matches;
}
function prismaModelMethodCallMatchesInStructure(content, structure, models, methods) {
const scanStructure = stripParenthesesAroundStaticMemberChains(content, structure);
const matches = [];
for (const modelAccess of staticPrismaPropertyAccessMatches(content, scanStructure, models)) {
const methodAccessStart = skipWhitespaceForward(scanStructure, modelAccess.end);
const methodAccess = staticPrismaPropertyAccessMatchAt(content, scanStructure, methodAccessStart, methods, methodAccessStart);
if (!methodAccess) continue;
const invocation = prismaMethodEquivalentInvocationMatch(content, scanStructure, methodAccess.end);
if (invocation) matches.push({ index: modelAccess.index, callStart: invocation.callStart });
}
const aliases = findStaticPrismaDelegateAliases(content, scanStructure, models);
const builtinInvokerAliases = findStaticBuiltinInvokerAliases(content, scanStructure);
const delegateWrappers = prismaDelegateWrapperSources(content, scanStructure, models, aliases);
const initialMethodWrappers = prismaModelMethodWrapperSources(content, scanStructure, models, methods, aliases);
const initialMethodAliases = prismaModelMethodWrapperAliases(content, scanStructure, initialMethodWrappers)
.concat(findStaticPrismaDestructuredModelMethodAliases(content, scanStructure, models, methods, aliases))
.concat(findStaticPrismaAssignedDestructuredModelMethodAliases(content, scanStructure, models, methods, aliases))
.concat(findStaticPrismaModelMethodPlainAliases(content, scanStructure, models, methods, aliases, initialMethodWrappers));
const boundMethodAliases = findStaticPrismaModelMethodBoundAliases(
content,
scanStructure,
models,
methods,
aliases,
initialMethodWrappers,
initialMethodAliases
);
const methodAliases = propagateStaticIdentifierAliases(
scanStructure,
initialMethodAliases.concat(boundMethodAliases),
(_sourceAlias, initializer) => ({ name: initializer.name, index: initializer.index, end: initializer.end })
);
const methodWrappers = prismaModelMethodWrapperSources(content, scanStructure, models, methods, aliases, methodAliases);
methodAliases.push(...prismaModelMethodWrapperAliases(content, scanStructure, methodWrappers));
matches.push(
...prismaFunctionPrototypeMethodCallMatchesInStructure(
content,
scanStructure,
models,
methods,
aliases,
builtinInvokerAliases,
methodWrappers,
methodAliases
)
);
for (const delegateWrapperUsage of staticWrapperSourceUsageMatches(content, scanStructure, delegateWrappers)) {
const methodAccessStart = skipWhitespaceForward(scanStructure, delegateWrapperUsage.end);
const methodAccess = staticPrismaPropertyAccessMatchAt(content, scanStructure, methodAccessStart, methods, methodAccessStart);
if (!methodAccess) continue;
const invocation = prismaMethodEquivalentInvocationMatch(content, scanStructure, methodAccess.end);
if (invocation) matches.push({ index: delegateWrapperUsage.index, callStart: invocation.callStart });
}
for (const methodWrapperUsage of staticWrapperSourceUsageMatches(content, scanStructure, methodWrappers)) {
const invocation = prismaMethodEquivalentInvocationMatch(content, scanStructure, methodWrapperUsage.end);
if (invocation) matches.push({ index: methodWrapperUsage.index, callStart: invocation.callStart });
}
const methodAliasNames = [...new Set(methodAliases.map((alias) => alias.name))];
const methodAliasPattern =
methodAliasNames.length > 0 ? new RegExp(String.raw`\b(${methodAliasNames.map(escapeRegExp).join("|")})\b`, "g") : null;
if (methodAliasPattern) {
for (const match of scanStructure.matchAll(methodAliasPattern)) {
if (typeof match.index !== "number") continue;
const aliasName = match[1];
if (!latestStaticAliasRecordAt(scanStructure, methodAliases, aliasName, match.index)) continue;
if (isMemberIdentifierReference(scanStructure, match.index)) continue;
const invocation = prismaMethodEquivalentInvocationMatch(content, scanStructure, match.index + aliasName.length);
if (invocation) matches.push({ index: match.index, callStart: invocation.callStart });
}
}
const aliasNames = [...new Set(aliases.map((alias) => alias.name))];
const aliasPattern = aliasNames.length > 0 ? new RegExp(String.raw`\b(${aliasNames.map(escapeRegExp).join("|")})\b`, "g") : null;
if (aliasPattern) {
for (const match of scanStructure.matchAll(aliasPattern)) {
if (typeof match.index !== "number") continue;
const aliasName = match[1];
if (!latestStaticAliasRecordAt(scanStructure, aliases, aliasName, match.index)) continue;
if (isMemberIdentifierReference(scanStructure, match.index)) continue;
const methodAccessStart = skipWhitespaceForward(scanStructure, match.index + aliasName.length);
const methodAccess = staticPrismaPropertyAccessMatchAt(content, scanStructure, methodAccessStart, methods, methodAccessStart);
if (!methodAccess) continue;
const invocation = prismaMethodEquivalentInvocationMatch(content, scanStructure, methodAccess.end);
if (invocation) matches.push({ index: match.index, callStart: invocation.callStart });
}
}
return matches.toSorted((left, right) => left.index - right.index);
}
function prismaModelMethodCallMatches(content, models, methods) {
const structure = stripStringLiteralsExceptPropertyKeys(maskCommentsOutsideStringLiterals(content));
return prismaModelMethodCallMatchesInStructure(content, structure, models, methods);
}
function sqlTableListContainingPattern(targetTableReference) {
const tableReference = sqlAnyTableReferencePattern();
const tableListItem = String.raw`(?:ONLY\s+)?${tableReference}(?:\s*\*)?`;
const targetTableListItem = String.raw`(?:ONLY\s+)?${targetTableReference}\b(?:\s*\*)?`;
// 表列表允许目标表出现在任意位置,避免只匹配列表首尾造成绕过。
return String.raw`(?:${tableListItem}\s*,\s*)*${targetTableListItem}(?:\s*,\s*${tableListItem})*`;
}
function sqlTruncateTableListPattern(targetTableReference) {
return String.raw`\bTRUNCATE(?:\s+TABLE)?\s+${sqlTableListContainingPattern(targetTableReference)}`;
}
function sqlDropTableListPattern(targetTableReference) {
return String.raw`\bDROP\s+TABLE\s+(?:IF\s+EXISTS\s+)?${sqlTableListContainingPattern(targetTableReference)}`;
}
function stripStringLiterals(content) {
return content
.replace(/"(?:\\.|[^"\\])*"/gs, '""')
.replace(/'(?:\\.|[^'\\])*'/gs, "''")
.replace(/`(?:\\.|[^`\\])*`/gs, "``");
}
function skipWhitespaceForward(content, index) {
let cursor = index;
while (cursor < content.length && /\s/.test(content[cursor])) cursor += 1;
return cursor;
}
function skipWhitespaceBackward(content, index) {
let cursor = index;
while (cursor >= 0 && /\s/.test(content[cursor])) cursor -= 1;
return cursor;
}
function isStaticQuotedPropertyKeyOrAccess(content, literalStart, literalEnd) {
const staticComputedSpan = staticComputedStringPropertyAccessSpan(content, literalStart);
if (staticComputedSpan) return true;
const afterLiteral = skipWhitespaceForward(content, literalEnd);
if (content[afterLiteral] === ":") return true;
const afterComputedKey = skipWhitespaceForward(content, afterLiteral + 1);
if (content[afterLiteral] !== "]") return false;
const beforeLiteral = skipWhitespaceBackward(content, literalStart - 1);
if (content[beforeLiteral] !== "[") return false;
// 保留静态 bracket key覆盖对象 key 与 Prisma property access 两种源码形态。
return (
content[afterComputedKey] === ":" ||
content[afterComputedKey] === "." ||
content[afterComputedKey] === "?" ||
content[afterComputedKey] === "[" ||
content[afterComputedKey] === "(" ||
content[afterComputedKey] === ";" ||
content[afterComputedKey] === "," ||
content[afterComputedKey] === ")" ||
content[afterComputedKey] === "}" ||
typeof content[afterComputedKey] === "undefined"
);
}
function staticComputedStringPropertyAccessSpan(content, literalStart) {
const openBracketIndex = content.lastIndexOf("[", literalStart);
if (openBracketIndex === -1) return null;
if (content.lastIndexOf("]", literalStart) > openBracketIndex) return null;
const structure = maskStringLiterals(maskCommentsOutsideStringLiterals(content));
const closeBracketIndex = findMatchingBracket(structure, openBracketIndex);
if (closeBracketIndex === -1 || literalStart > closeBracketIndex) return null;
const afterComputedKey = skipWhitespaceForward(content, closeBracketIndex + 1);
if (
!(
content[afterComputedKey] === ":" ||
content[afterComputedKey] === "." ||
content[afterComputedKey] === "?" ||
content[afterComputedKey] === "[" ||
content[afterComputedKey] === "(" ||
content[afterComputedKey] === ";" ||
content[afterComputedKey] === "," ||
content[afterComputedKey] === ")" ||
content[afterComputedKey] === "}" ||
typeof content[afterComputedKey] === "undefined"
)
) {
return null;
}
const expressionStart = openBracketIndex + 1;
const expressionEnd = closeBracketIndex;
const values = staticJavaScriptExpressionValues(
content,
structure,
content.slice(expressionStart, expressionEnd),
expressionStart,
openBracketIndex,
new Set(),
0
);
return values.length === 1 ? { start: openBracketIndex, end: closeBracketIndex + 1 } : null;
}
function stripStringLiteralsExceptPropertyKeys(content) {
let output = "";
for (let index = 0; index < content.length; index += 1) {
const quote = content[index];
if (quote === "/" && isRegexLiteralStart(content, index)) {
const literalEnd = findRegexLiteralEnd(content, index);
if (literalEnd !== -1) {
output += "/".padEnd(literalEnd - index, " ") + "/";
index = literalEnd;
continue;
}
}
if (quote !== '"' && quote !== "'" && quote !== "`") {
output += quote;
continue;
}
const literalStart = index;
let literalEnd = literalStart + 1;
while (literalEnd < content.length) {
const char = content[literalEnd];
if (char === "\\") {
literalEnd += 2;
continue;
}
literalEnd += 1;
if (char === quote) break;
}
const literal = content.slice(literalStart, literalEnd);
// 只保留静态 key/access普通字符串值仍等长剥离避免破坏源码索引。
output += isStaticQuotedPropertyKeyOrAccess(content, literalStart, literalEnd)
? literal
: `${quote}${" ".repeat(Math.max(0, literal.length - 2))}${quote}`;
index = literalEnd - 1;
}
return output;
}
function maskCommentsOutsideStringLiterals(content) {
let output = "";
let index = 0;
while (index < content.length) {
const char = content[index];
const next = content[index + 1];
if (char === "/" && next === "/") {
output += " ";
index += 2;
while (index < content.length && content[index] !== "\n" && content[index] !== "\r") {
output += " ";
index += 1;
}
continue;
}
if (char === "/" && next === "*") {
output += " ";
index += 2;
while (index < content.length) {
const blockChar = content[index];
const blockNext = content[index + 1];
if (blockChar === "*" && blockNext === "/") {
output += " ";
index += 2;
break;
}
output += blockChar === "\n" || blockChar === "\r" ? blockChar : " ";
index += 1;
}
continue;
}
if (char === "/" && isRegexLiteralStart(content, index)) {
const literalEnd = findRegexLiteralEnd(content, index);
if (literalEnd !== -1) {
output += content.slice(index, literalEnd + 1);
index = literalEnd + 1;
continue;
}
}
if (char !== '"' && char !== "'" && char !== "`") {
output += char;
index += 1;
continue;
}
output += char;
index += 1;
while (index < content.length) {
const stringChar = content[index];
output += stringChar;
index += 1;
if (stringChar === "\\") {
if (index < content.length) {
output += content[index];
index += 1;
}
continue;
}
if (stringChar === char) break;
}
}
return output;
}
function isRegexLiteralStart(content, slashIndex) {
let cursor = skipWhitespaceBackward(content, slashIndex - 1);
if (cursor < 0) return true;
const previous = content[cursor];
if ("([{:;,=!?&|+-*%^~<>".includes(previous)) return true;
return /\b(?:return|throw|case|delete|typeof|void|new|in|of|yield|await)\b/.test(content.slice(Math.max(0, cursor - 12), cursor + 1));
}
function findRegexLiteralEnd(content, slashIndex) {
let inCharClass = false;
for (let cursor = slashIndex + 1; cursor < content.length; cursor += 1) {
const char = content[cursor];
if (char === "\\") {
cursor += 1;
continue;
}
if (char === "[") inCharClass = true;
if (char === "]") inCharClass = false;
if (char === "/" && !inCharClass) {
let end = cursor + 1;
while (end < content.length && /[a-z]/i.test(content[end])) end += 1;
return end - 1;
}
}
return -1;
}
function findMatchingParen(content, openParenIndex) {
let depth = 0;
for (let index = openParenIndex; index < content.length; index += 1) {
const char = content[index];
if (char === "(") depth += 1;
if (char === ")") {
depth -= 1;
if (depth === 0) return index;
}
}
return -1;
}
function findMatchingBrace(content, openBraceIndex) {
let depth = 0;
for (let index = openBraceIndex; index < content.length; index += 1) {
const char = content[index];
if (char === "{") depth += 1;
if (char === "}") {
depth -= 1;
if (depth === 0) return index;
}
}
return -1;
}
function findMatchingBracket(content, openBracketIndex) {
let depth = 0;
for (let index = openBracketIndex; index < content.length; index += 1) {
const char = content[index];
if (char === "[") depth += 1;
if (char === "]") {
depth -= 1;
if (depth === 0) return index;
}
}
return -1;
}
function stripParenthesesAroundStaticMemberChains(content, structure) {
let output = structure;
let changed = false;
do {
changed = false;
for (let index = 0; index < output.length; index += 1) {
if (output[index] !== "(") continue;
if (!isParenthesizedExpressionOpen(output, index)) continue;
const closeParenIndex = findMatchingParen(output, index);
if (closeParenIndex === -1) continue;
const innerStart = skipWhitespaceForward(output, index + 1);
const innerEnd = skipWhitespaceBackward(output, closeParenIndex - 1) + 1;
if (innerStart >= innerEnd) continue;
const memberMatch = simpleStaticMemberChainMatchAt(content, output, innerStart);
if (!memberMatch || innerStart + memberMatch[0].length !== innerEnd) continue;
// Prisma/raw callee 允许完整 member chain 外包多层括号;剥离括号后仍保留源码索引长度。
output = `${output.slice(0, index)} ${output.slice(index + 1, closeParenIndex)} ${output.slice(closeParenIndex + 1)}`;
changed = true;
break;
}
} while (changed);
return output;
}
function maskStringLiterals(content) {
let output = "";
let index = 0;
while (index < content.length) {
const quote = content[index];
if (quote === "/" && isRegexLiteralStart(content, index)) {
const literalEnd = findRegexLiteralEnd(content, index);
if (literalEnd !== -1) {
output += "/".padEnd(literalEnd - index, " ") + "/";
index = literalEnd + 1;
continue;
}
}
if (quote !== '"' && quote !== "'" && quote !== "`") {
output += quote;
index += 1;
continue;
}
output += quote;
index += 1;
while (index < content.length) {
const char = content[index];
if (char === "\\") {
output += " ";
index += 1;
if (index < content.length) {
output += " ";
index += 1;
}
continue;
}
if (char === quote) {
output += quote;
index += 1;
break;
}
output += " ";
index += 1;
}
}
return output;
}
function findStringLiteralEnd(content, literalStart) {
const quote = content[literalStart];
let cursor = literalStart + 1;
while (cursor < content.length) {
const char = content[cursor];
if (char === "\\") {
cursor += 2;
continue;
}
if (char === quote) return cursor;
cursor += 1;
}
return -1;
}
function hasPrismaCallContaining(content, callPattern, termPattern) {
// 结构匹配只看源码骨架;注释中的括号/花括号不能参与调用体边界计算。
const scanContent = stripStringLiteralsExceptPropertyKeys(maskCommentsOutsideStringLiterals(content));
for (const match of scanContent.matchAll(callPattern)) {
const openParenIndex = scanContent.indexOf("(", match.index);
if (openParenIndex === -1) continue;
const closeParenIndex = findMatchingParen(scanContent, openParenIndex);
if (closeParenIndex === -1) continue;
const callBody = scanContent.slice(openParenIndex, closeParenIndex + 1);
if (termPattern.test(callBody)) return true;
}
return false;
}
function hasPrismaCallBodyContaining(scanContent, callStart, termPattern) {
const openParenIndex = scanContent[callStart] === "(" ? callStart : scanContent.indexOf("(", callStart);
if (openParenIndex === -1) return false;
const closeParenIndex = findMatchingParen(scanContent, openParenIndex);
if (closeParenIndex === -1) return false;
const callBody = scanContent.slice(openParenIndex, closeParenIndex + 1);
return termPattern.test(callBody);
}
function hasPrismaModelMethodCallContaining(content, models, methods, termPattern) {
// delegate alias 与直接 model access 共用同一调用体解析,避免 Job/Audit/guarded 边界分叉。
const scanContent = stripStringLiteralsExceptPropertyKeys(maskCommentsOutsideStringLiterals(content));
for (const match of prismaModelMethodCallMatchesInStructure(content, scanContent, models, methods)) {
if (hasPrismaCallBodyContaining(scanContent, match.callStart, termPattern)) return true;
}
return false;
}
function hasVariableizedOrSpreadJobData(callBody) {
return (
/\bdata\s*(?:[,}])/.test(callBody) ||
/\bdata\s*:\s*[A-Za-z_$][\w$]*\b/.test(callBody) ||
/\bdata\s*:\s*(?:\{|\[)[\s\S]*\.\.\./.test(callBody)
);
}
function hasComputedJobDataKey(callBody) {
const dataObjectPattern = /\bdata\s*:\s*\{/g;
for (const match of callBody.matchAll(dataObjectPattern)) {
const openBraceIndex = callBody.indexOf("{", match.index);
if (openBraceIndex === -1) continue;
const closeBraceIndex = findMatchingBrace(callBody, openBraceIndex);
if (closeBraceIndex === -1) continue;
const dataObjectBody = callBody.slice(openBraceIndex, closeBraceIndex + 1);
// Job 运行态字段必须由 JobExecutionStore 单写computed key 无法静态证明安全,按违规处理。
if (/\[[^\]]+\]\s*:/.test(dataObjectBody)) return true;
}
return false;
}
function hasForbiddenJobWriteCallBody(callBody) {
const runtimeFieldPattern = new RegExp(`\\b(?:${jobRuntimeFields.map(escapeRegExp).join("|")})\\b`);
return runtimeFieldPattern.test(callBody) || hasVariableizedOrSpreadJobData(callBody) || hasComputedJobDataKey(callBody);
}
function hasRawJobRuntimeFieldWrite(sqlSource) {
const sql = normalizeRawSqlScanSource(sqlSource);
const jobTable = sqlTableReferencePattern("Job");
const identifier = sqlBareIdentifierPattern();
const runtimeField = String.raw`(?:${jobRuntimeFields.map(escapeRegExp).join("|")})`;
const maybeQualifiedRuntimeField = String.raw`(?:${identifier}\s*\.\s*)?${runtimeField}`;
// PostgreSQL UPDATE 允许目标表后带 `*` 继承标记;扫描时必须先吃掉它再匹配 alias/SET。
const updateJobRuntimeFieldPattern = new RegExp(
String.raw`\bUPDATE\s+(?:ONLY\s+)?${jobTable}(?:\s*\*)?(?:\s+(?:AS\s+)?${identifier})?\s+SET\b[\s\S]*?\b${maybeQualifiedRuntimeField}\s*=`,
"i"
);
const updateJobTablePattern = new RegExp(
String.raw`\bUPDATE\s+(?:ONLY\s+)?${jobTable}(?:\s*\*)?(?:\s+(?:AS\s+)?${identifier})?\s+SET\b`,
"i"
);
const mergeJobTablePattern = new RegExp(
String.raw`\bMERGE\s+INTO\s+(?:ONLY\s+)?${jobTable}(?:\s+(?:AS\s+)?${identifier})?\b`,
"i"
);
return (
updateJobRuntimeFieldPattern.test(sql) ||
updateJobTablePattern.test(sql) ||
mergeJobTablePattern.test(sql) ||
new RegExp(String.raw`\bINSERT\s+INTO\s+${jobTable}\b`, "i").test(sql) ||
new RegExp(String.raw`\bDELETE\s+FROM\s+(?:ONLY\s+)?${jobTable}\b`, "i").test(sql) ||
new RegExp(sqlTruncateTableListPattern(jobTable), "i").test(sql)
);
}
function findStatementEnd(structure, startIndex) {
const semicolonIndex = structure.indexOf(";", startIndex);
return semicolonIndex === -1 ? structure.length : semicolonIndex;
}
function findVariableInitializerSources(content, structure, variableName, beforeIndex) {
const sources = [];
const escapedName = escapeRegExp(variableName);
const declarationPattern = new RegExp(
String.raw`\b(?:const|let|var)\s+${escapedName}\b(?:\s*:[^=;]+)?\s*=|\b${escapedName}\s*=`,
"g"
);
for (const match of structure.matchAll(declarationPattern)) {
if (typeof match.index !== "number" || match.index >= beforeIndex) continue;
const equalsIndex = structure.indexOf("=", match.index);
if (equalsIndex === -1 || equalsIndex >= beforeIndex) continue;
const initializerStart = skipWhitespaceForward(structure, equalsIndex + 1);
const initializerEnd = findStatementEnd(structure, initializerStart);
sources.push({ index: initializerStart, end: initializerEnd, source: content.slice(initializerStart, initializerEnd) });
}
return sources;
}
function findLatestVariableInitializerSource(content, structure, variableName, beforeIndex) {
const sources = findVariableInitializerSources(content, structure, variableName, beforeIndex);
return sources.length === 0 ? null : sources.at(-1);
}
function isStaticStringLiteralSource(source) {
const trimmed = source.trim();
if (trimmed.length < 2) return false;
const quote = trimmed[0];
if (quote !== '"' && quote !== "'" && quote !== "`") return false;
const literalEnd = findStringLiteralEnd(trimmed, 0);
if (literalEnd !== trimmed.length - 1) return false;
// 带表达式的 template literal 不是静态 SQL 片段,不能当成确定表名/字段名展开。
return quote !== "`" || !/\$\{/.test(trimmed);
}
function isStaticPrismaRawExpressionSource(source) {
const trimmed = source.trim();
const rawCallPattern = /\bPrisma\s*\.\s*raw\s*\(/;
const rawCallMatch = rawCallPattern.exec(trimmed);
if (!rawCallMatch || rawCallMatch.index !== 0) return false;
const openParenIndex = trimmed.indexOf("(", rawCallMatch.index + rawCallMatch[0].length - 1);
if (openParenIndex === -1) return false;
const closeParenIndex = findMatchingParen(maskCommentsOutsideStringLiterals(trimmed), openParenIndex);
if (closeParenIndex !== trimmed.length - 1) return false;
return isStaticStringLiteralSource(trimmed.slice(openParenIndex + 1, closeParenIndex));
}
function isStaticSqlFragmentSource(source) {
return isStaticStringLiteralSource(source) || isStaticPrismaRawExpressionSource(source);
}
function applyTextReplacements(source, replacements) {
let output = source;
for (const replacement of [...replacements].sort((left, right) => right.start - left.start)) {
output = `${output.slice(0, replacement.start)}${replacement.value}${output.slice(replacement.end)}`;
}
return output;
}
function staticSqlFragmentReplacementSource(content, structure, variableName, beforeIndex, seenVariables, depth) {
if (depth >= rawSqlVariableResolutionMaxDepth || seenVariables.has(variableName)) return null;
const initializer = findLatestVariableInitializerSource(content, structure, variableName, beforeIndex);
if (!initializer) return null;
const nestedSeenVariables = new Set(seenVariables);
nestedSeenVariables.add(variableName);
const expandedSource = expandStaticSqlSource(
content,
structure,
initializer.source,
initializer.index,
initializer.end,
initializer.index,
nestedSeenVariables,
depth + 1
);
return isStaticSqlFragmentSource(expandedSource) ? expandedSource.trim() : null;
}
function expandIdentifiersOutsideStringLiterals(content, structure, source, sourceStart, sourceEnd, seenVariables, depth) {
const sourceStructure = structure.slice(sourceStart, sourceEnd);
const replacements = [];
const identifierPattern = /\b[A-Za-z_$][\w$]*\b/g;
for (const match of sourceStructure.matchAll(identifierPattern)) {
if (typeof match.index !== "number") continue;
const variableName = match[0];
const replacement = staticSqlFragmentReplacementSource(
content,
structure,
variableName,
sourceStart + match.index,
seenVariables,
depth
);
if (!replacement) continue;
replacements.push({
start: match.index,
end: match.index + variableName.length,
value: replacement
});
}
return applyTextReplacements(source, replacements);
}
function expandPrismaRawIdentifierArguments(content, structure, source, sourceStart, seenVariables, depth) {
return source.replace(/\bPrisma\s*\.\s*raw\s*\(\s*([A-Za-z_$][\w$]*)\s*\)/g, (match, variableName, offset) => {
const variableOffset = offset + match.lastIndexOf(variableName);
const replacement = staticSqlFragmentReplacementSource(
content,
structure,
variableName,
sourceStart + variableOffset,
seenVariables,
depth
);
return replacement ? match.replace(variableName, replacement) : match;
});
}
function expandStaticPrismaRawTemplateIdentifiers(content, structure, source, sourceStart, seenVariables, depth) {
return source.replace(/\$\{\s*([A-Za-z_$][\w$]*)\s*\}/g, (match, variableName, offset) => {
const variableOffset = offset + match.lastIndexOf(variableName);
const replacement = staticSqlFragmentReplacementSource(
content,
structure,
variableName,
sourceStart + variableOffset,
seenVariables,
depth
);
// 只有已证明是 Prisma.raw(...) 的片段才当作 SQL 文本;普通 template 变量仍是参数。
return replacement && isStaticPrismaRawExpressionSource(replacement) ? `\${${replacement}}` : match;
});
}
function expandStaticSqlSource(content, structure, source, sourceStart, sourceEnd, beforeIndex, seenVariables = new Set(), depth = 0) {
if (depth >= rawSqlVariableResolutionMaxDepth) return source;
// 先展开字符串拼接里的静态中间变量,再补齐 Prisma.sql 模板中 Prisma.raw(...) 的静态参数。
const withConcatenatedIdentifiers = expandIdentifiersOutsideStringLiterals(
content,
structure,
source,
sourceStart,
sourceEnd,
seenVariables,
depth
);
const withRawArguments = expandPrismaRawIdentifierArguments(
content,
structure,
withConcatenatedIdentifiers,
sourceStart,
seenVariables,
depth
);
return expandStaticPrismaRawTemplateIdentifiers(content, structure, withRawArguments, sourceStart, seenVariables, depth);
}
function markDynamicTemplateSqlExpressions(source) {
return source.replace(/\$\{[\s\S]*?\}/g, (match) => {
const expressionSource = match.slice(2, -1).trim();
// Prisma.raw 静态字面量已可证明为 identifier其他模板表达式在 SQL 结构位按动态片段处理。
return isStaticPrismaRawExpressionSource(expressionSource) ? match : ` ${dynamicSqlIdentifierMarker} `;
});
}
function markDynamicCallExpressionsOutsideStringLiterals(source) {
const structure = maskStringLiterals(maskCommentsOutsideStringLiterals(source));
const replacements = [];
const callPattern = /\b[A-Za-z_$][\w$]*(?:\s*\.\s*[A-Za-z_$][\w$]*)*\s*\(/g;
const skippedCalls = new Set(["Prisma.raw", "Prisma.sql"]);
for (const match of structure.matchAll(callPattern)) {
if (typeof match.index !== "number") continue;
const openParenIndex = structure.indexOf("(", match.index + match[0].length - 1);
if (openParenIndex === -1) continue;
const closeParenIndex = findMatchingParen(structure, openParenIndex);
if (closeParenIndex === -1) continue;
const callName = match[0]
.slice(0, match[0].lastIndexOf("("))
.replace(/\s+/g, "");
if (skippedCalls.has(callName)) continue;
// 写 SQL 结构位的函数调用无法静态证明目标表/字段,统一当动态 identifier。
replacements.push({
start: match.index,
end: closeParenIndex + 1,
value: dynamicSqlIdentifierMarker
});
}
return applyTextReplacements(source, replacements);
}
function markDynamicIdentifiersOutsideStringLiterals(source) {
const structure = maskStringLiterals(maskCommentsOutsideStringLiterals(source));
const replacements = [];
const skippedIdentifiers = new Set(["Prisma", "sql", "raw", "true", "false", "null", "undefined"]);
const identifierPattern = /\b[A-Za-z_$][\w$]*\b/g;
for (const match of structure.matchAll(identifierPattern)) {
if (typeof match.index !== "number" || skippedIdentifiers.has(match[0])) continue;
replacements.push({
start: match.index,
end: match.index + match[0].length,
value: dynamicSqlIdentifierMarker
});
}
return applyTextReplacements(source, replacements);
}
function markDynamicSqlIdentifierFragments(source) {
return markDynamicIdentifiersOutsideStringLiterals(markDynamicCallExpressionsOutsideStringLiterals(markDynamicTemplateSqlExpressions(source)));
}
function rawSqlScanCandidateSources(content, structure, source, sourceStart, sourceEnd, beforeIndex) {
const expandedSource = expandStaticSqlSource(content, structure, source, sourceStart, sourceEnd, beforeIndex);
const dynamicMarkedSource = markDynamicSqlIdentifierFragments(expandedSource);
return [...new Set([source, expandedSource, dynamicMarkedSource])];
}
function firstCallArgumentSource(content, structure, openParenIndex, closeParenIndex) {
const argumentStart = skipWhitespaceForward(structure, openParenIndex + 1);
if (argumentStart >= closeParenIndex) return null;
let parenDepth = 0;
let braceDepth = 0;
let bracketDepth = 0;
for (let cursor = argumentStart; cursor < closeParenIndex; cursor += 1) {
const char = structure[cursor];
if (char === "(") parenDepth += 1;
if (char === ")") parenDepth -= 1;
if (char === "{") braceDepth += 1;
if (char === "}") braceDepth -= 1;
if (char === "[") bracketDepth += 1;
if (char === "]") bracketDepth -= 1;
if (char === "," && parenDepth === 0 && braceDepth === 0 && bracketDepth === 0) {
return {
index: argumentStart,
end: cursor,
source: content.slice(argumentStart, cursor)
};
}
}
return {
index: argumentStart,
end: closeParenIndex,
source: content.slice(argumentStart, closeParenIndex)
};
}
function hasDynamicRawSqlWrite(sqlSource) {
const sql = normalizeRawSqlScanSource(sqlSource);
const dynamicIdentifier = escapeRegExp(dynamicSqlIdentifierMarker);
const identifier = sqlBareIdentifierPattern();
const triggerIdentifier = sqlTriggerIdentifierPattern();
const dynamicTableReference = String.raw`(?:${dynamicIdentifier}|${identifier}\s*\.\s*${dynamicIdentifier}|${dynamicIdentifier}\s*\.\s*${identifier})`;
const dynamicFieldReference = String.raw`(?:${dynamicIdentifier}|${identifier}\s*\.\s*${dynamicIdentifier}|${dynamicIdentifier}\s*\.\s*${identifier})`;
const dynamicFunctionReference = String.raw`(?:${dynamicIdentifier}|${identifier}\s*\.\s*${dynamicIdentifier}|${dynamicIdentifier}\s*\.\s*${identifier})(?:\s*\([^)]*\))?`;
const dynamicFunctionList = sqlFunctionListContainingPattern(dynamicFunctionReference);
const anyTableReference = sqlAnyTableReferencePattern();
// raw 写 SQL 里的动态表/字段/trigger identifier 无法静态证明边界,必须 fail-closed。
return (
new RegExp(String.raw`\bUPDATE\s+(?:ONLY\s+)?${dynamicTableReference}(?:\s*\*)?(?:\s+(?:AS\s+)?${identifier})?\s+SET\b`, "i").test(
sql
) ||
new RegExp(String.raw`\bUPDATE\b[\s\S]*?\bSET\b[\s\S]*?\b${dynamicFieldReference}\s*=`, "i").test(sql) ||
new RegExp(String.raw`\bINSERT\s+INTO\s+${dynamicTableReference}\b`, "i").test(sql) ||
new RegExp(String.raw`\bINSERT\s+INTO\b[\s\S]*?\([^)]*\b${dynamicFieldReference}\b[^)]*\)\s+VALUES\b`, "i").test(sql) ||
new RegExp(String.raw`\bMERGE\s+INTO\s+(?:ONLY\s+)?${dynamicTableReference}(?:\s+(?:AS\s+)?${identifier})?\b`, "i").test(
sql
) ||
new RegExp(String.raw`\bDELETE\s+FROM\s+(?:ONLY\s+)?${dynamicTableReference}\b`, "i").test(sql) ||
new RegExp(sqlTruncateTableListPattern(dynamicTableReference), "i").test(sql) ||
new RegExp(sqlDropTableListPattern(dynamicTableReference), "i").test(sql) ||
new RegExp(String.raw`\bDROP\s+TRIGGER\s+(?:IF\s+EXISTS\s+)?${dynamicFieldReference}\s+ON\s+${anyTableReference}\b`, "i").test(
sql
) ||
new RegExp(
String.raw`\bDROP\s+TRIGGER\s+(?:IF\s+EXISTS\s+)?${triggerIdentifier}\s+ON\s+(?:ONLY\s+)?${dynamicTableReference}\b`,
"i"
).test(sql) ||
new RegExp(String.raw`\bALTER\s+TABLE\s+(?:IF\s+EXISTS\s+)?(?:ONLY\s+)?${dynamicTableReference}\b`, "i").test(sql) ||
new RegExp(String.raw`\bALTER\s+TABLE\b[\s\S]*?\bDISABLE\s+TRIGGER\s+${dynamicFieldReference}\b`, "i").test(sql) ||
// 函数 DDL 可以替换/移除 AuditLog append-only guard动态函数名必须 fail-closed。
new RegExp(String.raw`\bDROP\s+(?:FUNCTION|ROUTINE)\s+(?:IF\s+EXISTS\s+)?${dynamicFunctionList}(?=\s|;|$)`, "i").test(sql) ||
new RegExp(String.raw`\bALTER\s+(?:FUNCTION|ROUTINE)\s+(?:IF\s+EXISTS\s+)?${dynamicFunctionReference}(?=\s|;|$)`, "i").test(sql) ||
new RegExp(String.raw`\bCREATE\s+(?:OR\s+REPLACE\s+)?FUNCTION\s+${dynamicFunctionReference}(?=\s|,|;|$)`, "i").test(sql)
);
}
function hasDynamicRawSqlWriteCandidate(content, structure, source, sourceStart, sourceEnd, beforeIndex) {
return rawSqlScanCandidateSources(content, structure, source, sourceStart, sourceEnd, beforeIndex).some((candidateSource) =>
hasDynamicRawSqlWrite(candidateSource)
);
}
function firstCallIdentifierArgument(structure, openParenIndex, closeParenIndex) {
const argumentStart = skipWhitespaceForward(structure, openParenIndex + 1);
if (argumentStart >= closeParenIndex) return null;
const match = /^[A-Za-z_$][\w$]*/.exec(structure.slice(argumentStart, closeParenIndex));
if (!match) return null;
const identifierEnd = argumentStart + match[0].length;
const nextIndex = skipWhitespaceForward(structure, identifierEnd);
// 只跟踪 `raw(query)` 或 `raw(query, ...)` 这种简单变量,避免误读属性访问或函数调用。
if (structure[nextIndex] !== "," && nextIndex !== closeParenIndex) return null;
return match[0];
}
function nthCallArgumentSource(content, structure, openParenIndex, closeParenIndex, argumentIndex) {
const argumentStart = skipWhitespaceForward(structure, openParenIndex + 1);
if (argumentStart >= closeParenIndex) return null;
let parenDepth = 0;
let braceDepth = 0;
let bracketDepth = 0;
let currentArgumentIndex = 0;
let currentArgumentStart = argumentStart;
for (let cursor = argumentStart; cursor < closeParenIndex; cursor += 1) {
const char = structure[cursor];
if (char === "(") parenDepth += 1;
if (char === ")") parenDepth -= 1;
if (char === "{") braceDepth += 1;
if (char === "}") braceDepth -= 1;
if (char === "[") bracketDepth += 1;
if (char === "]") bracketDepth -= 1;
if (char === "," && parenDepth === 0 && braceDepth === 0 && bracketDepth === 0) {
if (currentArgumentIndex === argumentIndex) {
const end = skipWhitespaceBackward(structure, cursor - 1) + 1;
return { index: currentArgumentStart, end, source: content.slice(currentArgumentStart, end) };
}
currentArgumentIndex += 1;
currentArgumentStart = skipWhitespaceForward(structure, cursor + 1);
}
}
if (currentArgumentIndex !== argumentIndex || currentArgumentStart >= closeParenIndex) return null;
const end = skipWhitespaceBackward(structure, closeParenIndex - 1) + 1;
return { index: currentArgumentStart, end, source: content.slice(currentArgumentStart, end) };
}
const rawSqlExecutorNames = ["$executeRawUnsafe", "$executeRaw", "$queryRawUnsafe", "$queryRaw"];
const rawSqlExecutorPatternSource =
String.raw`${staticPrismaPropertyAccessPattern(rawSqlExecutorNames)}\s*(?:<[^>()` + "`" + String.raw`]*>)?\s*`;
function rawSqlExecutorPattern() {
return new RegExp(rawSqlExecutorPatternSource, "g");
}
function rawSqlStructure(content) {
// raw executor 需要保留 db["$executeRawUnsafe"] 这类静态 bracket keySQL 参数本体仍等长遮蔽。
return stripStringLiteralsExceptPropertyKeys(maskCommentsOutsideStringLiterals(content));
}
function findOpeningParen(content, closeParenIndex) {
let depth = 0;
for (let index = closeParenIndex; index >= 0; index -= 1) {
const char = content[index];
if (char === ")") depth += 1;
if (char === "(") {
depth -= 1;
if (depth === 0) return index;
}
}
return -1;
}
function isParenthesizedExpressionOpen(structure, openParenIndex) {
const previousIndex = skipWhitespaceBackward(structure, openParenIndex - 1);
if (previousIndex < 0) return true;
const previous = structure[previousIndex];
if ("([{=,:;!?&|+-*%^~<>".includes(previous)) return true;
if (!/[\w$]/.test(previous)) return false;
const previousWord = /[A-Za-z_$][\w$]*$/.exec(structure.slice(0, previousIndex + 1))?.[0] ?? "";
return /^(?:return|throw|await|yield|case|delete|typeof|void|new|in|of)$/.test(previousWord);
}
function skipOptionalTypeArguments(structure, index) {
let cursor = skipWhitespaceForward(structure, index);
if (structure.startsWith("?.", cursor)) {
const optionalCursor = skipWhitespaceForward(structure, cursor + 2);
if (structure[optionalCursor] !== "<") return cursor;
const optionalTypeEnd = staticTypeArgumentsEnd(structure, optionalCursor);
if (optionalTypeEnd === -1) return cursor;
const afterOptionalType = skipWhitespaceForward(structure, optionalTypeEnd);
return structure[afterOptionalType] === "(" ? afterOptionalType : cursor;
}
if (structure[cursor] !== "<") return cursor;
const typeEnd = staticTypeArgumentsEnd(structure, cursor);
if (typeEnd === -1) return cursor;
const afterType = skipWhitespaceForward(structure, typeEnd);
return structure[afterType] === "(" ? afterType : cursor;
}
function staticTypeArgumentsEnd(structure, openAngleIndex) {
if (structure[openAngleIndex] !== "<") return -1;
let depth = 1;
let sawTypeToken = false;
for (let cursor = openAngleIndex + 1; cursor < structure.length; cursor += 1) {
const char = structure[cursor];
if (char === '"' || char === "'" || char === "`") {
const literalEnd = findStringLiteralEnd(structure, cursor);
if (literalEnd === -1) return -1;
cursor = literalEnd;
sawTypeToken = true;
continue;
}
if (char === "<") {
depth += 1;
sawTypeToken = true;
continue;
}
if (char === ">") {
depth -= 1;
if (depth === 0) return sawTypeToken ? cursor + 1 : -1;
continue;
}
if (char === "(" || char === ")" || char === "{" || char === "}" || char === ";" || char === "\n" || char === "\r") {
return -1;
}
if (!/\s/.test(char)) sawTypeToken = true;
}
return -1;
}
function parenthesizedRawSqlOperationStart(structure, rawAccessEnd, closeParenIndex) {
let cursor = closeParenIndex;
while (structure[cursor] === ")") {
const openParenIndex = findOpeningParen(structure, cursor);
if (openParenIndex === -1 || !isParenthesizedExpressionOpen(structure, openParenIndex)) return -1;
if (rawAccessEnd > cursor) return -1;
cursor = skipWhitespaceForward(structure, cursor + 1);
}
return cursor;
}
function rawSqlMemberOperation(content, structure, callStart) {
const operationStart = skipWhitespaceForward(structure, callStart);
const genericCallStart = skipOptionalTypeArguments(structure, operationStart);
if (genericCallStart !== operationStart && structure[genericCallStart] === "(") {
// raw executor 的 TS 泛型调用 `raw<T>(...)` / `raw?.<T>(...)` 与普通调用等价SQL 参数仍从调用括号读取。
return { kind: "direct", callStart: genericCallStart };
}
if (structure[operationStart] === "(") {
return { kind: "direct", callStart: operationStart };
}
if (structure[operationStart] === "`") {
return { kind: "template", templateStart: operationStart };
}
if (structure[operationStart] === ")") {
// raw executor 被括号包成 callee 时,真实调用操作在 closing paren 之后。
const parenthesizedOperationStart = parenthesizedRawSqlOperationStart(structure, callStart, operationStart);
if (parenthesizedOperationStart !== -1) {
return rawSqlMemberOperation(content, structure, parenthesizedOperationStart);
}
}
if (structure.startsWith("?.", operationStart)) {
const optionalTargetStart = skipWhitespaceForward(structure, operationStart + 2);
if (structure[optionalTargetStart] === "(") {
return { kind: "direct", callStart: optionalTargetStart };
}
const optionalOperation = staticCallableMemberOperationMatchAt(content, structure, operationStart, ["call", "bind", "apply"]);
if (optionalOperation) return optionalOperation;
}
return staticCallableMemberOperationMatchAt(content, structure, operationStart, ["call", "bind", "apply"]);
}
function rawSqlBindCloseParenIndex(content, structure, rawMatchEnd, statementEnd = structure.length) {
const operation = rawSqlMemberOperation(content, structure, rawMatchEnd);
if (operation?.kind !== "bind") return -1;
const methodOpenParenIndex = operation.openParenIndex ?? structure.indexOf("(", operation.methodNameEnd);
if (methodOpenParenIndex === -1 || methodOpenParenIndex >= statementEnd) return -1;
const methodCloseParenIndex = operation.closeParenIndex ?? findMatchingParen(structure, methodOpenParenIndex);
return methodCloseParenIndex !== -1 && methodCloseParenIndex <= statementEnd ? methodCloseParenIndex : -1;
}
function hasLineTerminator(source) {
return /[\n\r\u2028\u2029]/.test(source);
}
function isRawSqlBoundAliasExpressionContinuation(structure, index) {
if (index >= structure.length) return false;
if (structure.startsWith("?.", index)) return true;
// 换行后这些 token 仍可能继续前一条表达式;此时不能把 `.bind(...)` 当作 ASI 结束。
return "([.`+-/*%,".includes(structure[index]);
}
function rawSqlBoundAliasInitializerEnd(structure, bindCloseParenIndex, statementEnd) {
const nextTokenIndex = skipWhitespaceForward(structure, bindCloseParenIndex + 1);
if (nextTokenIndex === statementEnd) return statementEnd;
const gapAfterBind = structure.slice(bindCloseParenIndex + 1, nextTokenIndex);
if (!hasLineTerminator(gapAfterBind)) return -1;
if (isRawSqlBoundAliasExpressionContinuation(structure, nextTokenIndex)) return -1;
// 无分号合法代码依靠 ASI 在换行处结束 initializeralias.end 只覆盖 `.bind(...)` 表达式本身。
return bindCloseParenIndex + 1;
}
function isSimpleRawSqlBoundAliasInitializerPrefix(structure, initializerStart, rawMatchStart) {
const prefix = structure.slice(initializerStart, rawMatchStart).trim();
if (prefix.length === 0) return false;
// 防止无分号场景把后续语句里的 raw executor 误归到当前声明 initializer。
return !/[;\n\r\u2028\u2029]/.test(prefix) && !/\b(?:const|let|var|await|return|throw|if|for|while|switch|class|function|import|export)\b/.test(prefix);
}
function isSimpleRawSqlExecutorAliasInitializerPrefix(structure, initializerStart, rawMatchStart) {
const prefix = structure.slice(initializerStart, rawMatchStart).trim();
if (prefix.length === 0) return false;
// 普通 raw executor alias 只接受静态 member chain避免把条件表达式或函数返回值误判成 Prisma raw executor。
const staticMemberChain = String.raw`(?:[A-Za-z_$][\w$]*|this)(?:\s*(?:(?:\.|\?\.)\s*[A-Za-z_$][\w$]*|(?:\?\.\s*)?\[\s*(?:"[^"]*"|'[^']*'|` + "`[^`]*`" + String.raw`)\s*\]))*`;
return new RegExp(String.raw`^${staticMemberChain}$`).test(prefix);
}
function rawSqlPlainAliasInitializerEnd(structure, rawMatchEnd, statementEnd) {
const nextTokenIndex = skipWhitespaceForward(structure, rawMatchEnd);
if (nextTokenIndex === statementEnd) return statementEnd;
const gapAfterAccess = structure.slice(rawMatchEnd, nextTokenIndex);
if (!hasLineTerminator(gapAfterAccess)) return -1;
if (isRawSqlBoundAliasExpressionContinuation(structure, nextTokenIndex)) return -1;
// 无分号合法代码依靠 ASI 在换行处结束 initializeralias.end 只覆盖 raw executor access 本身。
return rawMatchEnd;
}
function simpleRawSqlAliasInitializerEnd(structure, expressionEnd, statementEnd) {
const nextTokenIndex = skipWhitespaceForward(structure, expressionEnd);
if (nextTokenIndex === statementEnd) return statementEnd;
const gapAfterExpression = structure.slice(expressionEnd, nextTokenIndex);
if (!hasLineTerminator(gapAfterExpression)) return -1;
if (isRawSqlBoundAliasExpressionContinuation(structure, nextTokenIndex)) return -1;
return expressionEnd;
}
function simpleStaticMemberChainMatchAt(content, structure, index) {
const receiverMatch = /^(?:[A-Za-z_$][\w$]*|this)\b/.exec(structure.slice(index));
if (!receiverMatch) return null;
let cursor = index + receiverMatch[0].length;
while (true) {
const nextCursor = skipWhitespaceForward(structure, cursor);
if (structure[nextCursor] !== "." && structure[nextCursor] !== "[" && !structure.startsWith("?.", nextCursor)) break;
const propertyAccess = staticStaticMemberPropertyAccessMatchAt(content, structure, nextCursor);
if (!propertyAccess) break;
cursor = propertyAccess.end;
}
return [structure.slice(index, cursor)];
}
function staticStaticMemberPropertyAccessMatchAt(content, structure, index) {
let propertyStart = index;
if (structure.startsWith("?.", index)) {
propertyStart = skipWhitespaceForward(structure, index + 2);
} else if (structure[index] === ".") {
propertyStart = skipWhitespaceForward(structure, index + 1);
}
if (structure[propertyStart] === "[") {
const closeBracketIndex = findMatchingBracket(structure, propertyStart);
if (closeBracketIndex === -1) return null;
const expressionStart = propertyStart + 1;
const expressionEnd = closeBracketIndex;
const values = staticJavaScriptExpressionValues(
content,
structure,
content.slice(expressionStart, expressionEnd),
expressionStart,
index,
new Set(),
0
);
return values.length === 1 ? { index, end: closeBracketIndex + 1, value: values[0] } : null;
}
if (propertyStart === index && structure[index] !== "." && !structure.startsWith("?.", index)) return null;
const nameMatch = /^[A-Za-z_$][\w$]*/.exec(structure.slice(propertyStart));
return nameMatch ? { index, end: propertyStart + nameMatch[0].length, value: nameMatch[0] } : null;
}
function rawSqlExecutorAccessMatches(content, structure, startIndex = 0, endIndex = structure.length) {
const matches = [];
for (let cursor = startIndex; cursor < endIndex; cursor += 1) {
if (structure[cursor] !== "." && structure[cursor] !== "[" && !structure.startsWith("?.", cursor)) continue;
const match = staticPrismaPropertyAccessMatchAt(content, structure, cursor, rawSqlExecutorNames, cursor);
if (!match || match.end > endIndex) continue;
matches.push(match);
cursor = match.end - 1;
}
return matches;
}
function completeRawSqlBoundExecutorAliasExpressionMatch(content, structure, sourceStart, sourceEnd) {
const unwrapped = unwrapCompleteParenthesizedJavaScriptExpression(structure, sourceStart, sourceEnd);
const expressionStart = skipWhitespaceForward(structure, unwrapped.start);
const expressionEnd = skipWhitespaceBackward(structure, unwrapped.end - 1) + 1;
if (expressionStart >= expressionEnd) return null;
for (const rawMatch of rawSqlExecutorAccessMatches(content, structure, expressionStart, expressionEnd)) {
if (!isSimpleRawSqlBoundAliasInitializerPrefix(structure, expressionStart, rawMatch.index)) continue;
const bindCloseParenIndex = rawSqlBindCloseParenIndex(content, structure, rawMatch.end, expressionEnd);
if (bindCloseParenIndex === -1) continue;
if (!isAtStaticWrapperExpressionEnd(structure, bindCloseParenIndex + 1, expressionEnd)) continue;
return { kind: "rawExecutor", index: rawMatch.index, end: expressionEnd };
}
return null;
}
function completeRawSqlPlainExecutorAliasExpressionMatch(content, structure, sourceStart, sourceEnd) {
const unwrapped = unwrapCompleteParenthesizedJavaScriptExpression(structure, sourceStart, sourceEnd);
const expressionStart = skipWhitespaceForward(structure, unwrapped.start);
const expressionEnd = skipWhitespaceBackward(structure, unwrapped.end - 1) + 1;
if (expressionStart >= expressionEnd) return null;
for (const rawMatch of rawSqlExecutorAccessMatches(content, structure, expressionStart, expressionEnd)) {
if (!isSimpleRawSqlExecutorAliasInitializerPrefix(structure, expressionStart, rawMatch.index)) continue;
if (!isAtStaticWrapperExpressionEnd(structure, rawMatch.end, expressionEnd)) continue;
return { kind: "rawExecutor", index: rawMatch.index, end: expressionEnd };
}
return null;
}
function hasRawSqlExecutorAccess(content, structure) {
return rawSqlExecutorAccessMatches(content, structure).length > 0;
}
function hasRawSqlExecutorAccessInRange(content, structure, startIndex, endIndex) {
const scanStructure = stripParenthesesAroundStaticMemberChains(content, structure);
return rawSqlExecutorAccessMatches(content, scanStructure, startIndex, endIndex).length > 0;
}
function findRawSqlBoundExecutorAliases(content, structure) {
const aliases = [];
const declarationPattern = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\b(?:\s*:[^=;]+)?\s*=/g;
for (const declarationMatch of structure.matchAll(declarationPattern)) {
if (typeof declarationMatch.index !== "number") continue;
const equalsIndex = structure.indexOf("=", declarationMatch.index);
if (equalsIndex === -1) continue;
const initializerStart = skipWhitespaceForward(structure, equalsIndex + 1);
const statementEnd = findStatementEnd(structure, initializerStart);
const branchRawMatch = staticInitializerBranchMatch(
content,
structure,
initializerStart,
statementEnd,
completeRawSqlBoundExecutorAliasExpressionMatch
);
if (branchRawMatch?.branch) {
// mixed ternary/logical 中任一分支返回 raw executor bind都把 alias 视为 raw executor防止 safe 分支掩盖危险分支。
aliases.push({ name: declarationMatch[1], index: declarationMatch.index, end: statementEnd });
continue;
}
for (const rawMatch of rawSqlExecutorAccessMatches(content, structure, initializerStart, statementEnd)) {
const rawMatchStart = rawMatch.index;
if (!isSimpleRawSqlBoundAliasInitializerPrefix(structure, initializerStart, rawMatchStart)) continue;
const rawMatchEnd = rawMatch.end;
const bindCloseParenIndex = rawSqlBindCloseParenIndex(content, structure, rawMatchEnd, statementEnd);
if (bindCloseParenIndex === -1) continue;
const initializerEnd = rawSqlBoundAliasInitializerEnd(structure, bindCloseParenIndex, statementEnd);
if (initializerEnd === -1) continue;
// 只跟踪同文件的简单 bound executor alias复杂重赋值/跨函数数据流继续交给后续 review 扩展。
aliases.push({
name: declarationMatch[1],
index: declarationMatch.index,
end: initializerEnd
});
break;
}
}
return aliases;
}
function findRawSqlAssignedBoundExecutorAliases(content, structure) {
const aliases = [];
const assignmentPattern = /\b([A-Za-z_$][\w$]*)\b\s*=/g;
for (const assignmentMatch of structure.matchAll(assignmentPattern)) {
if (typeof assignmentMatch.index !== "number") continue;
if (isMemberIdentifierReference(structure, assignmentMatch.index)) continue;
const equalsIndex = structure.indexOf("=", assignmentMatch.index + assignmentMatch[0].lastIndexOf("="));
if (equalsIndex === -1) continue;
if ("=><!".includes(structure[equalsIndex - 1] ?? "") || ["=", ">"].includes(structure[equalsIndex + 1] ?? "")) continue;
const initializerStart = skipWhitespaceForward(structure, equalsIndex + 1);
const statementEnd = findStatementEnd(structure, initializerStart);
const branchRawMatch = staticInitializerBranchMatch(
content,
structure,
initializerStart,
statementEnd,
completeRawSqlBoundExecutorAliasExpressionMatch
);
if (branchRawMatch?.branch) {
// 后续赋值也按同一规则 fail-closed避免 `alias = ok ? raw.bind(db) : safe` 绕过。
aliases.push({ name: assignmentMatch[1], index: assignmentMatch.index, end: statementEnd });
continue;
}
for (const rawMatch of rawSqlExecutorAccessMatches(content, structure, initializerStart, statementEnd)) {
const rawMatchStart = rawMatch.index;
if (!isSimpleRawSqlBoundAliasInitializerPrefix(structure, initializerStart, rawMatchStart)) continue;
const rawMatchEnd = rawMatch.end;
const bindCloseParenIndex = rawSqlBindCloseParenIndex(content, structure, rawMatchEnd, statementEnd);
if (bindCloseParenIndex === -1) continue;
const initializerEnd = rawSqlBoundAliasInitializerEnd(structure, bindCloseParenIndex, statementEnd);
if (initializerEnd === -1) continue;
// 赋值 alias 覆盖 `let execRaw; execRaw = db.$executeRawUnsafe.bind(db)` 这类后续绑定。
aliases.push({ name: assignmentMatch[1], index: assignmentMatch.index, end: initializerEnd });
break;
}
}
return aliases;
}
function findRawSqlPlainExecutorAliases(content, structure) {
const aliases = [];
const declarationPattern = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\b(?:\s*:[^=;]+)?\s*=/g;
for (const declarationMatch of structure.matchAll(declarationPattern)) {
if (typeof declarationMatch.index !== "number") continue;
const equalsIndex = structure.indexOf("=", declarationMatch.index);
if (equalsIndex === -1) continue;
const initializerStart = skipWhitespaceForward(structure, equalsIndex + 1);
const statementEnd = findStatementEnd(structure, initializerStart);
const branchRawMatch = staticInitializerBranchMatch(
content,
structure,
initializerStart,
statementEnd,
completeRawSqlPlainExecutorAliasExpressionMatch
);
if (branchRawMatch?.branch) {
// 普通 raw executor alias 同样不能被 `safe || db.$executeRawUnsafe` 这类逻辑分支隐藏。
aliases.push({ name: declarationMatch[1], index: declarationMatch.index, end: statementEnd });
continue;
}
for (const rawMatch of rawSqlExecutorAccessMatches(content, structure, initializerStart, statementEnd)) {
const rawMatchStart = rawMatch.index;
if (!isSimpleRawSqlExecutorAliasInitializerPrefix(structure, initializerStart, rawMatchStart)) continue;
const rawMatchEnd = rawMatch.end;
const initializerEnd = rawSqlPlainAliasInitializerEnd(structure, rawMatchEnd, statementEnd);
if (initializerEnd === -1) continue;
// 只跟踪同文件的简单 raw executor alias复杂重赋值/跨函数数据流继续交给后续 review 扩展。
aliases.push({
name: declarationMatch[1],
index: declarationMatch.index,
end: initializerEnd
});
break;
}
}
return aliases;
}
function findRawSqlAssignedPlainExecutorAliases(content, structure) {
const aliases = [];
const assignmentPattern = /\b([A-Za-z_$][\w$]*)\b\s*=/g;
for (const assignmentMatch of structure.matchAll(assignmentPattern)) {
if (typeof assignmentMatch.index !== "number") continue;
if (isMemberIdentifierReference(structure, assignmentMatch.index)) continue;
const equalsIndex = structure.indexOf("=", assignmentMatch.index + assignmentMatch[0].lastIndexOf("="));
if (equalsIndex === -1) continue;
if ("=><!".includes(structure[equalsIndex - 1] ?? "") || ["=", ">"].includes(structure[equalsIndex + 1] ?? "")) continue;
const initializerStart = skipWhitespaceForward(structure, equalsIndex + 1);
const statementEnd = findStatementEnd(structure, initializerStart);
const branchRawMatch = staticInitializerBranchMatch(
content,
structure,
initializerStart,
statementEnd,
completeRawSqlPlainExecutorAliasExpressionMatch
);
if (branchRawMatch?.branch) {
// 赋值式 plain raw alias 覆盖 `let runRaw; runRaw = maybe || db.$executeRawUnsafe`。
aliases.push({ name: assignmentMatch[1], index: assignmentMatch.index, end: statementEnd });
continue;
}
for (const rawMatch of rawSqlExecutorAccessMatches(content, structure, initializerStart, statementEnd)) {
const rawMatchStart = rawMatch.index;
if (!isSimpleRawSqlExecutorAliasInitializerPrefix(structure, initializerStart, rawMatchStart)) continue;
const rawMatchEnd = rawMatch.end;
const initializerEnd = rawSqlPlainAliasInitializerEnd(structure, rawMatchEnd, statementEnd);
if (initializerEnd === -1) continue;
// 赋值 alias 覆盖 `let execRaw; execRaw = db.$executeRawUnsafe` 这类后续绑定。
aliases.push({ name: assignmentMatch[1], index: assignmentMatch.index, end: initializerEnd });
break;
}
}
return aliases;
}
function destructuringParts(structure, startIndex, endIndex) {
const parts = [];
let partStart = startIndex;
let braceDepth = 0;
let bracketDepth = 0;
let parenDepth = 0;
for (let cursor = startIndex; cursor < endIndex; cursor += 1) {
const char = structure[cursor];
if (char === "{") braceDepth += 1;
if (char === "}") braceDepth -= 1;
if (char === "[") bracketDepth += 1;
if (char === "]") bracketDepth -= 1;
if (char === "(") parenDepth += 1;
if (char === ")") parenDepth -= 1;
if (char !== "," || braceDepth !== 0 || bracketDepth !== 0 || parenDepth !== 0) continue;
parts.push({ start: partStart, end: cursor });
partStart = cursor + 1;
}
parts.push({ start: partStart, end: endIndex });
return parts;
}
function destructuredAliasNamesForStaticKeys(content, structure, bindingStart, bindingEnd, keys, beforeIndex) {
const names = [];
const allowedKeys = new Set(keys);
for (const part of destructuringParts(structure, bindingStart, bindingEnd)) {
const key = staticDestructuringPropertyKeyValue(content, structure, part.start, part.end, beforeIndex);
if (!allowedKeys.has(key)) continue;
const name = destructuringAliasName(structure, part.start, part.end);
if (name) names.push(name);
}
return names;
}
function staticDestructuringPropertyKeyValue(content, structure, partStart, partEnd, beforeIndex) {
const keyStart = skipWhitespaceForward(structure, partStart);
const keyEnd = destructuringPropertyKeyEnd(structure, keyStart, partEnd);
if (keyEnd === -1) return null;
const expressionStart = structure[keyStart] === "[" ? keyStart + 1 : keyStart;
const expressionEnd = structure[keyStart] === "[" ? keyEnd - 1 : keyEnd;
const keySource = content.slice(expressionStart, expressionEnd);
if (structure[keyStart] !== "[" && /^[A-Za-z_$][\w$]*$/.test(keySource.trim())) return keySource.trim();
return staticJavaScriptExpressionValues(content, structure, keySource, expressionStart, beforeIndex, new Set(), 0)[0] ?? null;
}
function destructuringPropertyKeyEnd(structure, keyStart, partEnd) {
if (keyStart >= partEnd) return -1;
if (structure[keyStart] === "[") {
const closeBracketIndex = findMatchingBracket(structure, keyStart);
if (closeBracketIndex === -1 || closeBracketIndex >= partEnd) return -1;
const afterKey = skipWhitespaceForward(structure, closeBracketIndex + 1);
return structure[afterKey] === ":" ? closeBracketIndex + 1 : -1;
}
const literalEnd = findStringLiteralEnd(structure, keyStart);
if ((structure[keyStart] === '"' || structure[keyStart] === "'" || structure[keyStart] === "`") && literalEnd !== -1 && literalEnd < partEnd) {
const afterKey = skipWhitespaceForward(structure, literalEnd + 1);
return structure[afterKey] === ":" ? literalEnd + 1 : -1;
}
const identifierMatch = /^[A-Za-z_$][\w$]*/.exec(structure.slice(keyStart, partEnd));
if (!identifierMatch) return -1;
const keyEnd = keyStart + identifierMatch[0].length;
const afterKey = skipWhitespaceForward(structure, keyEnd);
if (structure[afterKey] === ":" || afterKey === partEnd) return keyEnd;
return -1;
}
function destructuringAliasName(structure, partStart, partEnd) {
const keyStart = skipWhitespaceForward(structure, partStart);
const keyEnd = destructuringPropertyKeyEnd(structure, keyStart, partEnd);
if (keyEnd === -1) return null;
const afterKey = skipWhitespaceForward(structure, keyEnd);
if (structure[afterKey] !== ":") {
const shorthandName = structure.slice(keyStart, keyEnd).trim();
return /^[A-Za-z_$][\w$]*$/.test(shorthandName) ? shorthandName : null;
}
const aliasStart = skipWhitespaceForward(structure, afterKey + 1);
const aliasMatch = /^[A-Za-z_$][\w$]*/.exec(structure.slice(aliasStart, partEnd));
if (!aliasMatch) return null;
const aliasEnd = aliasStart + aliasMatch[0].length;
const afterAlias = skipWhitespaceForward(structure, aliasEnd);
return afterAlias === partEnd ? aliasMatch[0] : null;
}
function findRawSqlDestructuredExecutorAliases(content, structure) {
const aliases = [];
const declarationPattern = /\b(?:const|let|var)\s*\{/g;
for (const declarationMatch of structure.matchAll(declarationPattern)) {
if (typeof declarationMatch.index !== "number") continue;
const openBraceIndex = structure.indexOf("{", declarationMatch.index);
if (openBraceIndex === -1) continue;
const closeBraceIndex = findMatchingBrace(structure, openBraceIndex);
if (closeBraceIndex === -1) continue;
const names = destructuredAliasNamesForStaticKeys(
content,
structure,
openBraceIndex + 1,
closeBraceIndex,
rawSqlExecutorNames,
declarationMatch.index
);
if (names.length === 0) continue;
let cursor = skipWhitespaceForward(structure, closeBraceIndex + 1);
if (structure[cursor] === ":") {
const equalsAfterType = structure.indexOf("=", cursor + 1);
const semicolonAfterType = structure.indexOf(";", cursor + 1);
if (equalsAfterType === -1 || (semicolonAfterType !== -1 && semicolonAfterType < equalsAfterType)) continue;
cursor = skipWhitespaceForward(structure, equalsAfterType + 1);
} else {
if (structure[cursor] !== "=") continue;
cursor = skipWhitespaceForward(structure, cursor + 1);
}
const receiverMatch = simpleStaticMemberChainMatchAt(content, structure, cursor);
if (!receiverMatch) continue;
const expressionEnd = cursor + receiverMatch[0].length;
const statementEnd = findStatementEnd(structure, cursor);
const aliasEnd = simpleRawSqlAliasInitializerEnd(structure, expressionEnd, statementEnd);
if (aliasEnd === -1) continue;
// 解构 alias 只覆盖简单 receiver避免把复杂运行时对象误判为 Prisma raw executor。
for (const name of names) {
aliases.push({ name, index: declarationMatch.index, end: aliasEnd });
}
}
return aliases;
}
function findRawSqlAssignedDestructuredExecutorAliases(content, structure) {
const aliases = [];
const assignmentPattern = /\(\s*\{/g;
for (const assignmentMatch of structure.matchAll(assignmentPattern)) {
if (typeof assignmentMatch.index !== "number") continue;
const openParenIndex = structure.indexOf("(", assignmentMatch.index);
if (openParenIndex === -1 || !isParenthesizedExpressionOpen(structure, openParenIndex)) continue;
const openBraceIndex = structure.indexOf("{", openParenIndex);
if (openBraceIndex === -1) continue;
const closeBraceIndex = findMatchingBrace(structure, openBraceIndex);
const closeParenIndex = findMatchingParen(structure, openParenIndex);
if (closeBraceIndex === -1 || closeParenIndex === -1 || closeBraceIndex > closeParenIndex) continue;
const names = destructuredAliasNamesForStaticKeys(
content,
structure,
openBraceIndex + 1,
closeBraceIndex,
rawSqlExecutorNames,
assignmentMatch.index
);
if (names.length === 0) continue;
let cursor = skipWhitespaceForward(structure, closeBraceIndex + 1);
if (structure[cursor] !== "=") continue;
cursor = skipWhitespaceForward(structure, cursor + 1);
const receiverMatch = simpleStaticMemberChainMatchAt(content, structure, cursor);
if (!receiverMatch) continue;
const expressionEnd = cursor + receiverMatch[0].length;
if (skipWhitespaceForward(structure, expressionEnd) !== closeParenIndex) continue;
const statementEnd = findStatementEnd(structure, openParenIndex);
const aliasEnd = simpleRawSqlAliasInitializerEnd(structure, closeParenIndex + 1, statementEnd);
if (aliasEnd === -1) continue;
// 声明后的解构赋值 alias 覆盖 `let execRaw; ({ $executeRawUnsafe: execRaw } = db)`。
for (const name of names) {
aliases.push({ name, index: assignmentMatch.index, end: aliasEnd });
}
}
return aliases;
}
function isMemberIdentifierReference(structure, identifierIndex) {
const previousIndex = skipWhitespaceBackward(structure, identifierIndex - 1);
return previousIndex >= 0 && structure[previousIndex] === ".";
}
function firstArrayElementSource(content, structure, sourceStart, sourceEnd) {
return nthArrayElementSource(content, structure, { index: sourceStart, end: sourceEnd }, 0);
}
function nthArrayElementSource(content, structure, arrayArgument, elementIndex) {
if (!arrayArgument) return null;
const sourceStart = arrayArgument.index;
const sourceEnd = arrayArgument.end;
const arrayStart = skipWhitespaceForward(structure, sourceStart);
const arrayEnd = skipWhitespaceBackward(structure, sourceEnd - 1) + 1;
if (arrayStart >= arrayEnd || structure[arrayStart] !== "[") return null;
const closeBracketIndex = findMatchingBracket(structure, arrayStart);
if (closeBracketIndex === -1 || closeBracketIndex !== arrayEnd - 1) return null;
return nthCallArgumentSource(content, structure, arrayStart, closeBracketIndex, elementIndex);
}
function rawSqlInvocationSourcesFromArrayArgument(content, structure, arrayArgument, beforeIndex) {
const sqlArgument = firstArrayElementSource(content, structure, arrayArgument.index, arrayArgument.end);
return sqlArgument ? [{ ...sqlArgument, beforeIndex }] : [];
}
function rawSqlExecutorDirectAliases(content, structure) {
return findRawSqlBoundExecutorAliases(content, structure)
.concat(findRawSqlAssignedBoundExecutorAliases(content, structure))
.concat(findRawSqlPlainExecutorAliases(content, structure))
.concat(findRawSqlAssignedPlainExecutorAliases(content, structure))
.concat(findRawSqlDestructuredExecutorAliases(content, structure))
.concat(findRawSqlAssignedDestructuredExecutorAliases(content, structure));
}
function rawSqlExecutorAliases(content, structure) {
const directAliases = propagateStaticIdentifierAliases(structure, rawSqlExecutorDirectAliases(content, structure), (_sourceAlias, initializer) => ({
name: initializer.name,
index: initializer.index,
end: initializer.end
}));
const wrappers = rawSqlExecutorWrapperSources(content, structure, directAliases);
const aliases = findRawSqlBoundExecutorAliases(content, structure)
.concat(findRawSqlAssignedBoundExecutorAliases(content, structure))
.concat(findRawSqlPlainExecutorAliases(content, structure))
.concat(findRawSqlAssignedPlainExecutorAliases(content, structure))
.concat(findRawSqlDestructuredExecutorAliases(content, structure))
.concat(findRawSqlAssignedDestructuredExecutorAliases(content, structure))
.concat(findStaticWrapperExtractionAliases(content, structure, wrappers));
return propagateStaticIdentifierAliases(structure, aliases, (_sourceAlias, initializer) => ({
name: initializer.name,
index: initializer.index,
end: initializer.end
}));
}
function isRawSqlExecutorAliasTarget(structure, targetArgument, aliases) {
const aliasName = simpleIdentifierSourceName(structure, targetArgument.index, targetArgument.end);
if (!aliasName) return false;
// Reflect.apply 只能使用调用点之前已建立的同文件 raw executor alias。
return Boolean(latestStaticAliasRecordAt(structure, aliases, aliasName, targetArgument.index));
}
function isRawSqlExecutorTargetArgument(content, structure, targetArgument, aliases) {
return (
targetArgument &&
(hasRawSqlExecutorAccessInRange(content, structure, targetArgument.index, targetArgument.end) ||
isRawSqlExecutorAliasTarget(structure, targetArgument, aliases))
);
}
function rawSqlInvocationSourcesFromFunctionPrototypeArrayArgument(content, structure, arrayArgument, methodKind, beforeIndex) {
if (methodKind === "call") {
const sqlArgument = nthArrayElementSource(content, structure, arrayArgument, 1);
return sqlArgument ? [{ ...sqlArgument, beforeIndex }] : [];
}
if (methodKind === "apply") {
const nestedArrayArgument = nthArrayElementSource(content, structure, arrayArgument, 1);
return nestedArrayArgument ? rawSqlInvocationSourcesFromArrayArgument(content, structure, nestedArrayArgument, beforeIndex) : [];
}
return [];
}
function rawSqlFunctionPrototypeInvocationSources(content, structure, builtinInvokerAliases) {
const sources = [];
const aliases = rawSqlExecutorAliases(content, structure);
const boundTargetAliases = findFunctionPrototypeBoundTargetAliases(content, structure);
for (const invocation of functionPrototypeCallInvocationMatches(content, structure, builtinInvokerAliases)) {
const targetArgument = functionPrototypeEquivalentArgumentSource(content, structure, invocation, 0);
if (!isRawSqlExecutorTargetArgument(content, structure, targetArgument, aliases)) continue;
if (invocation.methodKind === "call") {
// Function.prototype.call.call(raw, thisArg, sql, ...) 的 SQL 是外层调用第三参。
const sqlArgument = functionPrototypeEquivalentArgumentSource(content, structure, invocation, 2);
if (sqlArgument) sources.push({ ...sqlArgument, beforeIndex: invocation.index });
continue;
}
// Function.prototype.apply.call(raw, thisArg, [sql, ...]) 的 SQL 在第三参数数组首项。
const arrayArgument = functionPrototypeEquivalentArgumentSource(content, structure, invocation, 2);
if (arrayArgument) sources.push(...rawSqlInvocationSourcesFromArrayArgument(content, structure, arrayArgument, invocation.index));
}
for (const invocation of functionPrototypeBoundTargetInvocationMatches(content, structure, boundTargetAliases)) {
if (!isRawSqlExecutorTargetArgument(content, structure, invocation.targetArgument, aliases)) continue;
if (invocation.methodKind === "call") {
// Function.prototype.call.bind(raw)(thisArg, sql, ...) 的 SQL 是绑定后调用的第二参。
const sqlArgument = nthCallArgumentSource(content, structure, invocation.openParenIndex, invocation.closeParenIndex, 1);
if (sqlArgument) sources.push({ ...sqlArgument, beforeIndex: invocation.index });
continue;
}
// Function.prototype.apply.bind(raw)(thisArg, [sql, ...]) 的 SQL 在绑定后调用第二参数数组首项。
const arrayArgument = nthCallArgumentSource(content, structure, invocation.openParenIndex, invocation.closeParenIndex, 1);
if (arrayArgument) sources.push(...rawSqlInvocationSourcesFromArrayArgument(content, structure, arrayArgument, invocation.index));
}
return sources;
}
function rawSqlReflectApplyInvocationSources(content, structure, builtinInvokerAliases) {
const sources = [];
const aliases = rawSqlExecutorAliases(content, structure);
for (const invocation of reflectApplyInvocationMatches(content, structure, builtinInvokerAliases)) {
const targetArgument = reflectApplyEquivalentArgumentSource(content, structure, invocation, 0);
if (!targetArgument) continue;
const functionPrototypeMethod = functionPrototypeMethodKindFromArgument(content, structure, targetArgument, builtinInvokerAliases);
if (functionPrototypeMethod) {
const invokedFunctionArgument = reflectApplyEquivalentArgumentSource(content, structure, invocation, 1);
if (!isRawSqlExecutorTargetArgument(content, structure, invokedFunctionArgument, aliases)) continue;
const arrayArgument = reflectApplyEquivalentArgumentSource(content, structure, invocation, 2);
if (!arrayArgument) continue;
sources.push(...rawSqlInvocationSourcesFromFunctionPrototypeArrayArgument(content, structure, arrayArgument, functionPrototypeMethod, invocation.index));
continue;
}
if (!isRawSqlExecutorTargetArgument(content, structure, targetArgument, aliases)) continue;
const arrayArgument = reflectApplyEquivalentArgumentSource(content, structure, invocation, 2);
if (!arrayArgument) continue;
sources.push(...rawSqlInvocationSourcesFromArrayArgument(content, structure, arrayArgument, invocation.index));
}
return sources;
}
function rawSqlInvocationSourcesFromOperation(content, structure, operation, beforeIndex) {
const sources = [];
if (!operation) return sources;
if (operation.kind === "direct") {
const closeParenIndex = findMatchingParen(structure, operation.callStart);
if (closeParenIndex === -1) return sources;
const sqlArgument = nthCallArgumentSource(content, structure, operation.callStart, closeParenIndex, 0);
if (sqlArgument) sources.push({ ...sqlArgument, beforeIndex });
return sources;
}
if (operation.kind === "template") {
const templateEnd = findStringLiteralEnd(content, operation.templateStart);
if (templateEnd !== -1) {
sources.push({
source: content.slice(operation.templateStart, templateEnd + 1),
index: operation.templateStart,
end: templateEnd + 1,
beforeIndex
});
}
return sources;
}
if (operation.kind !== "call" && operation.kind !== "bind" && operation.kind !== "apply") return sources;
const methodOpenParenIndex = operation.openParenIndex ?? structure.indexOf("(", operation.methodNameEnd);
if (methodOpenParenIndex === -1) return sources;
const methodCloseParenIndex = operation.closeParenIndex ?? findMatchingParen(structure, methodOpenParenIndex);
if (methodCloseParenIndex === -1) return sources;
if (operation.kind === "call") {
// .call(thisArg, sql, ...) 的 SQL 是第二个参数。
const sqlArgument = nthCallArgumentSource(content, structure, methodOpenParenIndex, methodCloseParenIndex, 1);
if (sqlArgument) sources.push({ ...sqlArgument, beforeIndex });
return sources;
}
if (operation.kind === "apply") {
// .apply(thisArg, [sql, ...]) 的 SQL 在参数数组首项,和 Reflect.apply 共享同一提取语义。
const arrayArgument = nthCallArgumentSource(content, structure, methodOpenParenIndex, methodCloseParenIndex, 1);
return arrayArgument ? rawSqlInvocationSourcesFromArrayArgument(content, structure, arrayArgument, beforeIndex) : sources;
}
let boundCallStart = skipWhitespaceForward(structure, methodCloseParenIndex + 1);
if (structure.startsWith("?.", boundCallStart)) {
boundCallStart = skipWhitespaceForward(structure, boundCallStart + 2);
}
if (structure[boundCallStart] !== "(") return sources;
const boundCloseParenIndex = findMatchingParen(structure, boundCallStart);
if (boundCloseParenIndex === -1) return sources;
const sqlArgument = nthCallArgumentSource(content, structure, boundCallStart, boundCloseParenIndex, 0);
if (sqlArgument) sources.push({ ...sqlArgument, beforeIndex });
return sources;
}
function rawSqlSourceMatcher(aliases = []) {
return (content, structure, sourceStart, sourceEnd) => {
const accessMatches = rawSqlExecutorAccessMatches(content, structure, sourceStart, sourceEnd);
for (const rawMatch of accessMatches) {
if (!isSimpleRawSqlExecutorAliasInitializerPrefix(structure, sourceStart, rawMatch.index)) continue;
const bindCloseParenIndex = rawSqlBindCloseParenIndex(content, structure, rawMatch.end, sourceEnd);
if (bindCloseParenIndex !== -1 && isAtStaticWrapperExpressionEnd(structure, bindCloseParenIndex + 1, sourceEnd)) {
return { kind: "rawExecutor", index: rawMatch.index, end: bindCloseParenIndex + 1 };
}
if (isAtStaticWrapperExpressionEnd(structure, rawMatch.end, sourceEnd)) {
return { kind: "rawExecutor", index: rawMatch.index, end: rawMatch.end };
}
}
return staticIdentifierAliasSourceMatchAt(structure, aliases, sourceStart, sourceEnd, sourceStart);
};
}
function rawSqlExecutorWrapperSources(content, structure, aliases = []) {
return staticWrapperSources(content, structure, rawSqlSourceMatcher(aliases));
}
function rawSqlWrappedExecutorInvocationSources(content, structure) {
const sources = [];
const directAliases = propagateStaticIdentifierAliases(structure, rawSqlExecutorDirectAliases(content, structure), (_sourceAlias, initializer) => ({
name: initializer.name,
index: initializer.index,
end: initializer.end
}));
for (const usage of staticWrapperSourceUsageMatches(content, structure, rawSqlExecutorWrapperSources(content, structure, directAliases))) {
const operation = rawSqlMemberOperation(content, structure, usage.end);
sources.push(...rawSqlInvocationSourcesFromOperation(content, structure, operation, usage.index));
}
return sources;
}
function rawSqlAliasInvocationSources(content, structure, aliases) {
const sources = [];
for (const alias of aliases) {
const aliasCallPattern = new RegExp(String.raw`(?:^|[^\w$])(${escapeRegExp(alias.name)})\b\s*(?:<[^>()` + "`" + String.raw`]*>)?\s*`, "g");
for (const aliasMatch of structure.matchAll(aliasCallPattern)) {
if (typeof aliasMatch.index !== "number" || aliasMatch.index <= alias.end) continue;
const aliasIndex = aliasMatch.index + aliasMatch[0].indexOf(alias.name);
if (aliasIndex <= alias.end) continue;
if (isMemberIdentifierReference(structure, aliasIndex)) continue;
const operationStart = skipWhitespaceForward(structure, aliasIndex + alias.name.length);
const operation = rawSqlMemberOperation(content, structure, operationStart);
sources.push(...rawSqlInvocationSourcesFromOperation(content, structure, operation, aliasIndex));
}
}
return sources;
}
function rawSqlBoundAliasInvocationSources(content, structure) {
return rawSqlAliasInvocationSources(
content,
structure,
findRawSqlBoundExecutorAliases(content, structure).concat(findRawSqlAssignedBoundExecutorAliases(content, structure))
);
}
function rawSqlPlainAliasInvocationSources(content, structure) {
return rawSqlAliasInvocationSources(
content,
structure,
findRawSqlPlainExecutorAliases(content, structure)
.concat(findRawSqlAssignedPlainExecutorAliases(content, structure))
.concat(findRawSqlDestructuredExecutorAliases(content, structure))
.concat(findRawSqlAssignedDestructuredExecutorAliases(content, structure))
);
}
function rawSqlInvocationSources(content, structure = rawSqlStructure(content)) {
const scanStructure = stripParenthesesAroundStaticMemberChains(content, structure);
const sources = [];
const builtinInvokerAliases = findStaticBuiltinInvokerAliases(content, scanStructure);
for (const match of rawSqlExecutorAccessMatches(content, scanStructure)) {
let callStart = skipWhitespaceForward(scanStructure, match.end);
const operation = rawSqlMemberOperation(content, scanStructure, callStart);
sources.push(...rawSqlInvocationSourcesFromOperation(content, scanStructure, operation, match.index));
}
return sources.concat(
rawSqlReflectApplyInvocationSources(content, scanStructure, builtinInvokerAliases),
rawSqlFunctionPrototypeInvocationSources(content, scanStructure, builtinInvokerAliases),
rawSqlWrappedExecutorInvocationSources(content, scanStructure),
rawSqlAliasInvocationSources(content, scanStructure, rawSqlExecutorAliases(content, scanStructure))
);
}
function simpleIdentifierSourceName(structure, sourceStart, sourceEnd) {
const identifierStart = skipWhitespaceForward(structure, sourceStart);
const identifierEnd = skipWhitespaceBackward(structure, sourceEnd - 1) + 1;
if (identifierStart >= identifierEnd) return null;
const source = structure.slice(identifierStart, identifierEnd);
return /^[A-Za-z_$][\w$]*$/.test(source) ? source : null;
}
function hasRawSqlInvocationMatching(content, predicate, structure = rawSqlStructure(content)) {
for (const invocation of rawSqlInvocationSources(content, structure)) {
const { source, index, end, beforeIndex } = invocation;
if (rawSqlScanCandidateSources(content, structure, source, index, end, beforeIndex).some((candidateSource) => predicate(candidateSource))) {
return true;
}
const variableName = simpleIdentifierSourceName(structure, index, end);
if (
variableName &&
findVariableInitializerSources(content, structure, variableName, beforeIndex).some(({ source: variableSource, index: variableIndex, end: variableEnd }) =>
rawSqlScanCandidateSources(content, structure, variableSource, variableIndex, variableEnd, beforeIndex).some((candidateSource) =>
predicate(candidateSource)
)
)
) {
return true;
}
}
return false;
}
function hasDynamicIdentifierRawSqlWrite(content) {
return hasRawSqlInvocationMatching(content, hasDynamicRawSqlWrite);
}
function hasRawJobStatusWrite(content) {
// raw SQL 检测仍从原始 content 切片读取 SQL 字符串structure 只负责可靠找到调用边界。
return hasRawSqlInvocationMatching(content, hasRawJobRuntimeFieldWrite);
}
function hasForbiddenRawJobStatusWrite(filePath, content) {
// migration.sql 没有 Prisma raw 调用包装,必须直接扫描 SQL 文件本体。
if (filePath.endsWith(".sql")) return hasRawJobRuntimeFieldWrite(content);
return hasRawJobStatusWrite(content);
}
function hasRawAuditLogMutation(sqlSource, options = {}) {
const sql = normalizeRawSqlScanSource(sqlSource);
const auditLogTable = sqlTableReferencePattern("AuditLog");
const auditLogAppendOnlyFunction = sqlSpecificFunctionReferencePattern(auditLogAppendOnlyFunctionName);
const auditLogAppendOnlyFunctionList = sqlFunctionListContainingPattern(auditLogAppendOnlyFunction);
const identifier = sqlBareIdentifierPattern();
const triggerIdentifier = sqlTriggerIdentifierPattern();
// PostgreSQL UPDATE 允许目标表后带 `*` 继承标记AuditLog append-only 扫描必须覆盖该合法语法。
const createsPlainAuditLogAppendOnlyFunction = new RegExp(
String.raw`\bCREATE\s+FUNCTION\s+${auditLogAppendOnlyFunction}\b`,
"i"
).test(sql);
const createsOrReplacesAuditLogAppendOnlyFunction = new RegExp(
String.raw`\bCREATE\s+OR\s+REPLACE\s+FUNCTION\s+${auditLogAppendOnlyFunction}\b`,
"i"
).test(sql);
return (
new RegExp(String.raw`\bUPDATE\s+(?:ONLY\s+)?${auditLogTable}(?:\s*\*)?(?:\s+(?:AS\s+)?${identifier})?\s+SET\b`, "i").test(sql) ||
new RegExp(String.raw`\bDELETE\s+FROM\s+(?:ONLY\s+)?${auditLogTable}\b`, "i").test(sql) ||
new RegExp(
String.raw`\bINSERT\s+INTO\s+(?:ONLY\s+)?${auditLogTable}\b[\s\S]*?\bON\s+CONFLICT\b[\s\S]*?\bDO\s+UPDATE\b`,
"i"
).test(sql) ||
new RegExp(String.raw`\bMERGE\s+INTO\s+(?:ONLY\s+)?${auditLogTable}(?:\s+(?:AS\s+)?${identifier})?\b`, "i").test(sql) ||
new RegExp(sqlTruncateTableListPattern(auditLogTable), "i").test(sql) ||
new RegExp(sqlDropTableListPattern(auditLogTable), "i").test(sql) ||
new RegExp(String.raw`\bDROP\s+TRIGGER\s+(?:IF\s+EXISTS\s+)?${triggerIdentifier}\s+ON\s+(?:ONLY\s+)?${auditLogTable}\b`, "i").test(sql) ||
new RegExp(
String.raw`\bALTER\s+TABLE\s+(?:IF\s+EXISTS\s+)?(?:ONLY\s+)?${auditLogTable}\s+(?:DISABLE\s+(?:TRIGGER|RULE)\b|DROP\b|RENAME\b)`,
"i"
).test(sql) ||
new RegExp(String.raw`\bDROP\s+(?:FUNCTION|ROUTINE)\s+(?:IF\s+EXISTS\s+)?${auditLogAppendOnlyFunctionList}(?=\s|;|$)`, "i").test(
sql
) ||
(!options.allowAuditLogAppendOnlyFunctionPlainCreate && createsPlainAuditLogAppendOnlyFunction) ||
(!options.allowAuditLogAppendOnlyFunctionCreateOrReplace && createsOrReplacesAuditLogAppendOnlyFunction) ||
new RegExp(String.raw`\bALTER\s+(?:FUNCTION|ROUTINE)\s+(?:IF\s+EXISTS\s+)?${auditLogAppendOnlyFunction}(?=\s|;|$)`, "i").test(sql)
);
}
function hasForbiddenRawAuditLogMutation(filePath, content, structure) {
const auditSqlOptions = auditLogAppendOnlyFunctionCreateOptions(filePath, content);
if (filePath.endsWith(".sql")) return hasRawAuditLogMutation(content, auditSqlOptions);
for (const invocation of rawSqlInvocationSources(content, structure)) {
const { source, index, end, beforeIndex } = invocation;
const hasForbiddenSql =
rawSqlScanCandidateSources(content, structure, source, index, end, beforeIndex).some((candidateSource) =>
hasRawAuditLogMutation(candidateSource, auditSqlOptions)
) ||
(() => {
const variableName = simpleIdentifierSourceName(structure, index, end);
return (
variableName !== null &&
findVariableInitializerSources(content, structure, variableName, beforeIndex).some(
({ source: variableSource, index: variableIndex, end: variableEnd }) =>
rawSqlScanCandidateSources(content, structure, variableSource, variableIndex, variableEnd, beforeIndex).some((candidateSource) =>
hasRawAuditLogMutation(candidateSource, auditSqlOptions)
)
)
);
})();
if (!hasForbiddenSql) continue;
if (isAllowedAuditTriggerAssertion(filePath, content, structure, beforeIndex)) continue;
return true;
}
return false;
}
function jobExecutionStoreClassSpans(content) {
const spans = [];
const structure = maskStringLiterals(maskCommentsOutsideStringLiterals(content));
const classPattern = /\b(?:export\s+)?class\s+JobExecutionStore\b/g;
for (const match of structure.matchAll(classPattern)) {
if (typeof match.index !== "number") continue;
const openBraceIndex = structure.indexOf("{", match.index + match[0].length);
if (openBraceIndex === -1) continue;
const closeBraceIndex = findMatchingBrace(structure, openBraceIndex);
if (closeBraceIndex === -1) continue;
spans.push({ start: match.index, end: closeBraceIndex + 1 });
}
return spans;
}
function maskSpans(content, spans) {
if (spans.length === 0) return content;
let output = "";
let cursor = 0;
for (const span of spans.toSorted((left, right) => left.start - right.start)) {
if (span.start < cursor) continue;
output += content.slice(cursor, span.start);
output += " ".repeat(span.end - span.start);
cursor = span.end;
}
return output + content.slice(cursor);
}
function maskJobExecutionStoreClassBodies(content) {
// 只遮蔽 JobExecutionStore 自身;同文件里的 API service / controller 仍要按普通边界扫描。
return maskSpans(content, jobExecutionStoreClassSpans(content));
}
function hasForbiddenMigrationGuardSetter(content) {
const sql = stripSqlComments(content);
return /set_config\s*\(|SET\s+LOCAL/i.test(sql);
}
function hasStateTransitionSpecifier(value) {
return /state-transition(?:\/index\.js)?/.test(value);
}
function unwrapCompleteParenthesizedJavaScriptExpression(structure, sourceStart, sourceEnd) {
let trimmedStart = skipWhitespaceForward(structure, sourceStart);
let trimmedEnd = skipWhitespaceBackward(structure, sourceEnd - 1) + 1;
while (trimmedStart < trimmedEnd && structure[trimmedStart] === "(") {
const closeParenIndex = findMatchingParen(structure, trimmedStart);
if (closeParenIndex !== trimmedEnd - 1) break;
const innerStart = skipWhitespaceForward(structure, trimmedStart + 1);
const innerEnd = skipWhitespaceBackward(structure, closeParenIndex - 1) + 1;
if (innerStart >= innerEnd) break;
// 只剥离完整包住表达式的括号,避免改变 ("a") + b 这类拼接分片边界。
trimmedStart = innerStart;
trimmedEnd = innerEnd;
}
return { start: trimmedStart, end: trimmedEnd };
}
function decodeStaticTemplateLiteralExpressionValue(content, structure, sourceStart, sourceEnd, beforeIndex, seenVariables, depth) {
if (content[sourceStart] !== "`") return null;
const templateEnd = findStringLiteralEnd(content, sourceStart);
if (templateEnd !== sourceEnd - 1) return null;
let output = "";
let cursor = sourceStart + 1;
while (cursor < templateEnd) {
if (content[cursor] === "\\") {
if (cursor + 1 < templateEnd) {
output += decodeJavaScriptStringLiteralBody(content.slice(cursor, cursor + 2));
cursor += 2;
continue;
}
return null;
}
if (content[cursor] === "$" && content[cursor + 1] === "{") {
const expressionStart = cursor + 2;
const expressionEnd = findMatchingBrace(content, cursor + 1);
if (expressionEnd === -1 || expressionEnd > templateEnd) return null;
const expressionStructure =
structure.slice(0, expressionStart) + content.slice(expressionStart, expressionEnd) + structure.slice(expressionEnd);
const values = staticJavaScriptExpressionValues(
content,
expressionStructure,
content.slice(expressionStart, expressionEnd),
expressionStart,
beforeIndex,
seenVariables,
depth + 1
);
if (values.length !== 1) return null;
output += values[0];
cursor = expressionEnd + 1;
continue;
}
output += content[cursor];
cursor += 1;
}
return output;
}
function staticJavaScriptExpressionValues(content, structure, source, sourceStart, beforeIndex, seenVariables, depth) {
if (depth >= rawSqlVariableResolutionMaxDepth) return [];
const { start: trimmedStart, end: trimmedEnd } = unwrapCompleteParenthesizedJavaScriptExpression(
structure,
sourceStart,
sourceStart + source.length
);
if (trimmedStart >= trimmedEnd) return [];
const expression = content.slice(trimmedStart, trimmedEnd);
const literalValue = decodeStaticJavaScriptStringLiteralSource(expression);
if (literalValue !== null) return [literalValue];
const templateValue = decodeStaticTemplateLiteralExpressionValue(content, structure, trimmedStart, trimmedEnd, beforeIndex, seenVariables, depth);
if (templateValue !== null) return [templateValue];
const identifierName = simpleIdentifierSourceName(structure, trimmedStart, trimmedEnd);
if (identifierName) {
return staticStringVariableInitializers(content, structure, identifierName, beforeIndex, seenVariables, depth + 1);
}
const expressionStructure = structure.slice(trimmedStart, trimmedEnd);
let parts = [];
let partStart = 0;
let parenDepth = 0;
let braceDepth = 0;
let bracketDepth = 0;
for (let cursor = 0; cursor < expressionStructure.length; cursor += 1) {
const char = expressionStructure[cursor];
if (char === "(") parenDepth += 1;
if (char === ")") parenDepth -= 1;
if (char === "{") braceDepth += 1;
if (char === "}") braceDepth -= 1;
if (char === "[") bracketDepth += 1;
if (char === "]") bracketDepth -= 1;
if (char !== "+" || parenDepth !== 0 || braceDepth !== 0 || bracketDepth !== 0) continue;
parts.push({ start: trimmedStart + partStart, end: trimmedStart + cursor });
partStart = cursor + 1;
}
if (parts.length === 0) return [];
parts.push({ start: trimmedStart + partStart, end: trimmedEnd });
const values = [];
for (const part of parts) {
const partSource = content.slice(part.start, part.end);
const partValues = staticJavaScriptExpressionValues(content, structure, partSource, part.start, beforeIndex, seenVariables, depth + 1);
if (partValues.length !== 1) return [];
values.push(partValues[0]);
}
// 只折叠全静态字符串拼接;任何动态片段都保持未知,不影响其它动态 import。
return [values.join("")];
}
function staticStringVariableInitializers(content, structure, variableName, beforeIndex, seenVariables = new Set(), depth = 0) {
if (depth >= rawSqlVariableResolutionMaxDepth || seenVariables.has(variableName)) return [];
const values = [];
const nestedSeenVariables = new Set(seenVariables);
nestedSeenVariables.add(variableName);
const initializerPatterns = [
new RegExp(String.raw`\b(?:const|let|var)\s+${escapeRegExp(variableName)}\b(?:\s*:[^=;]+)?\s*=`, "g"),
new RegExp(String.raw`\b${escapeRegExp(variableName)}\b\s*=`, "g")
];
for (const initializerPattern of initializerPatterns) {
for (const match of structure.matchAll(initializerPattern)) {
if (typeof match.index !== "number" || match.index >= beforeIndex) continue;
const variableOffset = match[0].indexOf(variableName);
const variableIndex = match.index + variableOffset;
if (initializerPattern !== initializerPatterns[0] && isMemberIdentifierReference(structure, variableIndex)) continue;
if (initializerPattern !== initializerPatterns[0] && /\b(?:const|let|var)\s*$/.test(structure.slice(Math.max(0, match.index - 16), variableIndex))) {
continue;
}
const equalsIndex = structure.indexOf("=", match.index + match[0].lastIndexOf("="));
if (equalsIndex === -1 || equalsIndex >= beforeIndex) continue;
if ("=><!".includes(structure[equalsIndex - 1] ?? "") || ["=", ">"].includes(structure[equalsIndex + 1] ?? "")) continue;
// 静态 key alias 既支持声明初始化,也支持调用点前的简单后续赋值。
const initializerStart = skipWhitespaceForward(structure, equalsIndex + 1);
const initializerEnd = findStatementEnd(structure, initializerStart);
values.push(
...staticJavaScriptExpressionValues(
content,
structure,
content.slice(initializerStart, initializerEnd),
initializerStart,
beforeIndex,
nestedSeenVariables,
depth
)
);
}
}
return values;
}
function hasStateTransitionVariableImport(content) {
const structure = maskStringLiterals(maskCommentsOutsideStringLiterals(content));
const dynamicImportPattern = /\b(?:import|require)\s*\(/g;
for (const match of structure.matchAll(dynamicImportPattern)) {
if (typeof match.index !== "number") continue;
const openParenIndex = structure.indexOf("(", match.index);
const closeParenIndex = openParenIndex === -1 ? -1 : findMatchingParen(structure, openParenIndex);
if (closeParenIndex === -1) continue;
const argumentStart = skipWhitespaceForward(structure, openParenIndex + 1);
const argumentEnd = skipWhitespaceBackward(structure, closeParenIndex - 1) + 1;
if (argumentStart >= argumentEnd) continue;
// 动态 import/require 的实参统一复用静态 JS 表达式折叠,覆盖 identifier、递归 alias 与直接字符串拼接。
if (
staticJavaScriptExpressionValues(
content,
structure,
content.slice(argumentStart, argumentEnd),
argumentStart,
match.index,
new Set(),
0
).some(hasStateTransitionSpecifier)
) {
return true;
}
}
return hasStateTransitionRequireAliasImport(content, structure);
}
function findRequireAliases(structure) {
const aliases = [];
const destructuringPattern = /\b(?:const|let|var)\s*\{\s*require\s*:\s*([A-Za-z_$][\w$]*)\s*\}\s*=\s*module\b/g;
const patterns = [
/\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\b(?:\s*:[^=;]+)?\s*=\s*require\b/g,
/\b([A-Za-z_$][\w$]*)\b\s*=\s*require\b/g
];
for (const match of structure.matchAll(destructuringPattern)) {
if (typeof match.index !== "number") continue;
const moduleIndex = match.index + match[0].lastIndexOf("module");
const afterModule = skipWhitespaceForward(structure, moduleIndex + "module".length);
const statementEnd = findStatementEnd(structure, moduleIndex);
if (afterModule !== statementEnd && !hasLineTerminator(structure.slice(afterModule, statementEnd))) continue;
// CommonJS module.require 的解构 alias 只接受简单同文件声明。
aliases.push({ name: match[1], index: match.index, end: afterModule });
}
for (const pattern of patterns) {
for (const match of structure.matchAll(pattern)) {
if (typeof match.index !== "number") continue;
if (isMemberIdentifierReference(structure, match.index + match[0].indexOf(match[1]))) continue;
const requireIndex = match.index + match[0].lastIndexOf("require");
const afterRequire = skipWhitespaceForward(structure, requireIndex + "require".length);
const statementEnd = findStatementEnd(structure, requireIndex);
if (afterRequire !== statementEnd && !hasLineTerminator(structure.slice(afterRequire, statementEnd))) continue;
// require alias 只跟踪简单声明/赋值,复杂初始化器不进入 import 边界判断。
aliases.push({ name: match[1], index: match.index, end: afterRequire });
}
}
return aliases;
}
function hasStateTransitionRequireAliasImport(content, structure) {
for (const alias of findRequireAliases(structure)) {
const aliasCallPattern = new RegExp(String.raw`\b${escapeRegExp(alias.name)}\b\s*\(`, "g");
for (const match of structure.matchAll(aliasCallPattern)) {
if (typeof match.index !== "number" || match.index <= alias.end) continue;
if (isMemberIdentifierReference(structure, match.index)) continue;
const openParenIndex = structure.indexOf("(", match.index + match[0].length - 1);
const closeParenIndex = openParenIndex === -1 ? -1 : findMatchingParen(structure, openParenIndex);
if (closeParenIndex === -1) continue;
const argument = nthCallArgumentSource(content, structure, openParenIndex, closeParenIndex, 0);
if (!argument) continue;
if (
staticJavaScriptExpressionValues(content, structure, argument.source, argument.index, match.index, new Set(), 0).some(
hasStateTransitionSpecifier
)
) {
return true;
}
}
}
return false;
}
function javaScriptStringLiteralSourcePattern() {
return String.raw`(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|` + "`(?:\\\\.|[^`\\\\])*`" + String.raw`)`;
}
function staticModuleSpecifierValues(content) {
const values = [];
const structure = maskCommentsOutsideStringLiterals(content);
const literalSource = javaScriptStringLiteralSourcePattern();
const patterns = [
new RegExp(String.raw`\bimport\s+(${literalSource})`, "g"),
new RegExp(String.raw`\bfrom\s+(${literalSource})`, "g")
];
for (const pattern of patterns) {
for (const match of structure.matchAll(pattern)) {
if (typeof match.index !== "number") continue;
const literalOffset = match[0].lastIndexOf(match[1]);
const literalStart = match.index + literalOffset;
const decodedValue = decodeStaticJavaScriptStringLiteralSource(content.slice(literalStart, literalStart + match[1].length));
if (decodedValue !== null) values.push(decodedValue);
}
}
return values;
}
function hasStaticStateTransitionSpecifier(content) {
return staticModuleSpecifierValues(content).some(hasStateTransitionSpecifier);
}
function hasStateTransitionServiceImport(content) {
// state-transition 只能由白名单路径直接耦合;静态、动态与 CommonJS 字符串入口都要按同一路径语义拦截。
return (
hasStaticStateTransitionSpecifier(content) ||
/\bimport\s*\(\s*(?:["'][^"']*state-transition(?:\/index\.js)?["']|`[^`]*state-transition(?:\/index\.js)?`)\s*\)/.test(
content
) ||
/\brequire\s*\(\s*(?:["'][^"']*state-transition(?:\/index\.js)?["']|`[^`]*state-transition(?:\/index\.js)?`)\s*\)/.test(
content
) ||
hasStateTransitionVariableImport(content) ||
/StateTransitionService/.test(content)
);
}
function isAllowedStateTransitionImportPath(filePath) {
// jobs 测试需要 StateTransitionService 创建受 guard 保护的版本夹具;这不是生产调用入口。
if (stateTransitionImportFixtureTests.has(filePath)) return true;
if (allowedStateTransitionImportPaths.has(filePath)) return true;
return allowedStateTransitionImportPrefixes.some((prefix) => filePath.startsWith(prefix));
}
function isAllowedS1AnchorPath(filePath) {
return (
filePath.startsWith("apps/api/prisma/") ||
filePath.startsWith(stateTransitionModulePrefix) ||
filePath.startsWith(auditModulePrefix) ||
filePath === historicalPrismaSchemaTest
);
}
function hasPrismaModelMethodCallExcluding(content, models, methods, excludeCallBody) {
const scanContent = stripStringLiteralsExceptPropertyKeys(maskCommentsOutsideStringLiterals(content));
for (const match of prismaModelMethodCallMatchesInStructure(content, scanContent, models, methods)) {
const openParenIndex = scanContent[match.callStart] === "(" ? match.callStart : scanContent.indexOf("(", match.callStart);
if (openParenIndex === -1) return true;
const closeParenIndex = findMatchingParen(scanContent, openParenIndex);
if (closeParenIndex === -1) return true;
const callBody = scanContent.slice(openParenIndex, closeParenIndex + 1);
if (!excludeCallBody(callBody)) return true;
}
return false;
}
function isSimpleIdentifierKey(structure, partStart, partEnd, expectedKey) {
const keyStart = skipWhitespaceForward(structure, partStart);
const keyEnd = destructuringPropertyKeyEnd(structure, keyStart, partEnd);
if (keyEnd === -1) return false;
if (structure.slice(keyStart, keyEnd).trim() !== expectedKey) return false;
return structure[skipWhitespaceForward(structure, keyEnd)] === ":";
}
function objectPropertyValueSpan(structure, partStart, partEnd) {
const keyStart = skipWhitespaceForward(structure, partStart);
const keyEnd = destructuringPropertyKeyEnd(structure, keyStart, partEnd);
if (keyEnd === -1) return null;
const colonIndex = skipWhitespaceForward(structure, keyEnd);
if (structure[colonIndex] !== ":") return null;
const valueStart = skipWhitespaceForward(structure, colonIndex + 1);
const valueEnd = skipWhitespaceBackward(structure, partEnd - 1) + 1;
return valueStart < valueEnd ? { start: valueStart, end: valueEnd } : null;
}
function soleObjectPropertyValueSpan(structure, objectStart, objectEnd, expectedKey) {
if (structure[objectStart] !== "{" || objectEnd <= objectStart) return null;
const closeBraceIndex = findMatchingBrace(structure, objectStart);
if (closeBraceIndex === -1 || closeBraceIndex !== objectEnd - 1) return null;
const parts = destructuringParts(structure, objectStart + 1, closeBraceIndex).filter(
(part) => skipWhitespaceForward(structure, part.start) < skipWhitespaceBackward(structure, part.end - 1) + 1
);
if (parts.length !== 1) return null;
const [part] = parts;
if (!isSimpleIdentifierKey(structure, part.start, part.end, expectedKey)) return null;
return objectPropertyValueSpan(structure, part.start, part.end);
}
function isRunIdStartsWithValue(structure, valueStart, valueEnd) {
const value = structure.slice(valueStart, valueEnd).trim();
return value === "runId";
}
function isAllowedJobDeleteCleanupCallBody(filePath, callBody) {
if (!isTestFile(filePath)) return false;
const callStart = skipWhitespaceForward(callBody, 0);
if (callBody[callStart] !== "(") return false;
const callEnd = findMatchingParen(callBody, callStart);
if (callEnd === -1 || skipWhitespaceForward(callBody, callEnd + 1) !== callBody.length) return false;
const argument = nthCallArgumentSource(callBody, callBody, callStart, callEnd, 0);
if (!argument) return false;
if (nthCallArgumentSource(callBody, callBody, callStart, callEnd, 1)) return false;
const argumentStart = skipWhitespaceForward(callBody, argument.index);
const argumentEnd = skipWhitespaceBackward(callBody, argument.end - 1) + 1;
// 测试清理只能是 deleteMany({ where: { id: { startsWith: runId } } });任何 OR/AND/NOT、额外 key、spread/computed 都 fail closed。
const whereValue = soleObjectPropertyValueSpan(callBody, argumentStart, argumentEnd, "where");
if (!whereValue) return false;
const whereStart = skipWhitespaceForward(callBody, whereValue.start);
const whereEnd = skipWhitespaceBackward(callBody, whereValue.end - 1) + 1;
const idValue = soleObjectPropertyValueSpan(callBody, whereStart, whereEnd, "id");
if (!idValue) return false;
const idStart = skipWhitespaceForward(callBody, idValue.start);
const idEnd = skipWhitespaceBackward(callBody, idValue.end - 1) + 1;
const startsWithValue = soleObjectPropertyValueSpan(callBody, idStart, idEnd, "startsWith");
if (!startsWithValue) return false;
return isRunIdStartsWithValue(callBody, startsWithValue.start, startsWithValue.end);
}
function hasForbiddenJobPrismaDelete(filePath, content) {
// 单行 delete 无法表达 runId 前缀清理语义,始终视为 Job 运行态删除;只有 deleteMany 可走测试清理白名单。
return (
prismaModelMethodCallMatches(content, ["job"], ["delete"]).length > 0 ||
hasPrismaModelMethodCallExcluding(content, ["job"], ["deleteMany"], (callBody) => isAllowedJobDeleteCleanupCallBody(filePath, callBody))
);
}
function hasJobStatusWrite(filePath, content) {
// Job 创建路径同样会写入运行态字段S1 必须统一收敛到 JobExecutionStore。
return (
hasPrismaModelMethodCallContaining(content, ["job"], jobPrismaWriteMethods, { test: hasForbiddenJobWriteCallBody }) ||
hasForbiddenJobPrismaDelete(filePath, content) ||
hasRawJobStatusWrite(content)
);
}
function hasForbiddenJobStatusWrite(filePath, content) {
// SQL 文件走原始 SQL 扫描;源码文件继续复用 Prisma 与 raw 调用扫描。
if (filePath.endsWith(".sql")) return hasForbiddenRawJobStatusWrite(filePath, content);
return hasJobStatusWrite(filePath, content);
}
function isAllowedJobExecutionStorePath(filePath) {
return filePath === jobExecutionStorePath;
}
async function collectFiles(root, relativeDir) {
const absoluteDir = path.join(root, relativeDir);
const entries = await readdir(absoluteDir, { withFileTypes: true }).catch((error) => {
if (error && error.code === "ENOENT") return [];
throw error;
});
const files = [];
for (const entry of entries) {
const relativePath = normalizePath(path.join(relativeDir, entry.name));
if (entry.isDirectory()) {
if (ignoredScanSegments.has(entry.name)) continue;
files.push(...(await collectFiles(root, relativePath)));
continue;
}
if (entry.isFile() && sourceLikeFilePattern.test(entry.name) && !isIgnoredScanPath(relativePath)) {
files.push({
path: relativePath,
content: await readFile(path.join(root, relativePath), "utf8")
});
}
}
return files;
}
async function collectWorkspaceFiles(root) {
const files = [];
for (const scanRoot of scanRoots) {
files.push(...(await collectFiles(root, scanRoot)));
}
return files;
}
function pushViolation(violations, filePath, reason, detail) {
violations.push({ path: filePath, reason, detail });
}
function normalizedPathSegments(filePath) {
return normalizePath(filePath)
.replace(/^\/+|\/+$/g, "")
.split("/")
.filter((segment) => segment.length > 0);
}
function hasS2ApprovedTermDenyPath(filePath) {
const normalizedFilePath = `/${normalizePath(filePath).replace(/^\/+|\/+$/g, "")}/`;
if (s2ApprovedTermDenyPathFragments.some((fragment) => normalizedFilePath.includes(fragment))) return true;
return normalizedPathSegments(filePath).some((segment) => s2ApprovedTermDenyPathSegments.has(segment));
}
function hasS2ApprovedTermAllowPath(filePath) {
if (s2ApprovedTermAllowExactPaths.has(filePath)) return true;
return s2ApprovedTermAllowPathPrefixes.some((prefix) => filePath.startsWith(prefix));
}
function isAllowedS2LegacyS3TermReference(filePath, term) {
return s2LegacyS3TermAllowExactPaths.get(term)?.has(filePath) ?? false;
}
function isAllowedS2ApprovedTermReference(filePath, term) {
if (isAllowedS2LegacyS3TermReference(filePath, term)) return true;
if (!s2ApprovedTerms.has(term)) return false;
if (hasS2ApprovedTermDenyPath(filePath)) return false;
// S2 approved 术语只能出现在 Creator Workbench、AI design、GameIR 和共享合同边界内。
// 不从 denylist 全局删除这些词,避免 simulation/runtime/conversion/feed 等后续阶段路径借名绕过门禁。
return hasS2ApprovedTermAllowPath(filePath);
}
function hasS3ApprovedTermAllowPath(filePath) {
if (s3ApprovedTermAllowExactPaths.has(filePath)) return true;
return s3ApprovedTermAllowPathPrefixes.some((prefix) => filePath.startsWith(prefix));
}
function isAllowedS3ApprovedTermReference(filePath, term) {
if (isAllowedS2ApprovedTermReference(filePath, term)) return true;
if (s3InheritedS2Terms.has(term) && hasS3ApprovedTermAllowPath(filePath)) return true;
// S3 只允许在 simulation slice 自有 API 模块和共享合同边界使用这些术语。
// 既有 S2 GameIR 合同可继续描述 simulation payload/NPC graph但不能借此开放 S3 新模块。
return s3ApprovedTerms.has(term) && hasS3ApprovedTermAllowPath(filePath);
}
function shouldSkipLaterPhaseTermViolation(filePath, term, phase) {
if (phase === "S2" && isAllowedS2ApprovedTermReference(filePath, term)) return true;
if (phase === "S1") return false;
if (phase === "S2") return false;
if (phase === "S3") return isAllowedS3ApprovedTermReference(filePath, term);
throw new Error(`Unsupported scope phase: ${phase}`);
}
function findS3OwnedPathScopeViolations(file, violations, phase) {
if (phase === "S3") return;
const normalizedFilePath = normalizePath(file.path);
if (s3OwnedPathPrefixes.some((prefix) => normalizedFilePath.startsWith(prefix))) {
pushViolation(violations, file.path, "LATER_PHASE_SCOPE_LEAK", "S3-owned path");
}
}
function findLaterPhaseScopeViolations(file, violations, phase) {
for (const term of laterPhaseDeniedTerms) {
// 路径名本身不能提供 validateContract 调用上下文harness-client 测试也不能按路径泛化放行。
if (pathContainsLaterPhaseTerm(file.path, term)) {
if (shouldSkipLaterPhaseTermViolation(file.path, term, phase)) continue;
pushViolation(violations, file.path, "LATER_PHASE_SCOPE_LEAK", term);
continue;
}
let hasContentViolation = false;
for (const contentTerm of contentDeniedTermVariants(term)) {
let index = file.content.indexOf(contentTerm);
while (index !== -1) {
if (!isAllowedHarnessValidationContractReferenceAt(file.path, file.content, contentTerm, index)) {
if (shouldSkipLaterPhaseTermViolation(file.path, term, phase)) break;
pushViolation(violations, file.path, "LATER_PHASE_SCOPE_LEAK", term);
hasContentViolation = true;
break;
}
index = file.content.indexOf(contentTerm, index + contentTerm.length);
}
if (hasContentViolation) break;
}
}
}
function findHarnessClientContractReferenceViolations(file, violations) {
if (!isHarnessClientTestPath(file.path)) return;
for (const term of harnessClientContractWatchTerms) {
let index = file.content.indexOf(term);
while (index !== -1) {
if (!isAllowedHarnessClientContractReferenceAt(file.path, file.content, term, index)) {
pushViolation(violations, file.path, "HARNESS_CONTRACT_REFERENCE_OUTSIDE_VALIDATION", term);
break;
}
index = file.content.indexOf(term, index + term.length);
}
}
}
function s1AnchorTermsForPhase(phase) {
if (phase === "S1") return s1AnchorTerms;
if (phase === "S2" || phase === "S3") {
// S2/S3 会启用 S1 已存在的创作 Agent anchor审核/生命周期 anchor 仍不能进入后续业务实现。
return s1AnchorTerms.filter((term) => !s2ApprovedS1AnchorTerms.has(term));
}
throw new Error(`Unsupported scope phase: ${phase}`);
}
function findS1AnchorScopeViolations(file, violations, phase) {
if (isAllowedS1AnchorPath(file.path)) return;
for (const term of s1AnchorTermsForPhase(phase)) {
if (file.content.includes(term)) {
pushViolation(violations, file.path, "S1_ANCHOR_SCOPE_MISUSE", term);
}
}
}
function findAuditBoundaryViolations(file, violations) {
if (isGeneratedPrismaFile(file.path)) return;
if (hasForbiddenAuditMutation(file.path, file.content)) {
pushViolation(violations, file.path, "AUDIT_UPDATE_DELETE_FORBIDDEN", "auditLog update/delete/upsert");
}
}
function findDynamicRawSqlViolations(file, violations) {
if (isGeneratedPrismaFile(file.path)) return;
if (hasDynamicIdentifierRawSqlWrite(file.content)) {
pushViolation(violations, file.path, "DYNAMIC_RAW_SQL_WRITE_FORBIDDEN", "dynamic raw SQL write identifier");
}
}
function findStateBoundaryViolations(file, violations) {
if (isHistoricalTestFile(file.path) || isGeneratedPrismaFile(file.path)) return;
if (file.path.startsWith("apps/worker/")) {
if (/@prisma\/client|generated\/prisma|PrismaClient/.test(file.content)) {
pushViolation(violations, file.path, "WORKER_DB_IMPORT_FORBIDDEN", "Prisma import in worker");
}
if (/state-transition|StateTransitionService|app\.state_transition_guard|set_config\s*\(|SET\s+LOCAL/i.test(file.content)) {
pushViolation(violations, file.path, "WORKER_STATE_TRANSITION_IMPORT_FORBIDDEN", "state transition coupling in worker");
}
return;
}
if (hasStateTransitionServiceImport(file.content) && !isAllowedStateTransitionImportPath(file.path)) {
pushViolation(violations, file.path, "STATE_TRANSITION_SERVICE_IMPORT_FORBIDDEN", "state-transition service import");
}
if (file.path.startsWith(stateTransitionModulePrefix)) return;
if (file.path.startsWith(prismaMigrationPrefix)) {
// migration 允许安装 trigger/current_setting guard但不允许写运行时 guard setter。
if (hasForbiddenMigrationGuardSetter(file.content)) {
pushViolation(violations, file.path, "STATE_GUARD_SETTER_OUTSIDE_STATE_TRANSITION", "migration runtime guard setter");
}
if (hasGuardedPrismaWrite(file.path, file.content)) {
pushViolation(violations, file.path, "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION", "migration guarded model write");
}
return;
}
if (hasGuardedPrismaWrite(file.path, file.content)) {
pushViolation(violations, file.path, "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION", "guarded model write");
}
if (hasRuntimeGuardSetter(file.content)) {
pushViolation(violations, file.path, "STATE_GUARD_SETTER_OUTSIDE_STATE_TRANSITION", "runtime guard setter");
}
}
function findJobExecutionBoundaryViolations(file, violations) {
const scanContent = isAllowedJobExecutionStorePath(file.path) ? maskJobExecutionStoreClassBodies(file.content) : file.content;
if (hasForbiddenJobStatusWrite(file.path, scanContent)) {
pushViolation(violations, file.path, "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE", "Job status write");
}
}
function findViolationsInFiles(files) {
return findScopeViolationsInFiles(files, "S1");
}
function findScopeViolationsInFiles(files, phase) {
const violations = [];
for (const file of files) {
if (isIgnoredScanPath(file.path) || isGeneratedPrismaFile(file.path)) continue;
findS3OwnedPathScopeViolations(file, violations, phase);
findLaterPhaseScopeViolations(file, violations, phase);
findHarnessClientContractReferenceViolations(file, violations);
findS1AnchorScopeViolations(file, violations, phase);
findAuditBoundaryViolations(file, violations);
findStateBoundaryViolations(file, violations);
findDynamicRawSqlViolations(file, violations);
findJobExecutionBoundaryViolations(file, violations);
}
return violations;
}
async function findS1ScopeViolations(root) {
return findScopeViolations(root, "S1");
}
async function findScopeViolations(root, phase) {
return findScopeViolationsInFiles(await collectWorkspaceFiles(root), phase);
}
async function withFixture(files, callback) {
const root = await mkdtemp(path.join(os.tmpdir(), "huijing-s1-scope-"));
try {
for (const [relativePath, content] of Object.entries(files)) {
const absolutePath = path.join(root, relativePath);
await mkdir(path.dirname(absolutePath), { recursive: true });
await writeFile(absolutePath, content);
}
return await callback(root);
} finally {
await rm(root, { recursive: true, force: true });
}
}
function assertHasReason(violations, reason, label) {
if (!violations.some((violation) => violation.reason === reason)) {
throw new Error(`${label}: expected ${reason}`);
}
}
function assertNoViolations(violations, label) {
if (violations.length > 0) {
const details = violations.map(formatViolation).join("; ");
throw new Error(`${label}: expected no violations, got ${details}`);
}
}
function assertThrows(fn, label) {
try {
fn();
} catch {
return;
}
throw new Error(`${label}: expected throw`);
}
function formatViolation(violation) {
return `${violation.path}: ${violation.reason}${violation.detail ? ` (${violation.detail})` : ""}`;
}
function parsePhaseArg(argv) {
const phaseFlagIndexes = argv.flatMap((arg, index) => (arg === "--phase" ? [index] : []));
if (phaseFlagIndexes.length !== 1) {
throw new Error("Expected exactly one --phase S1|S2|S3");
}
const [phaseFlagIndex] = phaseFlagIndexes;
if (phaseFlagIndex === argv.length - 1) {
throw new Error("Missing required --phase S1|S2|S3");
}
const phase = argv[phaseFlagIndex + 1]?.toUpperCase();
if (!supportedPhases.has(phase)) {
throw new Error(`Unsupported --phase value: ${argv[phaseFlagIndex + 1] ?? ""}`);
}
return phase;
}
async function runSelfTests() {
assertThrows(() => parsePhaseArg(["--phase", "S2", "--phase", "S1"]), "duplicate phase flag");
if (parsePhaseArg(["--phase", "S3"]) !== "S3") {
throw new Error("S3 phase parsing: expected S3");
}
await withFixture(
{
"apps/api/src/modules/creator/index.ts": 'export const route = "creation-agent/messages";'
},
async (root) => {
const violations = await findS1ScopeViolations(root);
assertHasReason(violations, "LATER_PHASE_SCOPE_LEAK", "later-phase app route");
}
);
await withFixture(
{
"packages/harness-client/src/index.spec.ts":
'createHarnessClient().validateContract("MiniGameCodeConversion", { id: "fixture" });'
},
async (root) => {
const violations = await findS1ScopeViolations(root);
assertNoViolations(violations, "harness-client S0 contract fixture reference");
}
);
await withFixture(
{
"packages/harness-client/src/probe.spec.ts":
'createHarnessClient(); const businessModelName = "MiniGameProject";'
},
async (root) => {
const violations = await findS1ScopeViolations(root);
assertHasReason(violations, "LATER_PHASE_SCOPE_LEAK", "harness-client non-validation S0 contract probe");
}
);
await withFixture(
{
"packages/harness-client/src/bad-game-ir.spec.ts": 'const contractName = "GameIR";'
},
async (root) => {
const violations = await findS1ScopeViolations(root);
assertHasReason(violations, "LATER_PHASE_SCOPE_LEAK", "harness-client non-validation GameIR contract name");
}
);
await withFixture(
{
"packages/harness-client/src/bad-validation-report.spec.ts": 'const contractName = "ValidationReport";'
},
async (root) => {
const violations = await findS1ScopeViolations(root);
assertHasReason(
violations,
"HARNESS_CONTRACT_REFERENCE_OUTSIDE_VALIDATION",
"harness-client non-validation ValidationReport contract name"
);
}
);
await withFixture(
{
"packages/harness-client/src/index.spec.ts":
'createHarnessClient().validateContract("GameIR", { id: "fixture" });'
},
async (root) => {
const violations = await findS1ScopeViolations(root);
assertNoViolations(violations, "harness-client GameIR validation reference");
}
);
await withFixture(
{
"packages/harness-client/src/index.spec.ts":
'createHarnessClient().validateContract("ValidationReport", { id: "fixture" });'
},
async (root) => {
const violations = await findS1ScopeViolations(root);
assertNoViolations(violations, "harness-client ValidationReport validation reference");
}
);
await withFixture(
{
"packages/harness-client/src/index.spec.ts": 'expect(args[2]).toBe("GameIR");'
},
async (root) => {
const violations = await findS1ScopeViolations(root);
assertHasReason(violations, "LATER_PHASE_SCOPE_LEAK", "isolated harness-client args[2] contract assertion");
}
);
await withFixture(
{
"packages/harness-client/src/index.spec.ts": `
const runner = async (_file, args) => {
expect(args.slice(0, 2)).toEqual([realHarnessCli, "--contract"]);
expect(args[2]).toBe("GameIR");
};
`
},
async (root) => {
const violations = await findS1ScopeViolations(root);
assertNoViolations(violations, "harness-client args[2] assertion with runner contract context");
}
);
const harnessClientPathOnlyLaterPhaseFixtures = [
"packages/harness-client/src/game-ir.spec.ts",
"packages/harness-client/src/game-ir/index.spec.ts",
"packages/harness-client/src/game-ir-artifact.spec.ts",
"packages/harness-client/src/validation-report.spec.ts"
];
for (const filePath of harnessClientPathOnlyLaterPhaseFixtures) {
await withFixture(
{
[filePath]: "export const harmless = true;"
},
async (root) => {
const violations = await findS1ScopeViolations(root);
assertHasReason(violations, "LATER_PHASE_SCOPE_LEAK", `harness-client path-only later-phase leak ${filePath}`);
}
);
}
await withFixture(
{
"apps/api/src/modules/projects/index.ts": 'const contractName = "MiniGameCodeConversion";'
},
async (root) => {
const violations = await findS1ScopeViolations(root);
assertHasReason(violations, "LATER_PHASE_SCOPE_LEAK", "API business later-phase contract");
}
);
await withFixture(
{
"apps/api/src/modules/projects/agent.ts": "export class MainCreationAgentSessionController {}"
},
async (root) => {
const violations = await findS1ScopeViolations(root);
assertHasReason(violations, "S1_ANCHOR_SCOPE_MISUSE", "S1 anchor business misuse");
}
);
await withFixture(
{
"apps/api/prisma/migrations/20260601000000_guard/migration.sql": `
CREATE OR REPLACE FUNCTION app_require_state_transition_guard()
RETURNS trigger AS $$
BEGIN
IF current_setting('app.state_transition_guard', true) IS DISTINCT FROM 'on' THEN
RAISE EXCEPTION 'guard required';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER "LifecycleEvent_a_guard_insert" BEFORE INSERT ON "LifecycleEvent"
FOR EACH ROW EXECUTE FUNCTION app_require_state_transition_guard();
`
},
async (root) => {
const violations = await findS1ScopeViolations(root);
assertNoViolations(violations, "migration trigger/current_setting guard");
}
);
await withFixture(
{
"apps/api/prisma/migrations/20260602010101_install_audit_guard/migration.sql": `
CREATE FUNCTION app_reject_audit_log_mutation()
RETURNS trigger AS $$
BEGIN
RAISE EXCEPTION 'AuditLog is append-only' USING ERRCODE = '42501';
END;
$$ LANGUAGE plpgsql;
`
},
async (root) => {
const violations = await findS1ScopeViolations(root);
assertNoViolations(violations, "plain AuditLog append-only function install migration");
}
);
const failingFixtures = [
{
label: "migration set_config guard setter",
reason: "STATE_GUARD_SETTER_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/prisma/migrations/20260601000001_bad_guard/migration.sql":
"SELECT set_config('app.state_transition_guard', 'on', true);"
}
},
{
label: "app SET LOCAL guard setter",
reason: "STATE_GUARD_SETTER_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/bad.ts": 'await db.$executeRawUnsafe("SET LOCAL app.state_transition_guard = on");'
}
},
{
label: "app raw SQL guard setter",
reason: "STATE_GUARD_SETTER_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/raw.ts":
'await db.$executeRawUnsafe("SELECT set_config(\'app.state_transition_guard\', \'on\', true)");'
}
},
{
label: "API spec raw SQL guard setter",
reason: "STATE_GUARD_SETTER_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/bad.spec.ts":
'await db.$executeRawUnsafe("SELECT set_config(\'app.state_transition_guard\', \'on\', true)");'
}
},
{
label: "API spec SET LOCAL guard setter",
reason: "STATE_GUARD_SETTER_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/bad-set-local.spec.ts":
'await db.$executeRawUnsafe("SET LOCAL app.state_transition_guard = on");'
}
},
{
label: "audit update/delete outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit.ts":
"await db.auditLog.update({ where: { id }, data: {} }); await db.auditLog.delete({ where: { id } });"
}
},
{
label: "audit bracket model update outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-bracket-model.ts":
'await db["auditLog"].update({ where: { id }, data: {} });'
}
},
{
label: "audit computed bracket model delete outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-computed-bracket-model.ts":
'await db["audit" + "Log"].delete({ where: { id } });'
}
},
{
label: "audit bracket method delete outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-bracket-method.ts":
'await db.auditLog["delete"]({ where: { id } });'
}
},
{
label: "audit bracket model and method delete outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-bracket-model-method.ts":
'await db["auditLog"]["delete"]({ where: { id } });'
}
},
{
label: "audit optional delete outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-optional-delete.ts": "await db.auditLog?.delete({ where: { id } });"
}
},
{
label: "audit optional bracket delete outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-optional-bracket-delete.ts":
'await db["auditLog"]?.["delete"]({ where: { id } });'
}
},
{
label: "audit parenthesized delegate delete outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-parenthesized-delete.ts":
"await (db.auditLog).delete({ where: { id } });"
}
},
{
label: "audit delegate alias delete outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-alias-delete.ts":
"const logs = db.auditLog; await logs.delete({ where: { id } });"
}
},
{
label: "audit destructured delegate alias delete outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-destructured-alias-delete.ts":
"const { auditLog: logs } = db; await logs.delete({ where: { id } });"
}
},
{
label: "audit quoted destructured delegate alias delete outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-quoted-destructured-alias-delete.ts":
'const { "auditLog": logs } = db; await logs.delete({ where: { id } });'
}
},
{
label: "audit assigned delegate alias delete outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-assigned-alias-delete.ts": `
let logs;
logs = db.auditLog;
await logs.delete({ where: { id } });
`
}
},
{
label: "audit Function.prototype.call delegate delete outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-function-call-delete.ts":
"Function.prototype.call.call(db.auditLog.delete, db.auditLog, { where: { id } });"
}
},
{
label: "audit Function.prototype.call alias delegate delete outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-function-call-alias-delete.ts": `
const c = Function.prototype.call;
c.call(db.auditLog.delete, db.auditLog, { where: { id } });
`
}
},
{
label: "audit update/delete in API spec outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit.spec.ts": "await db.auditLog.update({ where: { id }, data: {} });"
}
},
{
label: "audit update/delete inside audit module implementation",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/audit/bad.ts":
"await db.auditLog.update({ where: { id }, data: {} }); await db.auditLog.delete({ where: { id } });"
}
},
{
label: "state-transition service import outside allowed path",
reason: "STATE_TRANSITION_SERVICE_IMPORT_FORBIDDEN",
files: {
"apps/api/src/modules/assets/state.ts": 'import { StateTransitionService } from "../state-transition/index.js";'
}
},
{
label: "state-transition side-effect import outside allowed path",
reason: "STATE_TRANSITION_SERVICE_IMPORT_FORBIDDEN",
files: {
"apps/api/src/modules/assets/state-side-effect.ts": 'import "../state-transition/index.js";'
}
},
{
label: "state-transition escaped side-effect import outside allowed path",
reason: "STATE_TRANSITION_SERVICE_IMPORT_FORBIDDEN",
files: {
"apps/api/src/modules/assets/state-side-effect-escaped.ts": 'import "../state-\\u0074ransition/index.js";'
}
},
{
label: "state-transition escaped named import outside allowed path",
reason: "STATE_TRANSITION_SERVICE_IMPORT_FORBIDDEN",
files: {
"apps/api/src/modules/assets/state-named-escaped.ts":
'import { StateTransitionService } from "../state-\\u0074ransition/index.js";'
}
},
{
label: "state-transition escaped export from outside allowed path",
reason: "STATE_TRANSITION_SERVICE_IMPORT_FORBIDDEN",
files: {
"apps/api/src/modules/assets/state-export-escaped.ts":
'export { StateTransitionService } from "../state-\\u0074ransition/index.js";'
}
},
{
label: "state-transition dynamic import outside allowed path",
reason: "STATE_TRANSITION_SERVICE_IMPORT_FORBIDDEN",
files: {
"apps/api/src/modules/assets/state-dynamic.ts": 'await import("../state-transition/index.js");'
}
},
{
label: "state-transition backtick dynamic import outside allowed path",
reason: "STATE_TRANSITION_SERVICE_IMPORT_FORBIDDEN",
files: {
"apps/api/src/modules/assets/state-dynamic-backtick.ts": 'await import(`../state-transition/index.js`);'
}
},
{
label: "state-transition parenthesized literal dynamic import outside allowed path",
reason: "STATE_TRANSITION_SERVICE_IMPORT_FORBIDDEN",
files: {
"apps/api/src/modules/assets/state-dynamic-parenthesized-literal.ts":
'await import(("../state-transition/index.js"));'
}
},
{
label: "state-transition parenthesized backtick dynamic import outside allowed path",
reason: "STATE_TRANSITION_SERVICE_IMPORT_FORBIDDEN",
files: {
"apps/api/src/modules/assets/state-dynamic-parenthesized-backtick.ts":
"await import((`../state-transition/index.js`));"
}
},
{
label: "state-transition concatenated direct dynamic import outside allowed path",
reason: "STATE_TRANSITION_SERVICE_IMPORT_FORBIDDEN",
files: {
"apps/api/src/modules/assets/state-dynamic-concatenated-direct.ts":
'await import("../state-transition" + "/index.js");'
}
},
{
label: "state-transition parenthesized concatenated direct dynamic import outside allowed path",
reason: "STATE_TRANSITION_SERVICE_IMPORT_FORBIDDEN",
files: {
"apps/api/src/modules/assets/state-dynamic-parenthesized-concatenated-direct.ts":
'await import(("../state-transition" + "/index.js"));'
}
},
{
label: "state-transition require outside allowed path",
reason: "STATE_TRANSITION_SERVICE_IMPORT_FORBIDDEN",
files: {
"apps/api/src/modules/assets/state-require.ts": 'require("../state-transition/index.js");'
}
},
{
label: "state-transition parenthesized literal require outside allowed path",
reason: "STATE_TRANSITION_SERVICE_IMPORT_FORBIDDEN",
files: {
"apps/api/src/modules/assets/state-require-parenthesized-literal.ts":
'require(("../state-transition/index.js"));'
}
},
{
label: "state-transition concatenated direct require outside allowed path",
reason: "STATE_TRANSITION_SERVICE_IMPORT_FORBIDDEN",
files: {
"apps/api/src/modules/assets/state-require-concatenated-direct.ts":
'require("../state-" + "transition/index.js");'
}
},
{
label: "state-transition parenthesized concatenated direct require outside allowed path",
reason: "STATE_TRANSITION_SERVICE_IMPORT_FORBIDDEN",
files: {
"apps/api/src/modules/assets/state-require-parenthesized-concatenated-direct.ts":
'require(("../state-" + "transition/index.js"));'
}
},
{
label: "state-transition variable dynamic import outside allowed path",
reason: "STATE_TRANSITION_SERVICE_IMPORT_FORBIDDEN",
files: {
"apps/api/src/modules/assets/state-dynamic-variable.ts":
'const mod = "../state-transition/index.js"; await import(mod);'
}
},
{
label: "state-transition recursive variable dynamic import outside allowed path",
reason: "STATE_TRANSITION_SERVICE_IMPORT_FORBIDDEN",
files: {
"apps/api/src/modules/assets/state-dynamic-recursive-variable.ts":
'const stateTransitionPath = "../state-transition/index.js"; const mod = stateTransitionPath; await import(mod);'
}
},
{
label: "state-transition variable require outside allowed path",
reason: "STATE_TRANSITION_SERVICE_IMPORT_FORBIDDEN",
files: {
"apps/api/src/modules/assets/state-require-variable.ts":
'const mod = "../state-transition/index.js"; require(mod);'
}
},
{
label: "state-transition concatenated variable require outside allowed path",
reason: "STATE_TRANSITION_SERVICE_IMPORT_FORBIDDEN",
files: {
"apps/api/src/modules/assets/state-require-concatenated-variable.ts":
'const base = "../state-transition"; const mod = base + "/index.js"; require(mod);'
}
},
{
label: "state-transition concatenated variable dynamic import outside allowed path",
reason: "STATE_TRANSITION_SERVICE_IMPORT_FORBIDDEN",
files: {
"apps/api/src/modules/projects/state-dynamic-concatenated-variable.ts":
'const base = "../state-"; await import(base + "transition/index.js");'
}
},
{
label: "state-transition concatenated variable direct require outside allowed path",
reason: "STATE_TRANSITION_SERVICE_IMPORT_FORBIDDEN",
files: {
"apps/api/src/modules/projects/state-require-direct-concatenated-variable.ts":
'const base = "../state-"; require(base + "transition/index.js");'
}
},
{
label: "state-transition static template expression dynamic import outside allowed path",
reason: "STATE_TRANSITION_SERVICE_IMPORT_FORBIDDEN",
files: {
"apps/api/src/modules/projects/state-dynamic-static-template.ts":
'await import(`../${"state-transition"}/index.js`);'
}
},
{
label: "state-transition static template variable dynamic import outside allowed path",
reason: "STATE_TRANSITION_SERVICE_IMPORT_FORBIDDEN",
files: {
"apps/api/src/modules/projects/state-dynamic-static-template-variable.ts":
'const seg = "state-transition"; await import(`../${seg}/index.js`);'
}
},
{
label: "state-transition destructured require alias outside allowed path",
reason: "STATE_TRANSITION_SERVICE_IMPORT_FORBIDDEN",
files: {
"apps/api/src/modules/projects/state-destructured-require-alias.ts":
'const { require: load } = module; load("../state-transition/index.js");'
}
},
{
label: "state-transition backtick require outside allowed path",
reason: "STATE_TRANSITION_SERVICE_IMPORT_FORBIDDEN",
files: {
"apps/api/src/modules/assets/state-require-backtick.ts": 'require(`../state-transition/index.js`);'
}
},
{
label: "state-transition aliased require outside allowed path",
reason: "STATE_TRANSITION_SERVICE_IMPORT_FORBIDDEN",
files: {
"apps/api/src/modules/assets/state-require-alias.ts":
'const r = require; return r("../state-transition/index.js");'
}
},
{
label: "state-transition assigned aliased require outside allowed path",
reason: "STATE_TRANSITION_SERVICE_IMPORT_FORBIDDEN",
files: {
"apps/api/src/modules/assets/state-require-assigned-alias.ts": `
let r;
r = require;
r("../state-transition/index.js");
`
}
},
{
label: "Prisma import in worker",
reason: "WORKER_DB_IMPORT_FORBIDDEN",
files: {
"apps/worker/src/index.ts": 'import { PrismaClient } from "@prisma/client";'
}
},
{
label: "worker state-transition import",
reason: "WORKER_STATE_TRANSITION_IMPORT_FORBIDDEN",
files: {
"apps/worker/src/state.ts": 'import { StateTransitionService } from "../../api/src/modules/state-transition/index.js";'
}
},
{
label: "direct Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job.ts": "await db.job.update({ where: { id }, data: { status: 'failed' } });"
}
},
{
label: "direct Job delete outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-delete.ts": "await db.job.delete({ where: { id } });"
}
},
{
label: "direct Job deleteMany outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-delete-many.ts": 'await db.job.deleteMany({ where: { status: "running" } });'
}
},
{
label: "test Job delete runId cleanup outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-cleanup.spec.ts": `
const runId = "scope-test";
await db.job.delete({ where: { id: { startsWith: runId } } });
`
}
},
{
label: "test Job deleteMany OR cleanup outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-cleanup.spec.ts": `
const runId = "scope-test";
await db.job.deleteMany({ where: { OR: [{ id: { startsWith: runId } }, { status: "running" }] } });
`
}
},
{
label: "direct Job bracket model status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-bracket-model.ts":
'await db["job"].update({ where: { id }, data: { status: "failed" } });'
}
},
{
label: "direct Job bracket method status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-bracket-method.ts":
'await db.job["update"]({ where: { id }, data: { status: "failed" } });'
}
},
{
label: "direct Job bracket model and method status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-bracket-model-method.ts":
'await db["job"]["update"]({ where: { id }, data: { status: "failed" } });'
}
},
{
label: "direct Job optional status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-optional-update.ts":
"await db.job?.update({ where: { id }, data: { status } });"
}
},
{
label: "direct Job optional bracket status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-optional-bracket-update.ts":
'await db["job"]?.["update"]({ where: { id }, data: { status } });'
}
},
{
label: "direct Job parenthesized delegate status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-parenthesized-update.ts":
'await (db.job).update({ where: { id }, data: { status: "failed" } });'
}
},
{
label: "direct Job delegate alias status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-alias-update.ts":
"const jobs = db.job; await jobs.update({ where: { id }, data: { status } });"
}
},
{
label: "direct Job delegate alias delete outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-alias-delete.ts": "const jobs = db.job; await jobs.delete({ where: { id } });"
}
},
{
label: "direct Job destructured delegate alias status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-destructured-alias-update.ts":
'const { job } = db; await job.update({ where: { id }, data: { status: "failed" } });'
}
},
{
label: "direct Job assigned delegate alias status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-assigned-alias-update.ts": `
let jobs;
jobs = db.job;
await jobs.update({ where: { id }, data: { status: "running" } });
`
}
},
{
label: "direct Job status write with comment paren outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-comment-paren.ts": `
await db.job.update({ // )
where: { id },
data: { status: "failed" }
});
`
}
},
{
label: "direct Job status write with data identifier outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-data-identifier.ts":
'const patch = { status: "failed" }; await db.job.update({ where: { id }, data: patch });'
}
},
{
label: "direct Job status write with quoted keys outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-quoted.ts":
'await db.job.update({ where: { id }, data: { "status": "failed", \'attempts\': 1, [`nextRetryAt`]: null } });'
}
},
{
label: "direct Job lease token write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-lease-token.ts":
'await db.job.update({ where: { id }, data: { leaseToken: "stolen" } });'
}
},
{
label: "direct Job timeoutAt write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-timeout-at.ts":
'await db.job.update({ where: { id }, data: { timeoutAt: new Date() } });'
}
},
{
label: "direct Job runtime write with dynamic computed key outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-computed.ts":
'const field = "status"; await db.job.update({ where: { id }, data: { [field]: "failed" } });'
}
},
{
label: "direct Job status create outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-create.ts":
'await db.job.create({ data: { projectId, actorId, status: "queued", attempts: 0, nextRetryAt: null } });'
}
},
{
label: "variableized Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-variable-data.ts":
'const data = { status: "failed" }; await db.job.update({ where: { id }, data });'
}
},
{
label: "spread Job data write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-spread-data.ts":
'const patch = { attempts: 1 }; await db.job.update({ where: { id }, data: { ...patch } });'
}
},
{
label: "raw SQL Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-status.ts":
`await db.$executeRawUnsafe("UPDATE \\"Job\\" SET \\"status\\" = 'failed' WHERE id = $1", id);`
}
},
{
label: "raw SQL Job identity-escaped keyword status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-identity-escaped-keyword-status.ts":
'await db.$executeRawUnsafe("\\U\\P\\D\\A\\T\\E \\"Job\\" SET \\"status\\" = $1 WHERE id = $2", status, id);'
}
},
{
label: "raw SQL Job unicode-escaped table status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-unicode-table.ts":
'await db.$executeRawUnsafe("UPDATE \\"\\u004aob\\" SET \\"status\\" = $1 WHERE id = $2", status, id);'
}
},
{
label: "raw SQL Job unicode quoted identifier status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-unicode-quoted-identifier-status.ts":
`await db.$executeRawUnsafe('UPDATE U&"J\\\\006Fb" SET U&"stat\\\\0075s" = $1 WHERE id = $2', status, id);`
}
},
{
label: "raw SQL Job custom UESCAPE unicode quoted identifier status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-unicode-quoted-custom-uescape-status.ts":
`await db.$executeRawUnsafe('UPDATE U&"J!006Fb" UESCAPE \\'!\\' SET U&"stat!0075s" UESCAPE \\'!\\' = $1 WHERE id = $2', status, id);`
}
},
{
label: "optional raw SQL Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-optional-status.ts":
'await db.$executeRawUnsafe?.("UPDATE \\"Job\\" SET \\"status\\" = $1 WHERE id = $2", status, id);'
}
},
{
label: "parenthesized raw SQL Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-parenthesized-status.ts":
'await (db.$executeRawUnsafe)("UPDATE \\"Job\\" SET \\"status\\" = $1 WHERE id = $2", status, id);'
}
},
{
label: "parenthesized optional bracket raw SQL Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-parenthesized-optional-bracket-status.ts":
'await (db?.["$executeRawUnsafe"])("UPDATE \\"Job\\" SET \\"status\\" = $1 WHERE id = $2", status, id);'
}
},
{
label: "bound raw SQL Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-bind-status.ts":
'await db.$executeRawUnsafe.bind(db)("UPDATE \\"Job\\" SET \\"status\\" = $1 WHERE id = $2", status, id);'
}
},
{
label: "optional bound raw SQL Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-optional-bind-status.ts":
'await db.$executeRawUnsafe?.bind(db)("UPDATE \\"Job\\" SET \\"status\\" = $1 WHERE id = $2", status, id);'
}
},
{
label: "no-semicolon bound raw SQL Job status alias outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-bound-alias-no-semicolon.ts": `
const execRaw = db.$executeRawUnsafe.bind(db)
await execRaw("UPDATE \\"Job\\" SET \\"status\\" = $1 WHERE id = $2", status, id)
`
}
},
{
label: "no-semicolon optional bound raw SQL Job status alias outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-optional-bound-alias-no-semicolon.ts": `
const execRaw = db.$executeRawUnsafe?.bind(db)
await execRaw("UPDATE \\"Job\\" SET \\"status\\" = $1 WHERE id = $2", status, id)
`
}
},
{
label: "raw SQL Job status write with comment paren outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-comment-paren.ts": `
await db.$executeRawUnsafe( // )
"UPDATE \\"Job\\" SET \\"status\\" = 'failed' WHERE id = $1",
id
);
`
}
},
{
label: "raw SQL Job attempts write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-attempts.ts":
'await db.$executeRawUnsafe("UPDATE Job SET attempts = attempts + 1 WHERE id = $1", id);'
}
},
{
label: "raw SQL Job nextRetryAt write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-next-retry.ts":
'await db.$executeRawUnsafe("UPDATE \\"Job\\" SET \\"nextRetryAt\\" = NULL WHERE id = $1", id);'
}
},
{
label: "raw SQL Job insert status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-insert-status.ts":
'await db.$executeRawUnsafe("INSERT INTO \\"Job\\" (\\"id\\", \\"status\\") VALUES ($1, \'queued\')", id);'
}
},
{
label: "raw SQL Job lease fields update outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-lease.ts":
'await db.$executeRawUnsafe("UPDATE \\"Job\\" SET \\"leaseToken\\" = $1, \\"lockVersion\\" = \\"lockVersion\\" + 1 WHERE id = $2", token, id);'
}
},
{
label: "raw SQL Job multi-column SET outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-multi-column-set.ts":
'await db.$executeRawUnsafe("UPDATE \\"Job\\" SET (\\"status\\", \\"attempts\\") = (\'failed\', 1) WHERE id = $1", id);'
}
},
{
label: "raw SQL Job MERGE update outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-merge.ts":
'await db.$executeRawUnsafe("MERGE INTO \\"Job\\" j USING \\"JobPatch\\" p ON j.id = p.id WHEN MATCHED THEN UPDATE SET \\"status\\" = \'failed\'");'
}
},
{
label: "raw SQL Job ONLY MERGE update outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-only-merge.ts":
'await db.$executeRawUnsafe("MERGE INTO ONLY \\"Job\\" j USING \\"JobPatch\\" p ON j.id = p.id WHEN MATCHED THEN UPDATE SET \\"status\\" = \'failed\'");'
}
},
{
label: "raw SQL Job ONLY block-comment MERGE update outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-only-block-comment-merge.ts":
'await db.$executeRawUnsafe("MERGE/*x*/INTO ONLY \\"Job\\" j USING \\"JobPatch\\" p ON j.id = p.id WHEN MATCHED THEN UPDATE SET \\"status\\" = \'failed\'");'
}
},
{
label: "raw SQL Job nested block-comment update outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-nested-block-comment-update.ts":
'await db.$executeRawUnsafe("UPDATE/* outer /* inner */ still outer */\\"Job\\" SET \\"status\\" = $1 WHERE id = $2", status, id);'
}
},
{
label: "raw SQL Job status update after line-comment-looking string literal",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-status-after-string-line-comment.ts":
`await db.$executeRawUnsafe("SELECT '-- not a comment'; UPDATE \\"Job\\" SET \\"status\\" = $1 WHERE id = $2", status, id);`
}
},
{
label: "raw SQL Job multi-table truncate outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-multi-truncate.ts":
'await db.$executeRawUnsafe("TRUNCATE TABLE \\"Other\\", \\"Job\\"");'
}
},
{
label: "migration SQL Job status update outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/prisma/migrations/20260602000000_bad_job_update/migration.sql":
'UPDATE "Job" SET "status" = \'failed\' WHERE id = \'x\';'
}
},
{
label: "migration SQL Job ONLY inheritance status update outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/prisma/migrations/20260602000006_bad_job_only_inheritance_update/migration.sql":
'UPDATE ONLY "Job" * SET "status" = \'failed\' WHERE id = \'x\';'
}
},
{
label: "migration SQL Job unicode quoted identifier status update outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/prisma/migrations/20260602000007_bad_job_unicode_quoted_update/migration.sql":
'UPDATE U&"J\\006Fb" SET U&"stat\\0075s" = \'failed\' WHERE id = \'x\';'
}
},
{
label: "migration SQL Job status update after line-comment-looking string literal",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/prisma/migrations/20260602000004_bad_job_update_after_string_line_comment/migration.sql":
`SELECT '-- not a comment'; UPDATE "Job" SET "status" = 'failed' WHERE id = 'x';`
}
},
{
label: "migration SQL Job MERGE update outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/prisma/migrations/20260602000001_bad_job_merge/migration.sql":
'MERGE INTO "Job" j USING "JobPatch" p ON j.id = p.id WHEN MATCHED THEN UPDATE SET "status" = \'failed\';'
}
},
{
label: "migration SQL Job ONLY MERGE update outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/prisma/migrations/20260602000005_bad_job_only_merge/migration.sql":
'MERGE INTO ONLY "Job" j USING "JobPatch" p ON j.id = p.id WHEN MATCHED THEN UPDATE SET "status" = \'failed\';'
}
},
{
label: "migration SQL Job multi-table truncate outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/prisma/migrations/20260602000003_bad_job_truncate/migration.sql":
'TRUNCATE TABLE "Other", "Job";'
}
},
{
label: "raw SQL Job timeout insert outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-timeout.ts":
'await db.$executeRawUnsafe("INSERT INTO Job (id, timeoutAt) VALUES ($1, NOW())", id);'
}
},
{
label: "raw SQL schema-qualified Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-schema-qualified.ts":
'await db.$executeRawUnsafe("UPDATE public.\\"Job\\" SET \\"status\\" = $1 WHERE id = $2", status, id);'
}
},
{
label: "raw SQL aliased Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-alias.ts":
'await db.$executeRawUnsafe("UPDATE \\"Job\\" j SET \\"status\\" = $1 WHERE j.id = $2", status, id);'
}
},
{
label: "raw SQL ONLY Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-only.ts":
'await db.$executeRawUnsafe("UPDATE ONLY \\"Job\\" SET \\"status\\" = $1 WHERE id = $2", status, id);'
}
},
{
label: "raw SQL ONLY Job inheritance status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-only-inheritance.ts":
'await db.$executeRawUnsafe("UPDATE ONLY \\"Job\\" * SET \\"status\\" = $1 WHERE id = $2", status, id);'
}
},
{
label: "Prisma.sql variable Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-prisma-sql-variable.ts":
'const query = Prisma.sql`UPDATE public."Job" SET "status" = ${status} WHERE id = ${id}`; await db.$executeRaw(query);'
}
},
{
label: "concatenated SQL variable Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-concatenated-variable.ts":
'const query = "UPDATE " + "\\"Job\\"" + " SET " + "\\"status\\"" + " = $1 WHERE id = $2"; await db.$executeRawUnsafe(query, status, id);'
}
},
{
label: "raw SQL Job status write through intermediate constants outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-intermediate-constants.ts": `
const tableName = '"Job"';
const fieldName = '"status"';
const query = "UPDATE " + tableName + " SET " + fieldName + " = $1 WHERE id = $2";
await db.$executeRawUnsafe(query, status, id);
`
}
},
{
label: "raw SQL Job status write through unicode-escaped intermediate constants outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-unicode-intermediate-constants.ts": `
const tableName = '"\\u004aob"';
const fieldName = '"status"';
const query = "UPDATE " + tableName + " SET " + fieldName + " = $1 WHERE id = $2";
await db.$executeRawUnsafe(query, status, id);
`
}
},
{
label: "raw SQL Job status write through dynamic table variable outside JobExecutionStore",
reason: "DYNAMIC_RAW_SQL_WRITE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/job-raw-dynamic-table.ts": `
const tableName = process.env.JOB_TABLE ?? '"Job"';
const fieldName = '"status"';
const query = "UPDATE " + tableName + " SET " + fieldName + " = $1 WHERE id = $2";
await db.$executeRawUnsafe(query, status, id);
`
}
},
{
label: "parenthesized raw SQL dynamic write outside JobExecutionStore",
reason: "DYNAMIC_RAW_SQL_WRITE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/job-raw-parenthesized-dynamic-table.ts": `
const table = process.env.TABLE;
const query = "UPDATE " + table + " SET status = $1";
await (db.$executeRawUnsafe)(query, status);
`
}
},
{
label: "raw SQL Job ONLY inheritance write through dynamic table variable outside JobExecutionStore",
reason: "DYNAMIC_RAW_SQL_WRITE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/job-raw-dynamic-only-inheritance-table.ts": `
const tableName = process.env.JOB_TABLE ?? '"Job"';
const query = "UPDATE ONLY " + tableName + " * j SET status = $1 WHERE id = $2";
await db.$executeRawUnsafe(query, status, id);
`
}
},
{
label: "raw SQL Job write through identity-escaped dynamic table variable outside JobExecutionStore",
reason: "DYNAMIC_RAW_SQL_WRITE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/job-raw-identity-escaped-dynamic-table.ts": `
const tableName = process.env.JOB_TABLE ?? '"Job"';
const query = "\\U\\P\\D\\A\\T\\E " + tableName + " SET status = $1 WHERE id = $2";
await db.$executeRawUnsafe(query, status, id);
`
}
},
{
label: "raw SQL Job write through dynamic table function call outside JobExecutionStore",
reason: "DYNAMIC_RAW_SQL_WRITE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/job-raw-dynamic-table-call.ts": `
const query = "UPDATE " + resolveTable() + " SET status = $1 WHERE id = $2";
await db.$executeRawUnsafe(query, status, id);
`
}
},
{
label: "raw SQL Job write through dynamic field function call outside JobExecutionStore",
reason: "DYNAMIC_RAW_SQL_WRITE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/job-raw-dynamic-field-call.ts": `
const query = "UPDATE Job SET " + resolveField() + " = $1 WHERE id = $2";
await db.$executeRawUnsafe(query, value, id);
`
}
},
{
label: "raw SQL dynamic merge table outside JobExecutionStore",
reason: "DYNAMIC_RAW_SQL_WRITE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/job-raw-dynamic-merge-table.ts": `
const tableName = process.env.JOB_TABLE ?? '"Job"';
const query = "MERGE INTO " + tableName + " j USING \\"JobPatch\\" p ON j.id = p.id WHEN MATCHED THEN UPDATE SET status = $1";
await db.$executeRawUnsafe(query, status);
`
}
},
{
label: "raw SQL dynamic merge only table outside JobExecutionStore",
reason: "DYNAMIC_RAW_SQL_WRITE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/job-raw-dynamic-merge-only-table.ts": `
const tableName = process.env.JOB_TABLE ?? '"Job"';
const query = "MERGE INTO ONLY " + tableName + " j USING \\"JobPatch\\" p ON j.id = p.id WHEN MATCHED THEN UPDATE SET status = $1";
await db.$executeRawUnsafe(query, status);
`
}
},
{
label: "Prisma.sql Job status write through Prisma.raw intermediate constants outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-prisma-raw-intermediate-constants.ts": `
const tableName = '"Job"';
const fieldName = '"status"';
const query = Prisma.sql\`UPDATE \${Prisma.raw(tableName)} SET \${Prisma.raw(fieldName)} = \${status} WHERE id = \${id}\`;
await db.$executeRaw(query);
`
}
},
{
label: "Prisma.sql Job status write through dynamic Prisma.raw table outside JobExecutionStore",
reason: "DYNAMIC_RAW_SQL_WRITE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/job-prisma-raw-dynamic-table.ts": `
const tableName = process.env.JOB_TABLE ?? '"Job"';
const fieldName = '"status"';
const query = Prisma.sql\`UPDATE \${Prisma.raw(tableName)} SET \${Prisma.raw(fieldName)} = \${status} WHERE id = \${id}\`;
await db.$executeRaw(query);
`
}
},
{
label: "raw SQL AuditLog update outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-update.ts":
'await db.$executeRawUnsafe("UPDATE \\"AuditLog\\" SET \\"action\\" = $1 WHERE id = $2", action, id);'
}
},
{
label: "raw SQL AuditLog inheritance update outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-inheritance-update.ts":
'await db.$executeRawUnsafe("UPDATE \\"AuditLog\\" * SET \\"action\\" = $1 WHERE id = $2", action, id);'
}
},
{
label: "raw SQL AuditLog delete outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-delete.ts":
'await db.$executeRawUnsafe("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);'
}
},
{
label: "raw SQL AuditLog identity-escaped keyword delete outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-identity-escaped-delete.ts":
'await db.$executeRawUnsafe("\\D\\E\\L\\E\\T\\E FROM \\"AuditLog\\" WHERE id = $1", id);'
}
},
{
label: "raw SQL AuditLog hex-escaped delete outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-hex-delete.ts":
'await db.$executeRawUnsafe("DELETE FROM \\"\\x41uditLog\\" WHERE id = $1", id);'
}
},
{
label: "raw SQL AuditLog unicode quoted identifier delete outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-unicode-quoted-identifier-delete.ts":
`await db.$executeRawUnsafe('DELETE FROM U&"Audit\\\\004Cog" WHERE id = $1', id);`
}
},
{
label: "raw SQL AuditLog custom UESCAPE unicode quoted identifier delete outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-unicode-quoted-custom-uescape-delete.ts":
`await db.$executeRawUnsafe('DELETE FROM U&"Audit!004Cog" UESCAPE \\'!\\' WHERE id = $1', id);`
}
},
{
label: "raw SQL AuditLog delete after line-comment-looking string literal",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-delete-after-string-line-comment.ts":
`await db.$executeRawUnsafe("SELECT '-- not a comment'; DELETE FROM \\"AuditLog\\" WHERE id = $1", id);`
}
},
{
label: "raw SQL AuditLog delete after block-comment-looking string literal",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-delete-after-string-block-comment.ts":
`await db.$executeRawUnsafe("SELECT '/* not a comment */'; DELETE FROM \\"AuditLog\\" WHERE id = $1", id);`
}
},
{
label: "raw SQL AuditLog upsert outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-upsert.ts":
'await db.$executeRawUnsafe("INSERT INTO \\"AuditLog\\" (\\"id\\", \\"action\\") VALUES ($1, $2) ON CONFLICT (\\"id\\") DO UPDATE SET \\"action\\" = EXCLUDED.\\"action\\"", id, action);'
}
},
{
label: "raw SQL AuditLog block-comment upsert outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-block-comment-upsert.ts":
'await db.$executeRawUnsafe("INSERT/*x*/INTO \\"AuditLog\\" (\\"id\\", \\"action\\") VALUES ($1, $2) ON/*x*/CONFLICT (\\"id\\") DO/*x*/UPDATE SET \\"action\\" = EXCLUDED.\\"action\\"", id, action);'
}
},
{
label: "raw SQL AuditLog merge outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-merge.ts":
'await db.$executeRawUnsafe("MERGE INTO \\"AuditLog\\" a USING \\"AuditLogPatch\\" p ON a.id = p.id WHEN MATCHED THEN UPDATE SET \\"action\\" = p.\\"action\\"");'
}
},
{
label: "raw SQL AuditLog block-comment merge outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-block-comment-merge.ts":
'await db.$executeRawUnsafe("MERGE/*x*/INTO \\"AuditLog\\" a USING \\"AuditLogPatch\\" p ON a.id = p.id WHEN MATCHED THEN UPDATE SET \\"action\\" = p.\\"action\\"");'
}
},
{
label: "raw SQL AuditLog block-comment delete outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-block-comment-delete.ts":
'await db.$executeRawUnsafe("DELETE/*x*/FROM \\"AuditLog\\" WHERE id = $1", id);'
}
},
{
label: "raw SQL AuditLog block-comment update outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-block-comment-update.ts":
'await db.$executeRawUnsafe("UPDATE/*x*/\\"AuditLog\\" SET \\"action\\" = $1 WHERE id = $2", action, id);'
}
},
{
label: "raw SQL AuditLog delete through dot whitespace executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-dot-whitespace-delete.ts":
'await db. $executeRawUnsafe("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);'
}
},
{
label: "raw SQL AuditLog delete through optional receiver executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-optional-receiver-delete.ts":
'await db?.$executeRawUnsafe("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);'
}
},
{
label: "raw SQL AuditLog delete through bracket executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-bracket-delete.ts":
'await db["$executeRawUnsafe"]("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);'
}
},
{
label: "raw SQL AuditLog delete through optional bracket executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-optional-bracket-delete.ts":
'await db?.["$executeRawUnsafe"]("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);'
}
},
{
label: "raw SQL AuditLog delete through computed executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-computed-delete.ts":
'await db["$execute" + "RawUnsafe"]("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);'
}
},
{
label: "raw SQL AuditLog delete through three-part computed executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-computed-three-part-delete.ts":
'await db["$execute" + "Raw" + "Unsafe"]("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);'
}
},
{
label: "raw SQL AuditLog delete through computed executor name alias",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-computed-alias-delete.ts": `
const rawName = "$executeRawUnsafe";
await db[rawName]("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);
`
}
},
{
label: "raw SQL AuditLog delete through optional computed executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-optional-computed-delete.ts":
'await db?.["$execute" + "RawUnsafe"]("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);'
}
},
{
label: "raw SQL AuditLog delete through parenthesized executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-parenthesized-delete.ts":
'await (db.$executeRawUnsafe)("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);'
}
},
{
label: "raw SQL AuditLog delete through parenthesized bracket executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-parenthesized-bracket-delete.ts":
'await (db["$executeRawUnsafe"])("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);'
}
},
{
label: "raw SQL AuditLog delete through bracket call executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-bracket-call-delete.ts":
'await db["$executeRawUnsafe"].call(db, "DELETE FROM \\"AuditLog\\" WHERE id = $1", id);'
}
},
{
label: "raw SQL AuditLog delete through optional bracket call executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-optional-bracket-call-delete.ts":
'await db?.["$executeRawUnsafe"]?.call(db, "DELETE FROM \\"AuditLog\\" WHERE id = $1", id);'
}
},
{
label: "raw SQL AuditLog delete through bracket call operation executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-bracket-call-operation-delete.ts":
'await db.$executeRawUnsafe["call"](db, \'DELETE FROM "AuditLog" WHERE id = $1\', id);'
}
},
{
label: "raw SQL AuditLog delete through computed call operation executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-computed-call-operation-delete.ts":
'await db.$executeRawUnsafe["ca" + "ll"](db, \'DELETE FROM "AuditLog" WHERE id = $1\', id);'
}
},
{
label: "raw SQL AuditLog delete through bracket bind executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-bracket-bind-delete.ts":
'await db["$executeRawUnsafe"].bind(db)("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);'
}
},
{
label: "raw SQL AuditLog delete through bracket bind operation executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-bracket-bind-operation-delete.ts":
'await db.$executeRawUnsafe["bind"](db)(\'DELETE FROM "AuditLog" WHERE id = $1\', id);'
}
},
{
label: "raw SQL AuditLog delete through computed bind operation executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-computed-bind-operation-delete.ts":
'await db.$executeRawUnsafe["bi" + "nd"](db)(\'DELETE FROM "AuditLog" WHERE id = $1\', id);'
}
},
{
label: "raw SQL AuditLog delete through apply executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-apply-delete.ts":
'await db.$executeRawUnsafe.apply(db, ["DELETE FROM \\"AuditLog\\" WHERE id = $1", id]);'
}
},
{
label: "raw SQL Job status write through bracket apply operation executor access",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-bracket-apply-operation-status.ts":
'await db.$executeRawUnsafe["apply"](db, [\'UPDATE "Job" SET "status" = $1 WHERE id = $2\', status, id]);'
}
},
{
label: "raw SQL AuditLog delete through Function.prototype.call executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-function-prototype-call-delete.ts":
'Function.prototype.call.call(db.$executeRawUnsafe, db, \'DELETE FROM "AuditLog" WHERE id = $1\', id);'
}
},
{
label: "raw SQL AuditLog delete through Function.prototype.call.apply executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-function-prototype-call-apply-delete.ts":
'Function.prototype.call.apply(db.$executeRawUnsafe, [db, \'DELETE FROM "AuditLog" WHERE id = $1\', id]);'
}
},
{
label: "raw SQL AuditLog delete through Function.prototype.call alias executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-function-prototype-call-alias-delete.ts": `
const c = Function.prototype.call;
c.call(db.$executeRawUnsafe, db, 'DELETE FROM "AuditLog" WHERE id = $1', id);
`
}
},
{
label: "raw SQL AuditLog delete through Function.prototype.call alias apply executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-function-prototype-call-alias-apply-delete.ts": `
const c = Function.prototype.call;
c.apply(db.$executeRawUnsafe, [db, 'DELETE FROM "AuditLog" WHERE id = $1', id]);
`
}
},
{
label: "raw SQL AuditLog delete through assigned Function.prototype.call alias executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-function-prototype-call-assigned-alias-delete.ts": `
let c;
c = Function.prototype.call;
c.call(db.$executeRawUnsafe, db, 'DELETE FROM "AuditLog" WHERE id = $1', id);
`
}
},
{
label: "raw SQL Job status write through Reflect.apply executor access",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-reflect-apply-status.ts":
'Reflect.apply(db.$executeRawUnsafe, db, ["UPDATE \\"Job\\" SET \\"status\\" = $1 WHERE id = $2", status, id]);'
}
},
{
label: "raw SQL AuditLog delete through Reflect.apply.call executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-reflect-apply-call-delete.ts": `
Reflect.apply.call(
Reflect,
db.$executeRawUnsafe,
db,
["DELETE FROM \\"AuditLog\\" WHERE id = $1", id]
);
`
}
},
{
label: "raw SQL Job status write through Reflect.apply.apply executor access",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-reflect-apply-apply-status.ts": `
Reflect.apply.apply(Reflect, [
db.$executeRawUnsafe,
db,
["UPDATE \\"Job\\" SET \\"status\\" = $1 WHERE id = $2", status, id]
]);
`
}
},
{
label: "raw SQL AuditLog delete through Reflect.apply Function.prototype.call executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-reflect-function-prototype-call-delete.ts":
'Reflect.apply(Function.prototype.call, db.$executeRawUnsafe, [db, \'DELETE FROM "AuditLog" WHERE id = $1\', id]);'
}
},
{
label: "raw SQL AuditLog delete through Reflect.apply alias Function.prototype.call executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-reflect-apply-alias-function-prototype-call-delete.ts": `
const ra = Reflect.apply;
ra(Function.prototype.call, db.$executeRawUnsafe, [db, 'DELETE FROM "AuditLog" WHERE id = $1', id]);
`
}
},
{
label: "raw SQL Job status write through Function.prototype.apply executor access",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-function-prototype-apply-status.ts":
'Function.prototype.apply.call(db.$executeRawUnsafe, db, [\'UPDATE "Job" SET "status" = $1 WHERE id = $2\', status, id]);'
}
},
{
label: "raw SQL Job status write through Function.prototype.apply.apply executor access",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-function-prototype-apply-apply-status.ts":
'Function.prototype.apply.apply(db.$executeRawUnsafe, [db, [\'UPDATE "Job" SET "status" = $1 WHERE id = $2\', status, id]]);'
}
},
{
label: "raw SQL Job status write through Function.prototype.apply alias executor access",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-function-prototype-apply-alias-status.ts": `
const a = Function.prototype.apply;
a.call(db.$executeRawUnsafe, db, ['UPDATE "Job" SET "status" = $1 WHERE id = $2', status, id]);
`
}
},
{
label: "raw SQL Job status write through Function.prototype.apply alias apply executor access",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-function-prototype-apply-alias-apply-status.ts": `
const a = Function.prototype.apply;
a.apply(db.$executeRawUnsafe, [db, ['UPDATE "Job" SET "status" = $1 WHERE id = $2', status, id]]);
`
}
},
{
label: "raw SQL Job status write through Reflect.apply Function.prototype.apply executor access",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-reflect-function-prototype-apply-status.ts":
'Reflect.apply(Function.prototype.apply, db.$executeRawUnsafe, [db, [\'UPDATE "Job" SET "status" = $1 WHERE id = $2\', status, id]]);'
}
},
{
label: "raw SQL Job status write through assigned Reflect.apply alias Function.prototype.apply executor access",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-reflect-apply-assigned-alias-function-prototype-apply-status.ts": `
let ra;
ra = Reflect.apply;
ra(Function.prototype.apply, db.$executeRawUnsafe, [db, ['UPDATE "Job" SET "status" = $1 WHERE id = $2', status, id]]);
`
}
},
{
label: "raw SQL AuditLog delete through Reflect.apply alias executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-reflect-apply-alias-direct-delete.ts": `
const ra = Reflect.apply;
ra(db.$executeRawUnsafe, db, ['DELETE FROM "AuditLog" WHERE id = $1', id]);
`
}
},
{
label: "raw SQL AuditLog delete through propagated Reflect.apply alias executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-reflect-apply-propagated-alias-direct-delete.ts": `
const r1 = Reflect.apply;
const r2 = r1;
r2(db.$executeRawUnsafe, db, ['DELETE FROM "AuditLog" WHERE id = $1', id]);
`
}
},
{
label: "raw SQL AuditLog delete through optional Reflect.apply alias executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-optional-reflect-apply-alias-direct-delete.ts": `
const ra = Reflect.apply;
ra?.(db.$executeRawUnsafe, db, ['DELETE FROM "AuditLog" WHERE id = $1', id]);
`
}
},
{
label: "raw SQL AuditLog delete through bound Reflect.apply alias executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-bound-reflect-apply-alias-delete.ts": `
const ra = Reflect.apply.bind(Reflect);
ra(db.$executeRawUnsafe, db, ["DELETE FROM \\"AuditLog\\" WHERE id = $1", id]);
`
}
},
{
label: "raw SQL AuditLog delete through null-bound Reflect.apply alias executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-null-bound-reflect-apply-alias-delete.ts": `
const ra = Reflect.apply.bind(null);
ra(db.$executeRawUnsafe, db, ["DELETE FROM \\"AuditLog\\" WHERE id = $1", id]);
`
}
},
{
label: "raw SQL Job status write through optional bound Reflect.apply alias executor access",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-optional-bound-reflect-apply-alias-status.ts": `
const ra = Reflect.apply.bind(Reflect);
ra?.(db.$executeRawUnsafe, db, ['UPDATE "Job" SET "status" = $1 WHERE id = $2', status, id]);
`
}
},
{
label: "raw SQL AuditLog delete through bound Function.prototype.call alias executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-bound-function-prototype-call-alias-delete.ts": `
const c = Function.prototype.call.bind(Function.prototype.call);
c(db.$executeRawUnsafe, db, "DELETE FROM \\"AuditLog\\" WHERE id = $1", id);
`
}
},
{
label: "raw SQL AuditLog delete through bracket bound Function.prototype.call alias executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-bracket-bound-function-prototype-call-alias-delete.ts": `
const c = Function.prototype["call"].bind(Function.prototype["call"]);
c(db.$executeRawUnsafe, db, 'DELETE FROM "AuditLog" WHERE id = $1', id);
`
}
},
{
label: "raw SQL AuditLog delete through optional bracket bound Function.prototype.call alias executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-optional-bracket-bound-function-prototype-call-alias-delete.ts": `
const pc = Function.prototype["call"].bind(Function.prototype["call"]);
pc?.(db.$executeRawUnsafe, db, 'DELETE FROM "AuditLog" WHERE id = $1', id);
`
}
},
{
label: "raw SQL AuditLog delete through computed bound Function.prototype.call alias executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-computed-bound-function-prototype-call-alias-delete.ts": `
const c = Function.prototype["ca" + "ll"].bind(Function.prototype["ca" + "ll"]);
c(db.$executeRawUnsafe, db, 'DELETE FROM "AuditLog" WHERE id = $1', id);
`
}
},
{
label: "raw SQL Job status write through bracket bound Function.prototype.apply alias executor access",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-bracket-bound-function-prototype-apply-alias-status.ts": `
const a = Function.prototype["apply"].bind(Function.prototype["apply"]);
a(db.$executeRawUnsafe, db, ['UPDATE "Job" SET "status" = $1 WHERE id = $2', status, id]);
`
}
},
{
label: "raw SQL AuditLog delete through Function.prototype.call bound target immediate call",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-function-prototype-call-bind-target-delete.ts": `
Function.prototype.call.bind(db.$executeRawUnsafe)(
db,
'DELETE FROM "AuditLog" WHERE id = $1',
id
);
`
}
},
{
label: "raw SQL AuditLog delete through optional Function.prototype call bind target immediate call",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-function-prototype-optional-call-bind-target-delete.ts": `
Function.prototype.call?.bind(db.$executeRawUnsafe)(
db,
'DELETE FROM "AuditLog" WHERE id = $1',
id
);
`
}
},
{
label: "raw SQL AuditLog delete through bracket Function.prototype call bind target immediate call",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-function-prototype-bracket-call-bind-target-delete.ts": `
Function.prototype["call"].bind(db.$executeRawUnsafe)(
db,
'DELETE FROM "AuditLog" WHERE id = $1',
id
);
`
}
},
{
label: "raw SQL AuditLog delete through computed Function.prototype call bind target immediate call",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-function-prototype-computed-call-bind-target-delete.ts": `
Function.prototype["ca" + "ll"].bind(db.$executeRawUnsafe)(
db,
'DELETE FROM "AuditLog" WHERE id = $1',
id
);
`
}
},
{
label: "raw SQL Job status write through Function.prototype.apply bound target immediate call",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-function-prototype-apply-bind-target-status.ts": `
Function.prototype.apply.bind(db.$executeRawUnsafe)(
db,
['UPDATE "Job" SET "status" = $1 WHERE id = $2', status, id]
);
`
}
},
{
label: "raw SQL Job status write through computed Function.prototype apply bind target immediate call",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-function-prototype-computed-apply-bind-target-status.ts": `
Function.prototype["ap" + "ply"].bind(db.$executeRawUnsafe)(
db,
['UPDATE "Job" SET "status" = $1 WHERE id = $2', status, id]
);
`
}
},
{
label: "raw SQL AuditLog delete through Function.prototype.call bound target alias",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-function-prototype-call-bind-target-alias-delete.ts": `
const exec = Function.prototype.call.bind(db.$executeRawUnsafe);
exec(db, 'DELETE FROM "AuditLog" WHERE id = $1', id);
`
}
},
{
label: "raw SQL AuditLog delete through optional Function.prototype.call bound target alias",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-optional-function-prototype-call-bind-target-alias-delete.ts": `
const exec = Function.prototype.call.bind(db.$executeRawUnsafe);
exec?.(db, 'DELETE FROM "AuditLog" WHERE id = $1', id);
`
}
},
{
label: "raw SQL AuditLog delete through assigned Function.prototype.call bound target alias",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-function-prototype-call-bind-target-assigned-alias-delete.ts": `
let exec;
exec = Function.prototype.call.bind(db.$executeRawUnsafe);
exec(db, 'DELETE FROM "AuditLog" WHERE id = $1', id);
`
}
},
{
label: "raw SQL Job status write through Reflect.apply computed executor access",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-reflect-apply-computed-status.ts":
'Reflect.apply((db["$execute" + "Raw" + "Unsafe"]), db, ["UPDATE \\"Job\\" SET \\"status\\" = $1 WHERE id = $2", status, id]);'
}
},
{
label: "raw SQL AuditLog delete through Reflect bracket apply executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-reflect-bracket-apply-delete.ts": `
Reflect["apply"](db.$executeRawUnsafe, db, [
'DELETE FROM "AuditLog" WHERE id = $1',
id
]);
`
}
},
{
label: "raw SQL AuditLog delete through Reflect computed apply executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-reflect-computed-apply-delete.ts": `
Reflect["ap" + "ply"](db.$executeRawUnsafe, db, [
'DELETE FROM "AuditLog" WHERE id = $1',
id
]);
`
}
},
{
label: "raw SQL Job status write through Reflect.apply executor name alias",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-reflect-apply-alias-status.ts": `
const rawName = "$executeRawUnsafe";
Reflect.apply(db[rawName], db, ["UPDATE \\"Job\\" SET \\"status\\" = $1 WHERE id = $2", status, id]);
`
}
},
{
label: "raw SQL AuditLog delete through Reflect.apply executor name alias",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-reflect-apply-alias-delete.ts": `
const rawName = "$executeRawUnsafe";
Reflect.apply(db[rawName], db, ["DELETE FROM \\"AuditLog\\" WHERE id = $1", id]);
`
}
},
{
label: "guarded raw SQL update through Reflect.apply executor name alias outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-raw-reflect-apply-alias-update.ts": `
const rawName = "$executeRawUnsafe";
Reflect.apply(db[rawName], db, ["UPDATE \\"GameVersion\\" SET \\"status\\" = $1 WHERE id = $2", status, id]);
`
}
},
{
label: "dynamic raw SQL write through Reflect.apply executor name alias",
reason: "DYNAMIC_RAW_SQL_WRITE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/job-raw-reflect-apply-alias-dynamic.ts": `
const rawName = "$executeRawUnsafe";
const tableName = process.env.TABLE;
const query = "UPDATE " + tableName + " SET status = $1";
Reflect.apply(db[rawName], db, [query, status]);
`
}
},
{
label: "raw SQL AuditLog delete through Reflect.apply plain executor alias",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-reflect-apply-plain-alias-delete.ts": `
const execRaw = db.$executeRawUnsafe;
Reflect.apply(execRaw, db, ['DELETE FROM "AuditLog" WHERE id = $1', id]);
`
}
},
{
label: "raw SQL Job status write through Reflect.apply bound executor alias",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-reflect-apply-bound-alias-status.ts": `
const execRaw = db.$executeRawUnsafe.bind(db);
Reflect.apply(execRaw, db, ['UPDATE "Job" SET "status" = $1 WHERE id = $2', status, id]);
`
}
},
{
label: "guarded raw SQL update through Reflect.apply assigned executor alias outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-raw-reflect-apply-assigned-alias-update.ts": `
let execRaw;
execRaw = db.$executeRawUnsafe;
Reflect.apply(execRaw, db, ['UPDATE "GameVersion" SET "status" = $1 WHERE id = $2', status, id]);
`
}
},
{
label: "dynamic raw SQL write through Reflect.apply destructured executor alias",
reason: "DYNAMIC_RAW_SQL_WRITE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/job-raw-reflect-apply-destructured-alias-dynamic.ts": `
const { $executeRawUnsafe: execRaw } = db;
const tableName = process.env.TABLE;
const query = "UPDATE " + tableName + " SET status = $1";
Reflect.apply(execRaw, db, [query, status]);
`
}
},
{
label: "raw SQL AuditLog delete through Reflect.apply computed destructured executor alias",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-reflect-apply-computed-destructured-alias-delete.ts": `
const rawName = "$executeRawUnsafe";
const { [rawName]: execRaw } = db;
Reflect.apply(execRaw, db, ['DELETE FROM "AuditLog" WHERE id = $1', id]);
`
}
},
{
label: "raw SQL AuditLog delete through assigned executor name alias",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-assigned-computed-key-delete.ts": `
let rawName;
rawName = "$executeRawUnsafe";
await db[rawName]('DELETE FROM "AuditLog" WHERE id = $1', id);
`
}
},
{
label: "raw SQL AuditLog delete through optional bracket bind executor access",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-optional-bracket-bind-delete.ts":
'await db?.["$executeRawUnsafe"]?.bind(db)("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);'
}
},
{
label: "raw SQL AuditLog delete through bracket bound alias",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-bracket-bound-alias-delete.ts": `
const execRaw = db["$executeRawUnsafe"].bind(db);
await execRaw("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);
`
}
},
{
label: "raw SQL AuditLog delete through plain alias direct call",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-plain-alias-direct-delete.ts": `
const execRaw = db.$executeRawUnsafe;
await execRaw("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);
`
}
},
{
label: "raw SQL AuditLog delete through mixed ternary bound executor alias",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-mixed-ternary-bound-alias-delete.ts": `
const execRaw = ok ? db.$executeRawUnsafe.bind(db) : safe;
await execRaw("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);
`
}
},
{
label: "raw SQL AuditLog delete through propagated plain alias direct call",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-propagated-plain-alias-direct-delete.ts": `
const execRaw = db.$executeRawUnsafe;
const runRaw = execRaw;
await runRaw('DELETE FROM "AuditLog" WHERE id = $1', id);
`
}
},
{
label: "raw SQL AuditLog delete through parenthesized plain alias direct call",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-parenthesized-plain-alias-direct-delete.ts": `
const execRaw = (db.$executeRawUnsafe);
await execRaw("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);
`
}
},
{
label: "raw SQL AuditLog delete through destructured executor alias direct call",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-destructured-alias-direct-delete.ts": `
const { $executeRawUnsafe } = db;
await $executeRawUnsafe("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);
`
}
},
{
label: "raw SQL AuditLog delete through renamed destructured executor alias direct call",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-renamed-destructured-alias-direct-delete.ts": `
const { $executeRawUnsafe: execRaw } = db;
await execRaw("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);
`
}
},
{
label: "raw SQL AuditLog delete through quoted destructured executor alias direct call",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-quoted-destructured-alias-direct-delete.ts": `
const { "$executeRawUnsafe": execRaw } = db;
await execRaw("UPDATE \\"AuditLog\\" SET action = $1 WHERE id = $2", action, id);
`
}
},
{
label: "raw SQL AuditLog delete through computed destructured executor alias direct call",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-computed-destructured-alias-direct-delete.ts": `
const { ["$executeRawUnsafe"]: execRaw } = db;
await execRaw("UPDATE \\"AuditLog\\" SET action = $1 WHERE id = $2", action, id);
`
}
},
{
label: "raw SQL AuditLog delete through post-declaration destructured executor alias",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-assigned-destructured-alias-delete.ts": `
let execRaw;
({ $executeRawUnsafe: execRaw } = db);
await execRaw("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);
`
}
},
{
label: "raw SQL AuditLog delete through assigned computed destructured executor alias",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-assigned-computed-destructured-alias-delete.ts": `
let execRaw;
({ ["$executeRawUnsafe"]: execRaw } = db);
await execRaw("UPDATE \\"AuditLog\\" SET action = $1 WHERE id = $2", action, id);
`
}
},
{
label: "raw SQL Job status write through assigned executor alias direct call",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-assigned-alias-direct-status.ts": `
let execRaw;
execRaw = db.$executeRawUnsafe;
await execRaw("UPDATE \\"Job\\" SET \\"status\\" = $1 WHERE id = $2", status, id);
`
}
},
{
label: "raw SQL Job status write through assigned logical plain executor alias",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-assigned-logical-plain-alias-status.ts": `
let runRaw;
runRaw = maybe || db.$executeRawUnsafe;
await runRaw("UPDATE \\"Job\\" SET \\"status\\" = $1 WHERE id = $2", status, id);
`
}
},
{
label: "raw SQL Job status write through post-declaration destructured executor alias",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-assigned-destructured-alias-status.ts": `
let $executeRawUnsafe;
({ $executeRawUnsafe } = db);
await $executeRawUnsafe("UPDATE \\"Job\\" SET \\"status\\" = $1 WHERE id = $2", status, id);
`
}
},
{
label: "raw SQL Job status write through assigned bound executor alias direct call",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-assigned-bound-alias-direct-status.ts": `
let execRaw;
execRaw = db.$executeRawUnsafe.bind(db);
await execRaw("UPDATE \\"Job\\" SET \\"status\\" = $1 WHERE id = $2", status, id);
`
}
},
{
label: "raw SQL Job status write through propagated bound alias direct call",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-propagated-bound-alias-direct-status.ts": `
const execRaw = db.$executeRawUnsafe.bind(db);
const runRaw = execRaw;
await runRaw('UPDATE "Job" SET "status" = $1 WHERE id = $2', status, id);
`
}
},
{
label: "optional raw SQL Job status write through plain alias direct call",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-plain-alias-optional-direct-status.ts": `
const execRaw = db.$executeRawUnsafe;
await execRaw?.("UPDATE \\"Job\\" SET \\"status\\" = $1 WHERE id = $2", status, id);
`
}
},
{
label: "raw SQL AuditLog delete through plain alias call",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-plain-alias-call-delete.ts": `
const execRaw = db.$executeRawUnsafe;
await execRaw.call(db, "DELETE FROM \\"AuditLog\\" WHERE id = $1", id);
`
}
},
{
label: "raw SQL Job status write through plain alias call",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-plain-alias-call-status.ts": `
const execRaw = db.$executeRawUnsafe;
await execRaw.call(db, "UPDATE \\"Job\\" SET \\"status\\" = $1 WHERE id = $2", status, id);
`
}
},
{
label: "optional raw SQL AuditLog delete through plain alias call",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-plain-alias-optional-call-delete.ts": `
const execRaw = db.$executeRawUnsafe;
await execRaw?.call(db, "DELETE FROM \\"AuditLog\\" WHERE id = $1", id);
`
}
},
{
label: "raw SQL AuditLog delete through plain alias bind",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-plain-alias-bind-delete.ts": `
const execRaw = db.$executeRawUnsafe;
await execRaw.bind(db)("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);
`
}
},
{
label: "raw SQL Job status write through plain alias bind",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-plain-alias-bind-status.ts": `
const execRaw = db.$executeRawUnsafe;
await execRaw.bind(db)("UPDATE \\"Job\\" SET \\"status\\" = $1 WHERE id = $2", status, id);
`
}
},
{
label: "optional raw SQL Job status write through plain alias bind",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-plain-alias-optional-bind-status.ts": `
const execRaw = db.$executeRawUnsafe;
await execRaw?.bind(db)("UPDATE \\"Job\\" SET \\"status\\" = $1 WHERE id = $2", status, id);
`
}
},
{
label: "raw SQL AuditLog delete through optional bracket plain alias call",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-optional-bracket-plain-alias-call-delete.ts": `
const execRaw = db?.["$executeRawUnsafe"];
await execRaw.call(db, "DELETE FROM \\"AuditLog\\" WHERE id = $1", id);
`
}
},
{
label: "no-semicolon bracket raw SQL AuditLog delete through bound alias",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-bracket-bound-alias-no-semicolon-delete.ts": `
const execRaw = db["$executeRawUnsafe"].bind(db)
await execRaw("DELETE FROM \\"AuditLog\\" WHERE id = $1", id)
`
}
},
{
label: "no-semicolon optional bracket raw SQL AuditLog delete through bound alias",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-optional-bracket-bound-alias-no-semicolon-delete.ts": `
const execRaw = db?.["$executeRawUnsafe"]?.bind(db)
await execRaw("DELETE FROM \\"AuditLog\\" WHERE id = $1", id)
`
}
},
{
label: "raw SQL AuditLog delete through object property wrapped executor",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-object-property-wrapper-delete.ts": `
const ops = { execRaw: db.$executeRawUnsafe };
await ops.execRaw.call(db, 'DELETE FROM "AuditLog" WHERE id = $1', id);
`
}
},
{
label: "raw SQL AuditLog delete through object wrapper destructured executor",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-object-wrapper-destructured-delete.ts": `
const ops = { execRaw: db.$executeRawUnsafe };
const { execRaw } = ops;
await execRaw("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);
`
}
},
{
label: "raw SQL AuditLog delete through object wrapper property alias",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-object-wrapper-property-alias-delete.ts": `
const ops = { execRaw: db.$executeRawUnsafe };
const runRaw = ops.execRaw;
await runRaw("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);
`
}
},
{
label: "raw SQL AuditLog delete through object shorthand wrapper executor",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-object-shorthand-wrapper-delete.ts": `
const execRaw = db.$executeRawUnsafe.bind(db);
const ops = { execRaw };
await ops.execRaw('DELETE FROM "AuditLog" WHERE id=1');
`
}
},
{
label: "raw SQL AuditLog delete through Object.assign shorthand wrapper executor",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-object-assign-shorthand-wrapper-delete.ts": `
const execRaw = db.$executeRawUnsafe.bind(db);
const box = Object.assign({}, { execRaw });
await box.execRaw('DELETE FROM "AuditLog" WHERE id = $1', id);
`
}
},
{
label: "raw SQL AuditLog delete through array wrapper executor",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-array-wrapper-delete.ts": `
const ops = [db.$executeRawUnsafe];
await ops[0]("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);
`
}
},
{
label: "raw SQL AuditLog delete through array wrapper skipped destructured executor",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-array-wrapper-skipped-destructured-delete.ts": `
const ops = [1, db.$executeRawUnsafe.bind(db)];
const [, execRaw] = ops;
await execRaw('DELETE FROM "AuditLog" WHERE id=1');
`
}
},
{
label: "raw SQL AuditLog delete through array wrapper assigned skipped destructured executor",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-array-wrapper-assigned-skipped-destructured-delete.ts": `
const ops = [1, db.$executeRawUnsafe.bind(db)];
let execRaw;
([, execRaw] = ops);
await execRaw('DELETE FROM "AuditLog" WHERE id=1');
`
}
},
{
label: "raw SQL AuditLog delete through function return wrapper executor",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-function-return-wrapper-delete.ts": `
function raw() { return db.$executeRawUnsafe; }
await raw()("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);
`
}
},
{
label: "raw SQL AuditLog delete through function returned object wrapper executor",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-function-return-object-wrapper-delete.ts": `
async function f(db) {
function getBox() {
return { raw: db.$executeRawUnsafe.bind(db) };
}
const { raw } = getBox();
await raw('DELETE FROM "AuditLog" WHERE id=1');
}
`
}
},
{
label: "raw SQL AuditLog delete through parenthesized function returned object wrapper executor",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-parenthesized-function-return-object-wrapper-delete.ts": `
function rawBox() { return ({ execRaw: db.$executeRawUnsafe }); }
rawBox().execRaw('DELETE FROM "AuditLog" WHERE id = $1', id);
`
}
},
{
label: "raw SQL AuditLog delete through function returned array wrapper executor",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-function-return-array-wrapper-delete.ts": `
function getBox() { return [db.$executeRawUnsafe.bind(db)]; }
await getBox()[0]('DELETE FROM "AuditLog" WHERE id=1');
`
}
},
{
label: "raw SQL guarded state truncate through parenthesized function returned array wrapper executor",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/review-raw-parenthesized-function-return-array-wrapper-truncate.ts": `
function rawArray() { return ([db.$executeRawUnsafe]); }
rawArray()[0]('TRUNCATE TABLE "ReviewRecord"');
`
}
},
{
label: "raw SQL Job status write through parenthesized arrow returned object wrapper executor",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-parenthesized-arrow-return-object-wrapper-status.ts": `
const rawArrowBox = () => ({ execRaw: db.$executeRawUnsafe });
rawArrowBox().execRaw('UPDATE "Job" SET "status" = $1 WHERE id = $2', status, id);
`
}
},
{
label: "raw SQL AuditLog delete through typed arrow return wrapper executor",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-typed-arrow-return-wrapper-delete.ts": `
const raw = (): typeof db.$executeRawUnsafe => db.$executeRawUnsafe;
raw()('DELETE FROM "AuditLog" WHERE id = $1', id);
`
}
},
{
label: "raw SQL Job status write through typed arrow returned object wrapper executor",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-raw-typed-arrow-return-object-wrapper-status.ts": `
const box = (): { execRaw: typeof db.$executeRawUnsafe } => ({ execRaw: db.$executeRawUnsafe });
box().execRaw('UPDATE "Job" SET "status" = $1 WHERE id = $2', status, id);
`
}
},
{
label: "raw SQL AuditLog delete through nested object wrapper destructured executor",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-nested-object-wrapper-destructured-delete.ts": `
const raw = { inner: { exec: db.$executeRawUnsafe } };
const { inner: { exec } } = raw;
await exec('DELETE FROM "AuditLog" WHERE id = $1', id);
`
}
},
{
label: "raw SQL AuditLog delete through arrow return wrapper executor",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-arrow-return-wrapper-delete.ts": `
const raw = () => db.$executeRawUnsafe;
await raw()('DELETE FROM "AuditLog" WHERE id = $1', id);
`
}
},
{
label: "raw SQL AuditLog delete through block arrow return wrapper executor",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-block-arrow-return-wrapper-delete.ts": `
const getRaw = () => { return db.$executeRawUnsafe.bind(db); };
await getRaw()('DELETE FROM "AuditLog" WHERE id = $1', id);
`
}
},
{
label: "raw SQL AuditLog delete through function expression return wrapper executor",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-function-expression-return-wrapper-delete.ts": `
const raw = function () { return db.$executeRawUnsafe; };
await raw()('DELETE FROM "AuditLog" WHERE id = $1', id);
`
}
},
{
label: "raw SQL AuditLog delete through call outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-call-delete.ts":
'await db.$executeRawUnsafe.call(db, "DELETE FROM \\"AuditLog\\" WHERE id = $1", id);'
}
},
{
label: "optional raw SQL AuditLog delete through call outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-optional-call-delete.ts":
'await db.$executeRawUnsafe?.call(db, "DELETE FROM \\"AuditLog\\" WHERE id = $1", id);'
}
},
{
label: "optional raw SQL AuditLog delete outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-optional-delete.ts":
'await db.$executeRawUnsafe?.("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);'
}
},
{
label: "bound raw SQL AuditLog delete alias outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-bound-alias-delete.ts": `
const execRaw = db.$executeRawUnsafe.bind(db);
await execRaw("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);
`
}
},
{
label: "raw SQL AuditLog multi-table truncate outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-raw-multi-truncate.ts":
'await db.$executeRawUnsafe("TRUNCATE TABLE \\"Other\\", \\"AuditLog\\"");'
}
},
{
label: "raw SQL AuditLog IF EXISTS trigger disable outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-disable-trigger-if-exists.ts":
'await db.$executeRawUnsafe("ALTER TABLE IF EXISTS \\"AuditLog\\" DISABLE TRIGGER ALL");'
}
},
{
label: "raw SQL AuditLog trigger disable outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-disable-trigger.ts":
'await db.$executeRawUnsafe("ALTER TABLE \\"AuditLog\\" DISABLE TRIGGER \\"AuditLog_reject_delete\\"");'
}
},
{
label: "raw SQL AuditLog drop trigger outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-drop-trigger.ts":
'await db.$executeRawUnsafe("DROP TRIGGER audit_append_only ON \\"AuditLog\\"");'
}
},
{
label: "raw SQL AuditLog quoted drop trigger outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-drop-trigger-quoted.ts":
'await db.$executeRawUnsafe("DROP TRIGGER \\"audit-append-only\\" ON \\"AuditLog\\";");'
}
},
{
label: "raw SQL AuditLog multi-table drop outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-drop-table-list.ts":
'await db.$executeRawUnsafe("DROP TABLE \\"Other\\", \\"AuditLog\\"");'
}
},
{
label: "raw SQL AuditLog alter table drop outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-alter-table-drop.ts":
'await db.$executeRawUnsafe("ALTER TABLE \\"AuditLog\\" DROP CONSTRAINT foo");'
}
},
{
label: "raw SQL AuditLog drop append-only function outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-drop-function.ts":
'await db.$executeRawUnsafe("DROP FUNCTION app_reject_audit_log_mutation() CASCADE");'
}
},
{
label: "raw SQL AuditLog unicode-codepoint drop append-only function outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-drop-function-unicode-codepoint.ts":
'await db.$executeRawUnsafe("DROP FUNCTION \\u{61}pp_reject_audit_log_mutation() CASCADE");'
}
},
{
label: "raw SQL AuditLog replace append-only function outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-replace-function.ts":
'await db.$executeRawUnsafe("CREATE OR REPLACE FUNCTION app_reject_audit_log_mutation() RETURNS trigger AS $$ BEGIN RETURN OLD; END; $$ LANGUAGE plpgsql");'
}
},
{
label: "raw SQL AuditLog alter append-only function outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-alter-function.ts":
'await db.$executeRawUnsafe("ALTER FUNCTION app_reject_audit_log_mutation() OWNER TO app");'
}
},
{
label: "raw SQL AuditLog alter IF EXISTS append-only function outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-alter-function-if-exists.ts":
'await db.$executeRawUnsafe("ALTER FUNCTION IF EXISTS app_reject_audit_log_mutation() OWNER TO postgres");'
}
},
{
label: "raw SQL AuditLog multi-function drop append-only function outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-drop-function-list.ts":
'await db.$executeRawUnsafe("DROP FUNCTION IF EXISTS harmless(), app_reject_audit_log_mutation() CASCADE");'
}
},
{
label: "raw SQL AuditLog drop append-only routine outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-drop-routine.ts":
'await db.$executeRawUnsafe("DROP ROUTINE IF EXISTS app_reject_audit_log_mutation() CASCADE");'
}
},
{
label: "raw SQL AuditLog alter append-only routine outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-alter-routine.ts":
'await db.$executeRawUnsafe("ALTER ROUTINE app_reject_audit_log_mutation() OWNER TO app");'
}
},
{
label: "raw SQL AuditLog alter IF EXISTS append-only routine outside trigger assertion",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-alter-routine-if-exists.ts":
'await db.$executeRawUnsafe("ALTER ROUTINE IF EXISTS app_reject_audit_log_mutation() OWNER TO postgres");'
}
},
{
label: "raw SQL dynamic drop function outside trigger assertion",
reason: "DYNAMIC_RAW_SQL_WRITE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-drop-dynamic-function.ts": `
const fn = process.env.AUDIT_FN ?? 'app_reject_audit_log_mutation()';
const query = "DROP FUNCTION " + fn + " CASCADE";
await db.$executeRawUnsafe(query);
`
}
},
{
label: "raw SQL dynamic alter function outside trigger assertion",
reason: "DYNAMIC_RAW_SQL_WRITE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-alter-dynamic-function.ts": `
const fn = process.env.AUDIT_FN ?? 'app_reject_audit_log_mutation()';
const query = "ALTER FUNCTION " + fn + " OWNER TO app";
await db.$executeRawUnsafe(query);
`
}
},
{
label: "raw SQL dynamic drop routine outside trigger assertion",
reason: "DYNAMIC_RAW_SQL_WRITE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-drop-dynamic-routine.ts": `
const fn = process.env.AUDIT_FN ?? 'app_reject_audit_log_mutation()';
const query = "DROP ROUTINE " + fn + " CASCADE";
await db.$executeRawUnsafe(query);
`
}
},
{
label: "raw SQL dynamic alter routine outside trigger assertion",
reason: "DYNAMIC_RAW_SQL_WRITE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-alter-dynamic-routine.ts": `
const fn = process.env.AUDIT_FN ?? 'app_reject_audit_log_mutation()';
const query = "ALTER ROUTINE " + fn + " OWNER TO app";
await db.$executeRawUnsafe(query);
`
}
},
{
label: "raw SQL dynamic drop function list outside trigger assertion",
reason: "DYNAMIC_RAW_SQL_WRITE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-drop-dynamic-function-list.ts": `
const fn = process.env.AUDIT_FN ?? 'app_reject_audit_log_mutation()';
const query = "DROP FUNCTION IF EXISTS harmless(), " + fn + " CASCADE";
await db.$executeRawUnsafe(query);
`
}
},
{
label: "raw SQL dynamic create function outside trigger assertion",
reason: "DYNAMIC_RAW_SQL_WRITE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-create-dynamic-function.ts": `
const fn = process.env.AUDIT_FN ?? 'app_reject_audit_log_mutation()';
const query = "CREATE FUNCTION " + fn + " RETURNS trigger AS $$ BEGIN RETURN OLD; END; $$ LANGUAGE plpgsql";
await db.$executeRawUnsafe(query);
`
}
},
{
label: "raw SQL dynamic create or replace function outside trigger assertion",
reason: "DYNAMIC_RAW_SQL_WRITE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-replace-dynamic-function.ts": `
const fn = process.env.AUDIT_FN ?? 'app_reject_audit_log_mutation()';
const query = "CREATE OR REPLACE FUNCTION " + fn + " RETURNS trigger AS $$ BEGIN RETURN OLD; END; $$ LANGUAGE plpgsql";
await db.$executeRawUnsafe(query);
`
}
},
{
label: "migration replace AuditLog append-only function",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/prisma/migrations/20260602010102_replace_audit_guard/migration.sql": `
CREATE OR REPLACE FUNCTION app_reject_audit_log_mutation()
RETURNS trigger AS $$
BEGIN
RAISE EXCEPTION 'AuditLog is append-only' USING ERRCODE = '42501';
END;
$$ LANGUAGE plpgsql;
`
}
},
{
label: "migration block-comment tampered AuditLog append-only function with RETURN NEW",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/prisma/migrations/20260602010108_block_comment_tampered_audit_guard_return/migration.sql": `
CREATE/*x*/FUNCTION app_reject_audit_log_mutation()
RETURNS trigger AS $$
BEGIN
RETURN NEW;
RAISE EXCEPTION 'AuditLog is append-only' USING ERRCODE = '42501';
END;
$$ LANGUAGE plpgsql;
`
}
},
{
label: "migration tampered AuditLog append-only function with RETURN NEW",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/prisma/migrations/20260602010105_tampered_audit_guard_return/migration.sql": `
CREATE FUNCTION app_reject_audit_log_mutation()
RETURNS trigger AS $$
BEGIN
RETURN NEW;
RAISE EXCEPTION 'AuditLog is append-only' USING ERRCODE = '42501';
END;
$$ LANGUAGE plpgsql;
`
}
},
{
label: "migration tampered AuditLog append-only function with IF branch",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/prisma/migrations/20260602010106_tampered_audit_guard_if/migration.sql": `
CREATE FUNCTION app_reject_audit_log_mutation()
RETURNS trigger AS $$
BEGIN
IF false THEN
RAISE EXCEPTION 'AuditLog is append-only' USING ERRCODE = '42501';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
`
}
},
{
label: "migration mixed canonical and bad AuditLog append-only function definitions",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/prisma/migrations/20260602010107_mixed_audit_guard/migration.sql": `
CREATE FUNCTION app_reject_audit_log_mutation()
RETURNS trigger AS $$
BEGIN
RAISE EXCEPTION 'AuditLog is append-only' USING ERRCODE = '42501';
END;
$$ LANGUAGE plpgsql;
CREATE FUNCTION app_reject_audit_log_mutation()
RETURNS trigger AS $$
BEGIN
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
`
}
},
{
label: "migration drop AuditLog append-only function",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/prisma/migrations/20260602010103_drop_audit_guard/migration.sql":
"DROP FUNCTION app_reject_audit_log_mutation() CASCADE;"
}
},
{
label: "migration drop AuditLog append-only function in list",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/prisma/migrations/20260602010120_drop_audit_guard_function_list/migration.sql":
"DROP FUNCTION IF EXISTS harmless(), app_reject_audit_log_mutation() CASCADE;"
}
},
{
label: "migration drop AuditLog append-only routine",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/prisma/migrations/20260602010121_drop_audit_guard_routine/migration.sql":
"DROP ROUTINE IF EXISTS app_reject_audit_log_mutation() CASCADE;"
}
},
{
label: "migration block-comment drop AuditLog append-only function",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/prisma/migrations/20260602010109_block_comment_drop_audit_guard/migration.sql":
"DROP/*x*/FUNCTION app_reject_audit_log_mutation() CASCADE;"
}
},
{
label: "migration nested block-comment drop AuditLog append-only function",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/prisma/migrations/20260602010116_nested_block_comment_drop_audit_guard/migration.sql":
"DROP/* outer /* inner */ still outer */FUNCTION app_reject_audit_log_mutation() CASCADE;"
}
},
{
label: "migration alter AuditLog append-only function",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/prisma/migrations/20260602010104_alter_audit_guard/migration.sql":
"ALTER FUNCTION app_reject_audit_log_mutation() OWNER TO app;"
}
},
{
label: "migration alter IF EXISTS AuditLog append-only function",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/prisma/migrations/20260602010124_alter_if_exists_audit_guard/migration.sql":
"ALTER FUNCTION IF EXISTS app_reject_audit_log_mutation() OWNER TO postgres;"
}
},
{
label: "migration alter AuditLog append-only routine",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/prisma/migrations/20260602010122_alter_audit_guard_routine/migration.sql":
"ALTER ROUTINE app_reject_audit_log_mutation() OWNER TO app;"
}
},
{
label: "migration alter IF EXISTS AuditLog append-only routine",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/prisma/migrations/20260602010125_alter_if_exists_audit_guard_routine/migration.sql":
"ALTER ROUTINE IF EXISTS app_reject_audit_log_mutation() OWNER TO postgres;"
}
},
{
label: "migration block-comment alter AuditLog append-only function",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/prisma/migrations/20260602010110_block_comment_alter_audit_guard/migration.sql":
"ALTER/*x*/FUNCTION app_reject_audit_log_mutation() OWNER TO app;"
}
},
{
label: "migration SQL AuditLog block-comment delete",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/prisma/migrations/20260602010111_block_comment_audit_delete/migration.sql":
'DELETE/*x*/FROM "AuditLog" WHERE id = \'x\';'
}
},
{
label: "migration SQL AuditLog unicode quoted identifier delete",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/prisma/migrations/20260602010119_unicode_quoted_audit_delete/migration.sql":
'DELETE FROM U&"Audit\\004Cog" WHERE id = \'x\';'
}
},
{
label: "migration SQL AuditLog custom UESCAPE unicode quoted identifier delete",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/prisma/migrations/20260602010123_unicode_quoted_custom_uescape_audit_delete/migration.sql":
'DELETE FROM U&"Audit!004Cog" UESCAPE \'!\' WHERE id = \'x\';'
}
},
{
label: "migration SQL AuditLog inheritance update",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/prisma/migrations/20260602010118_audit_inheritance_update/migration.sql":
'UPDATE "AuditLog" * SET "action" = \'changed\' WHERE id = \'x\';'
}
},
{
label: "migration SQL AuditLog nested block-comment delete",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/prisma/migrations/20260602010117_nested_block_comment_audit_delete/migration.sql":
'DELETE/* outer /* inner */ still outer */FROM "AuditLog" WHERE id = \'x\';'
}
},
{
label: "migration SQL AuditLog delete after line-comment-looking string literal",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/prisma/migrations/20260602010115_audit_delete_after_string_line_comment/migration.sql":
`SELECT '-- not a comment'; DELETE FROM "AuditLog" WHERE id = 'x';`
}
},
{
label: "migration SQL AuditLog upsert",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/prisma/migrations/20260602010112_audit_upsert/migration.sql":
'INSERT INTO "AuditLog" ("id", "action") VALUES (\'x\', \'created\') ON CONFLICT ("id") DO UPDATE SET "action" = EXCLUDED."action";'
}
},
{
label: "migration SQL AuditLog merge",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/prisma/migrations/20260602010113_audit_merge/migration.sql":
'MERGE INTO "AuditLog" a USING "AuditLogPatch" p ON a.id = p.id WHEN MATCHED THEN UPDATE SET "action" = p."action";'
}
},
{
label: "migration SQL AuditLog block-comment merge",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/prisma/migrations/20260602010114_block_comment_audit_merge/migration.sql":
'MERGE/*x*/INTO "AuditLog" a USING "AuditLogPatch" p ON a.id = p.id WHEN MATCHED THEN UPDATE SET "action" = p."action";'
}
},
{
label: "migration SQL Job block-comment merge outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/prisma/migrations/20260602010112_block_comment_job_merge/migration.sql":
'MERGE/*x*/INTO "Job" j USING "JobPatch" p ON j.id = p.id WHEN MATCHED THEN UPDATE SET "status" = \'failed\';'
}
},
{
label: "raw SQL AuditLog delete through direct generic executor",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-direct-generic-raw-delete.ts":
'await db.$executeRawUnsafe<number>("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);'
}
},
{
label: "raw SQL Job update through direct generic executor",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-direct-generic-raw-update.ts":
'await db.$executeRaw<number>("UPDATE \\"Job\\" SET \\"status\\" = $1 WHERE id = $2", status, id);'
}
},
{
label: "raw SQL Job update through bound alias generic executor",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-bound-alias-generic-raw-update.ts": `
const execRaw = db.$executeRawUnsafe.bind(db);
await execRaw<number>("UPDATE \\"Job\\" SET \\"status\\" = $1 WHERE id = $2", status, id);
`
}
},
{
label: "raw SQL AuditLog delete through plain alias generic executor",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-plain-alias-generic-raw-delete.ts": `
const execRaw = db.$executeRawUnsafe;
await execRaw<number>("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);
`
}
},
{
label: "raw SQL AuditLog delete through optional plain alias generic executor",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-optional-plain-alias-generic-raw-delete.ts": `
const execRaw = db.$executeRawUnsafe;
await execRaw?.<number>("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);
`
}
},
{
label: "raw SQL AuditLog delete through wrapped generic executor",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-wrapped-generic-raw-delete.ts": `
const box = { execRaw: db.$executeRawUnsafe.bind(db) };
await box.execRaw<number>("DELETE FROM \\"AuditLog\\" WHERE id = $1", id);
`
}
},
{
label: "raw SQL AuditLog trigger disable through intermediate table constant",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-disable-trigger-intermediate.ts": `
const tableName = '"AuditLog"';
const query = "ALTER TABLE " + tableName + " DISABLE TRIGGER ALL";
await db.$executeRawUnsafe(query);
`
}
},
{
label: "raw SQL AuditLog IF EXISTS trigger disable through dynamic table variable",
reason: "DYNAMIC_RAW_SQL_WRITE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-disable-trigger-if-exists-dynamic-table.ts": `
const tableName = process.env.AUDIT_TABLE ?? '"AuditLog"';
const query = "ALTER TABLE IF EXISTS " + tableName + " DISABLE TRIGGER ALL";
await db.$executeRawUnsafe(query);
`
}
},
{
label: "raw SQL multi-table truncate through dynamic table variable",
reason: "DYNAMIC_RAW_SQL_WRITE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-multi-truncate-dynamic-table.ts": `
const tableName = process.env.AUDIT_TABLE ?? '"AuditLog"';
const query = "TRUNCATE TABLE \\"Other\\", " + tableName;
await db.$executeRawUnsafe(query);
`
}
},
{
label: "raw SQL AuditLog trigger disable through dynamic table variable",
reason: "DYNAMIC_RAW_SQL_WRITE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-disable-trigger-dynamic-table.ts": `
const tableName = process.env.AUDIT_TABLE ?? '"AuditLog"';
const query = "ALTER TABLE " + tableName + " DISABLE TRIGGER ALL";
await db.$executeRawUnsafe(query);
`
}
},
{
label: "raw SQL drop trigger through dynamic trigger and table variables",
reason: "DYNAMIC_RAW_SQL_WRITE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-drop-trigger-dynamic.ts": `
const triggerName = process.env.TRIGGER_NAME ?? 'audit_append_only';
const tableName = process.env.AUDIT_TABLE ?? '"AuditLog"';
const query = "DROP TRIGGER " + triggerName + " ON " + tableName;
await db.$executeRawUnsafe(query);
`
}
},
{
label: "raw SQL drop table through dynamic table variable",
reason: "DYNAMIC_RAW_SQL_WRITE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-drop-table-dynamic.ts": `
const tableName = process.env.AUDIT_TABLE ?? '"AuditLog"';
const query = "DROP TABLE " + tableName;
await db.$executeRawUnsafe(query);
`
}
},
{
label: "raw SQL alter table drop through dynamic table variable",
reason: "DYNAMIC_RAW_SQL_WRITE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-alter-drop-dynamic.ts": `
const tableName = process.env.AUDIT_TABLE ?? '"AuditLog"';
const query = "ALTER TABLE " + tableName + " DROP CONSTRAINT foo";
await db.$executeRawUnsafe(query);
`
}
},
{
label: "Prisma.sql AuditLog delete through Prisma.raw intermediate table constant",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-prisma-raw-intermediate.ts": `
const tableName = '"AuditLog"';
const query = Prisma.sql\`DELETE FROM \${Prisma.raw(tableName)} WHERE id = \${id}\`;
await db.$executeRaw(query);
`
}
},
{
label: "Prisma.sql AuditLog delete through unicode-escaped Prisma.raw intermediate table constant",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-prisma-raw-unicode-intermediate.ts": `
const tableName = '"\\u0041uditLog"';
const query = Prisma.sql\`DELETE FROM \${Prisma.raw(tableName)} WHERE id = \${id}\`;
await db.$executeRaw(query);
`
}
},
{
label: "Prisma.sql AuditLog delete through dynamic Prisma.raw table",
reason: "DYNAMIC_RAW_SQL_WRITE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-prisma-raw-dynamic-table.ts": `
const tableName = process.env.AUDIT_TABLE ?? '"AuditLog"';
const query = Prisma.sql\`DELETE FROM \${Prisma.raw(tableName)} WHERE id = \${id}\`;
await db.$executeRaw(query);
`
}
},
{
label: "direct Job status write inside jobs module outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/jobs/bad.ts":
'await db.job.update({ where: { id }, data: { status: "failed", attempts: { increment: 1 }, nextRetryAt: null } });'
}
},
{
label: "direct Job status write inside jobs index outside JobExecutionStore class",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/jobs/index.ts": `
export class JobExecutionStore {
async ok(db) {
await db.job.update({ where: { id: "ok" }, data: { status: "running" } });
}
}
export class JobApiService {
async bad(db) {
await db.job.update({ where: { id: "bad" }, data: { status: "failed" } });
}
}
`
}
},
{
label: "computed Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-computed-bracket-model.ts":
'await db["j" + "o" + "b"].update({ where: { id }, data: { status: "failed" } });'
}
},
{
label: "parenthesized computed Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-parenthesized-computed-bracket-model.ts":
'await db[("j" + "o" + "b")].update({ where: { id }, data: { status: "failed" } });'
}
},
{
label: "computed Job update method write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-computed-bracket-method.ts":
'await db.job[("up" + "date")]({ where: { id }, data: { status: "failed" } });'
}
},
{
label: "Function.prototype.call Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-function-prototype-call-status.ts":
"Function.prototype.call.call(db.job.update, db.job, { where: { id }, data: { status } });"
}
},
{
label: "Function.prototype.call propagated alias Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-function-prototype-call-propagated-alias-status.ts": `
const c1 = Function.prototype.call;
const c2 = c1;
c2.call(db.job.update, db.job, { where: { id }, data: { status: "failed" } });
`
}
},
{
label: "bracket call operation audit delegate delete outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-bracket-call-operation-delete.ts":
'await db.auditLog.delete["call"](db.auditLog, { where: { id } });'
}
},
{
label: "bracket call operation guarded Prisma write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-bracket-call-operation-status.ts":
'await db["gameVersion"].update["call"](db.gameVersion, { where: { id }, data: { status: "x" } });'
}
},
{
label: "bracket apply operation Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-bracket-apply-operation-status.ts":
'await db.job.update["apply"](db.job, [{ where: { id }, data: { status: "failed" } }]);'
}
},
{
label: "computed apply operation Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-computed-apply-operation-status.ts":
'await db.job.update["app" + "ly"](db.job, [{ where: { id }, data: { status: "failed" } }]);'
}
},
{
label: "bracket bind operation Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-bracket-bind-operation-status.ts":
'await db.job.update["bind"](db.job)({ where: { id }, data: { status: "failed" } });'
}
},
{
label: "delegate propagated alias Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-delegate-propagated-alias-status.ts": `
const jobs = db.job;
const queueJobs = jobs;
await queueJobs.update({ where: { id }, data: { status: "failed" } });
`
}
},
{
label: "delegate mixed ternary alias Job delete outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-delegate-mixed-ternary-alias-delete.ts": `
const jobs = ok ? db.job : safeJobs;
await jobs.delete({ where: { id } });
`
}
},
{
label: "delegate assigned logical alias audit delete outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-delegate-assigned-logical-alias-delete.ts": `
let logs;
logs = maybe && db.auditLog;
await logs.delete({ where: { id } });
`
}
},
{
label: "delegate mixed ternary alias guarded write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-delegate-mixed-ternary-alias-status.ts": `
const versions = ok ? db.gameVersion : safeVersions;
await versions.update({ where: { id }, data: { status: "active" } });
`
}
},
{
label: "delegate object wrapper destructured Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-delegate-object-wrapper-destructured-status.ts": `
const ops = { jobs: db.job };
const { jobs } = ops;
await jobs.update({ where: { id }, data: { status: "failed" } });
`
}
},
{
label: "delegate nested object wrapper destructured guarded write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-delegate-nested-object-wrapper-destructured-status.ts": `
const box = { inner: { version: db.gameVersion } };
const { inner: { version } } = box;
await version.update({ where: { id }, data: { status: "active" } });
`
}
},
{
label: "delegate array wrapper Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-delegate-array-wrapper-status.ts": `
const ops = [db.job];
await ops[0].update({ where: { id }, data: { status: "failed" } });
`
}
},
{
label: "delegate function return wrapper audit delete outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-delegate-function-return-wrapper-delete.ts": `
function audit() { return db.auditLog; }
await audit().delete({ where: { id } });
`
}
},
{
label: "delegate function returned object wrapper audit delete outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-delegate-function-return-object-wrapper-delete.ts": `
async function f(db) {
function getBox() {
return { logs: db.auditLog };
}
const { logs } = getBox();
await logs.delete({ where: { id: "1" } });
}
`
}
},
{
label: "delegate arrow return wrapper guarded write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-delegate-arrow-return-wrapper-status.ts": `
const getVersion = () => db.gameVersion;
await getVersion().update({ where: { id }, data: { status: "active" } });
`
}
},
{
label: "delegate block arrow return wrapper guarded write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-delegate-block-arrow-return-wrapper-status.ts": `
const getVersion = () => { return db.gameVersion; };
await getVersion().update({ where: { id }, data: { status: "active" } });
`
}
},
{
label: "delegate function returned object wrapper guarded write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-delegate-function-return-object-wrapper-status.ts": `
async function f(db) {
function getBox() {
return { versions: db.gameVersion };
}
const { versions } = getBox();
await versions.update({ where: { id: "1" }, data: { status: "x" } });
}
`
}
},
{
label: "delegate parenthesized function returned object wrapper guarded write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-delegate-parenthesized-function-return-object-wrapper-status.ts": `
function delegateBox() { return ({ versions: db.gameVersion }); }
delegateBox().versions.update({ where: { id }, data: { status: 'active' } });
`
}
},
{
label: "delegate function expression return wrapper audit delete outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-delegate-function-expression-return-wrapper-delete.ts": `
const audit = function () { return db.auditLog; };
await audit().delete({ where: { id } });
`
}
},
{
label: "method object wrapper Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-method-object-wrapper-status.ts": `
const ops = { updateJob: db.job.update };
await ops.updateJob.call(db.job, { where: { id }, data: { status: "failed" } });
`
}
},
{
label: "method bound alias Job delete outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-method-bound-alias-delete.ts": `
const deleteJob = db.job.delete.bind(db.job);
await deleteJob({ where: { id } });
`
}
},
{
label: "method plain alias Job delete outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-method-plain-alias-delete.ts": `
const del = db.job.delete;
await del({ where: { id } });
`
}
},
{
label: "method assigned plain alias Job deleteMany outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-method-assigned-plain-alias-delete-many.ts": `
let delMany;
delMany = db.job.deleteMany;
await delMany({ where: { status: "running" } });
`
}
},
{
label: "method mixed ternary plain alias Job delete outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-method-mixed-ternary-plain-alias-delete.ts": `
const del = ok ? db.job.delete : safe;
await del.call(db.job, { where: { id } });
`
}
},
{
label: "method mixed ternary bound alias Job delete outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-method-mixed-ternary-bound-alias-delete.ts": `
const del = ok ? db.job.delete.bind(db.job) : safe;
await del({ where: { id } });
`
}
},
{
label: "method assigned bound alias Job delete outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-method-assigned-bound-alias-delete.ts": `
let del;
del = db.job.delete.bind(db.job);
await del({ where: { id } });
`
}
},
{
label: "method mixed assigned ternary bound alias Job delete outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-method-mixed-assigned-ternary-bound-alias-delete.ts": `
let del;
del = ok ? db.job.delete.bind(db.job) : safe;
await del({ where: { id } });
`
}
},
{
label: "method destructured alias Job delete outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-method-destructured-alias-delete.ts": `
const { delete: deleteJob } = db.job;
await deleteJob.call(db.job, { where: { id } });
`
}
},
{
label: "method Object.assign wrapper Job delete outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-method-object-assign-wrapper-delete.ts": `
const deleteJob = db.job.delete.bind(db.job);
const box = Object.assign({}, { deleteJob });
await box.deleteJob({ where: { id } });
`
}
},
{
label: "method function return wrapper Job deleteMany outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-method-function-return-wrapper-delete-many.ts": `
const getDeleteMany = () => { return db.job.deleteMany.bind(db.job); };
await getDeleteMany()({ where: { status: "running" } });
`
}
},
{
label: "method direct bound alias audit delegate delete outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-method-direct-bound-alias-delete.ts": `
const del = db.auditLog.delete.bind(db.auditLog);
await del({ where: { id } });
`
}
},
{
label: "method plain alias audit delegate delete outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-method-plain-alias-delete.ts": `
const deleteAudit = db.auditLog.delete;
await deleteAudit({ where: { id } });
`
}
},
{
label: "method assigned mixed ternary plain alias audit delegate delete outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-method-assigned-mixed-ternary-plain-alias-delete.ts": `
let del;
del = ok ? db.auditLog.delete : safe;
await del({ where: { id } });
`
}
},
{
label: "method assigned bound alias audit delegate delete outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-method-assigned-bound-alias-delete.ts": `
let del;
del = db.auditLog.delete.bind(db.auditLog);
await del({ where: { id } });
`
}
},
{
label: "method mixed ternary bound alias audit delegate delete outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-method-mixed-ternary-bound-alias-delete.ts": `
const del = ok ? db.auditLog.delete.bind(db.auditLog) : safe;
await del({ where: { id } });
`
}
},
{
label: "method destructured bound alias audit delegate delete outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-method-destructured-bound-alias-delete.ts": `
const { delete: del0 } = db.auditLog;
const del = del0.bind(db.auditLog);
await del({ where: { id } });
`
}
},
{
label: "method direct bound alias guarded write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-method-direct-bound-alias-status.ts": `
const updateVersion = db.gameVersion.update.bind(db.gameVersion);
await updateVersion({ where: { id }, data: { status: "active" } });
`
}
},
{
label: "method assigned plain alias guarded write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-method-assigned-plain-alias-status.ts": `
let updateVersion;
updateVersion = db.gameVersion.update;
await updateVersion({ where: { id }, data: { status: "active" } });
`
}
},
{
label: "method plain alias call guarded write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-method-plain-alias-call-status.ts": `
const updateVersion = db.gameVersion.update;
await updateVersion.call(db.gameVersion, { where: { id }, data: { status: "active" } });
`
}
},
{
label: "method logical plain alias guarded write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-method-logical-plain-alias-call-status.ts": `
const update = maybe || db.gameVersion.update;
await update.call(db.gameVersion, { where: { id }, data: { status: "active" } });
`
}
},
{
label: "method assigned bound alias guarded write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-method-assigned-bound-alias-status.ts": `
let update;
update = db.gameVersion.update.bind(db.gameVersion);
await update({ where: { id }, data: { status: "active" } });
`
}
},
{
label: "method mixed ternary bound alias guarded write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-method-mixed-ternary-bound-alias-status.ts": `
const update = ok ? db.gameVersion.update.bind(db.gameVersion) : safe;
await update({ where: { id }, data: { status: "active" } });
`
}
},
{
label: "method ternary bound alias guarded write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-method-ternary-bound-alias-status.ts": `
const update = ok ? db.gameVersion.update.bind(db.gameVersion) : db.gameVersion.update.bind(db.gameVersion);
await update({ where: { id }, data: { status: 'active' } });
`
}
},
{
label: "method logical OR bound alias audit delegate delete outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-method-logical-or-bound-alias-delete.ts": `
const del = maybe || db.auditLog.delete.bind(db.auditLog);
await del({ where: { id } });
`
}
},
{
label: "method logical AND bound alias audit delegate delete outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-method-logical-and-bound-alias-delete.ts": `
const del = ok && db.auditLog.delete.bind(db.auditLog);
await del({ where: { id } });
`
}
},
{
label: "method Object.assign wrapper guarded write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-method-object-assign-wrapper-status.ts": `
const update = db.gameVersion.update.bind(db.gameVersion);
const box = Object.assign({ safe: 1 }, { update });
await box.update({ where: { id }, data: { status: "active" } });
`
}
},
{
label: "method block arrow return wrapper guarded write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-method-block-arrow-return-wrapper-status.ts": `
const getUpdate = () => { return db.gameVersion.update; };
await getUpdate().call(db.gameVersion, { where: { id }, data: { status: "active" } });
`
}
},
{
label: "method object wrapper destructured guarded write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-method-object-wrapper-destructured-status.ts": `
const ops = { update: db.gameVersion.update };
const { update } = ops;
await update({ where: { id }, data: { status: "active" } });
`
}
},
{
label: "method object wrapper assigned destructured guarded write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-method-object-wrapper-assigned-destructured-status.ts": `
const ops = { update: db.gameVersion.update };
let update;
({ update } = ops);
await update.call(db.gameVersion, { where: { id }, data: { status: "active" } });
`
}
},
{
label: "method object wrapper property alias guarded call outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-method-object-wrapper-property-alias-call-status.ts": `
const ops = { update: db.gameVersion.update };
const fn = ops.update;
await fn.call(db.gameVersion, { where: { id }, data: { status: "active" } });
`
}
},
{
label: "method object wrapper property alias guarded apply outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-method-object-wrapper-property-alias-apply-status.ts": `
const ops = { update: db.gameVersion.update };
const fn = ops.update;
await fn.apply(db.gameVersion, [{ where: { id }, data: { status: "active" } }]);
`
}
},
{
label: "method object wrapper property alias guarded bind outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-method-object-wrapper-property-alias-bind-status.ts": `
const ops = { update: db.gameVersion.update };
const fn = ops.update;
await fn.bind(db.gameVersion)({ where: { id }, data: { status: "active" } });
`
}
},
{
label: "method function returned object wrapper property alias guarded call outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-method-function-return-object-wrapper-property-alias-call-status.ts": `
function getBox() { return { update: db.gameVersion.update }; }
const update = getBox().update;
await update.call(db.gameVersion, { where: { id }, data: { status: "x" } });
`
}
},
{
label: "method parenthesized function returned object wrapper guarded write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-method-parenthesized-function-return-object-wrapper-status.ts": `
function methodBox() { return ({ update: db.gameVersion.update }); }
methodBox().update({ where: { id }, data: { status: 'active' } });
`
}
},
{
label: "Function.prototype.call.apply Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-function-prototype-call-apply-status.ts":
"Function.prototype.call.apply(db.job.update, [db.job, { where: { id }, data: { status: 'failed' } }]);"
}
},
{
label: "Function.prototype.call.apply audit delegate delete outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-function-prototype-call-apply-delete.ts":
"Function.prototype.call.apply(db.auditLog.delete, [db.auditLog, { where: { id } }]);"
}
},
{
label: "Function.prototype.call bound target audit delegate delete outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-function-prototype-call-bind-target-delete.ts": `
Function.prototype.call.bind(db.auditLog.delete)(
db.auditLog,
{ where: { id } }
);
`
}
},
{
label: "Function.prototype.apply bound target Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-function-prototype-apply-bind-target-status.ts": `
Function.prototype.apply.bind(db.job.update)(
db.job,
[{ where: { id }, data: { status: "failed" } }]
);
`
}
},
{
label: "Function.prototype computed apply bound target Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-function-prototype-computed-apply-bind-target-status.ts": `
Function.prototype["ap" + "ply"].bind(db.job.update)(
db.job,
[{ where: { id }, data: { status: "failed" } }]
);
`
}
},
{
label: "bracket bound Function.prototype.call alias Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-bracket-bound-function-prototype-call-alias-status.ts": `
const pc = Function.prototype["call"].bind(Function.prototype["call"]);
pc(db.job.update, db.job, { where: { id }, data: { status: "failed" } });
`
}
},
{
label: "optional bracket bound Function.prototype.call alias Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-optional-bracket-bound-function-prototype-call-alias-status.ts": `
const pc = Function.prototype["call"].bind(Function.prototype["call"]);
pc?.(db.job.update, db.job, { where: { id }, data: { status: "failed" } });
`
}
},
{
label: "Reflect bracket apply guarded Prisma write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-reflect-bracket-apply-prisma-update.ts": `
Reflect["apply"](db.gameVersion.update, db.gameVersion, [
{ where: { id }, data: { status: "active" } }
]);
`
}
},
{
label: "null-bound Reflect.apply Job delete outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-null-bound-reflect-apply-delete.ts": `
const apply = Reflect.apply.bind(null);
apply(db.job.delete, db.job, [{ where: { id } }]);
`
}
},
{
label: "null-bound Reflect.apply audit delegate delete outside audit module",
reason: "AUDIT_UPDATE_DELETE_FORBIDDEN",
files: {
"apps/api/src/modules/projects/audit-null-bound-reflect-apply-delete.ts": `
const apply = Reflect.apply.bind(null);
apply(db.auditLog.delete, db.auditLog, [{ where: { id } }]);
`
}
},
{
label: "null-bound Reflect.apply guarded Prisma write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-null-bound-reflect-apply-prisma-update.ts": `
const apply = Reflect.apply.bind(null);
apply(db.gameVersion.update, db.gameVersion, [{ where: { id }, data: { status: "active" } }]);
`
}
},
{
label: "Reflect.apply.call Function.prototype.call Job status write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-reflect-apply-call-function-prototype-call-status.ts": `
Reflect.apply.call(Reflect, Function.prototype.call, db.job.update, [
db.job,
{ where: { id }, data: { status } }
]);
`
}
},
{
label: "computed destructured Job delegate alias write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-computed-destructured-alias.ts": `
const { ["j" + "o" + "b"]: jobs } = db;
await jobs.update({ where: { id }, data: { status: "failed" } });
`
}
},
{
label: "assigned variable computed Job delegate write outside JobExecutionStore",
reason: "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
files: {
"apps/api/src/modules/projects/job-assigned-computed-model.ts": `
let modelName;
modelName = "job";
await db[modelName].update({ where: { id }, data: { status: "failed" } });
`
}
},
{
label: "guarded Prisma write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version.ts": "await db.gameVersion.update({ where: { id }, data: { status: 'active' } });"
}
},
{
label: "guarded optional Prisma write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-optional.ts":
'await db.gameVersion?.update({ where: { id }, data: { status: "active" } });'
}
},
{
label: "guarded parenthesized Prisma write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-parenthesized.ts":
'await (db.gameVersion).update({ where: { id }, data: { status: "active" } });'
}
},
{
label: "guarded delegate alias Prisma write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-alias.ts":
'const versions = db.gameVersion; await versions.update({ where: { id }, data: { status: "active" } });'
}
},
{
label: "guarded assigned delegate alias Prisma write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-assigned-alias.ts": `
let versions;
versions = db.gameVersion;
await versions.update({ where: { id }, data: { status: "active" } });
`
}
},
{
label: "guarded assigned destructured delegate alias Prisma write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-assigned-destructured-alias.ts": `
let versions;
({ gameVersion: versions } = db);
await versions.update({ where: { id }, data: { status: "active" } });
`
}
},
{
label: "guarded assigned computed destructured delegate alias Prisma write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-assigned-computed-destructured-alias.ts": `
let versions;
({ ["game" + "Version"]: versions } = db);
await versions.update({ where: { id }, data: { status: "active" } });
`
}
},
{
label: "guarded Prisma bracket model write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-bracket-model.ts":
'await db["gameVersion"].update({ where: { id }, data: { status: "active" } });'
}
},
{
label: "guarded Prisma computed bracket model write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-computed-bracket-model.ts":
'await db["game" + "Version"].update({ where: { id }, data: { status: "active" } });'
}
},
{
label: "guarded Prisma bracket method write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/review-bracket-method.ts":
'await db.reviewRecord["update"]({ where: { id }, data: { status: "approved" } });'
}
},
{
label: "guarded Prisma bracket model and method write outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/lifecycle-bracket-model-method.ts":
'await db["lifecycleEvent"]["delete"]({ where: { eventId } });'
}
},
{
label: "guarded raw SQL simple update outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-raw-update.ts":
'await db.$executeRawUnsafe("UPDATE \\"GameVersion\\" SET \\"status\\" = $1 WHERE id = $2", status, id);'
}
},
{
label: "guarded parenthesized raw SQL update outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-raw-parenthesized-update.ts":
'await (db.$executeRawUnsafe)("UPDATE \\"GameVersion\\" SET \\"status\\" = $1 WHERE id = $2", status, id);'
}
},
{
label: "guarded raw SQL block-comment update through optional bracket outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-raw-block-comment-update.ts":
'await db?.["$executeRawUnsafe"]?.("UPDATE/*x*/\\"GameVersion\\" SET \\"status\\" = $1 WHERE id = $2", status, id);'
}
},
{
label: "guarded raw SQL UESCAPE update through call outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-raw-uescape-update.ts":
'await db.$executeRawUnsafe.call(db, "UPDATE U&\\"Game!0056ersion\\" UESCAPE \'!\' SET \\"status\\" = $1 WHERE id = $2", status, id);'
}
},
{
label: "guarded raw SQL inheritance update through bound alias outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-raw-bound-alias-update.ts": `
const execRaw = db.$executeRawUnsafe.bind(db);
await execRaw('UPDATE ONLY "GameVersion" * gv SET "status" = $1 WHERE gv.id = $2', status, id);
`
}
},
{
label: "guarded raw SQL insert through plain alias outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/review-raw-plain-alias-insert.ts": `
const execRaw = db.$executeRawUnsafe;
await execRaw('INSERT INTO "ReviewRecord" ("id") VALUES ($1)', id);
`
}
},
{
label: "guarded raw SQL delete through assigned plain alias outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/lifecycle-raw-assigned-plain-alias-delete.ts": `
let execRaw;
execRaw = db.$executeRawUnsafe;
await execRaw('DELETE FROM "LifecycleEvent" WHERE "eventId" = $1', eventId);
`
}
},
{
label: "guarded raw SQL merge through destructured alias outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/version-raw-destructured-alias-merge.ts": `
const { $executeRawUnsafe: execRaw } = db;
await execRaw('MERGE INTO "GameVersion" gv USING "GameVersionPatch" p ON gv.id = p.id WHEN MATCHED THEN UPDATE SET "status" = p."status"');
`
}
},
{
label: "guarded raw SQL truncate through assigned bound alias outside state-transition",
reason: "GUARDED_PRISMA_WRITE_OUTSIDE_STATE_TRANSITION",
files: {
"apps/api/src/modules/projects/review-raw-assigned-bound-alias-truncate.ts": `
let execRaw;
execRaw = db.$executeRawUnsafe.bind(db);
await execRaw('TRUNCATE TABLE "ReviewRecord"');
`
}
},
{
label: "later-phase creation-agent messages path",
reason: "LATER_PHASE_SCOPE_LEAK",
files: {
"apps/api/src/modules/creation-agent/messages/index.ts": "export const harmless = true;"
}
},
{
label: "later-phase creation-agent messages single-file path",
reason: "LATER_PHASE_SCOPE_LEAK",
files: {
"apps/api/src/modules/creation-agent/messages.ts": "export const harmless = true;"
}
},
{
label: "later-phase compile-game-ir module path",
reason: "LATER_PHASE_SCOPE_LEAK",
files: {
"apps/api/src/modules/compile-game-ir/index.ts": "export const harmless = true;"
}
},
{
label: "later-phase events batch route path",
reason: "LATER_PHASE_SCOPE_LEAK",
files: {
"apps/api/src/modules/events/batch/index.ts": "export const harmless = true;"
}
},
{
label: "later-phase events batch route content without leading slash",
reason: "LATER_PHASE_SCOPE_LEAK",
files: {
"apps/api/src/modules/events/index.ts": 'export const route = "events/batch";'
}
},
{
label: "later-phase events batch single-file path",
reason: "LATER_PHASE_SCOPE_LEAK",
files: {
"apps/api/src/modules/events/batch.ts": "export const harmless = true;"
}
},
{
label: "later-phase PromptRecord app implementation content",
reason: "LATER_PHASE_SCOPE_LEAK",
files: {
"apps/api/src/modules/projects/prompt-record.ts": "export interface PromptRecord { rawPrompt: string; }"
}
},
{
label: "later-phase DesignSection app implementation content",
reason: "LATER_PHASE_SCOPE_LEAK",
files: {
"apps/api/src/modules/projects/design-section.ts": "export interface DesignSection { projectId: string; payloadJson: unknown; }"
}
},
{
label: "later-phase DesignSectionPatch app implementation content",
reason: "LATER_PHASE_SCOPE_LEAK",
files: {
"apps/api/src/modules/projects/design-section-patch.ts": "export interface DesignSectionPatch { id: string; patch: unknown; }"
}
},
{
label: "later-phase AIGameDesignDraft filename path",
reason: "LATER_PHASE_SCOPE_LEAK",
files: {
"apps/api/src/modules/AIGameDesignDraft.ts": "export const harmless = true;"
}
},
{
label: "later-phase GameIR app module path",
reason: "LATER_PHASE_SCOPE_LEAK",
files: {
"apps/api/src/modules/projects/game-ir.ts": "export const harmless = true;"
}
},
{
label: "later-phase GameIR app implementation content",
reason: "LATER_PHASE_SCOPE_LEAK",
files: {
"apps/api/src/modules/projects/index.ts": "class GameIRService {}"
}
}
];
for (const fixture of failingFixtures) {
await withFixture(fixture.files, async (root) => {
const violations = await findS1ScopeViolations(root);
assertHasReason(violations, fixture.reason, fixture.label);
});
}
await withFixture(
{
"apps/api/prisma/schema.prisma": `
model MainCreationAgentSession { id String @id }
model AgentTask { id String @id }
model ReviewRecord { id String @id }
model LifecycleEvent { eventId String @id }
`
},
async (root) => {
const violations = await findS1ScopeViolations(root);
assertNoViolations(violations, "S1 anchor models");
}
);
await withFixture(
{
"apps/api/src/prisma.schema.spec.ts": `
const route = "creation-agent/messages";
await db.job.update({ where: { id }, data: { status: "failed" } });
`
},
async (root) => {
const violations = await findS1ScopeViolations(root);
assertHasReason(violations, "LATER_PHASE_SCOPE_LEAK", "historical prisma schema test later-phase leak");
assertHasReason(violations, "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE", "historical prisma schema test Job write");
}
);
await withFixture(
{
"apps/api/src/modules/state-transition/index.ts": 'import "./index.js"; export class StateTransitionService {}',
"apps/api/src/modules/state-transition/self.ts":
'await import(`../state-transition/index.js`); require(`../state-transition/index.js`); const r = require; r(`../state-transition/index.js`); let rr; rr = require; rr("../state-transition/index.js");',
"apps/api/src/modules/projects/index.ts":
'import "../state-transition/index.js"; await import("../state-transition/index.js"); require("../state-transition/index.js"); await import(`../state-transition/index.js`); require(`../state-transition/index.js`); await import("../state-transition" + "/index.js"); require("../state-" + "transition/index.js"); const mod = "../state-transition/index.js"; await import(mod); require(mod); const stateTransitionPath = "../state-transition/index.js"; const recursiveMod = stateTransitionPath; await import(recursiveMod); const base = "../state-transition"; const concatenatedMod = base + "/index.js"; require(concatenatedMod);'
},
async (root) => {
const violations = await findS1ScopeViolations(root);
assertNoViolations(violations, "allowed state-transition import paths");
}
);
await withFixture(
{
"apps/api/src/modules/state-transition/raw-guarded.ts": `
const execRaw = db.$executeRawUnsafe.bind(db);
await execRaw('UPDATE "GameVersion" SET "status" = $1 WHERE id = $2', status, id);
await db.$executeRawUnsafe('TRUNCATE TABLE "ReviewRecord"');
`
},
async (root) => {
const violations = await findS1ScopeViolations(root);
assertNoViolations(violations, "allowed state-transition raw guarded write path");
}
);
await withFixture(
{
"apps/api/src/modules/audit/audit.spec.ts": `
await expectPrismaRejectedInSavepoint(
tx,
() => tx.$executeRawUnsafe('UPDATE "AuditLog" SET "action" = $1 WHERE id = $2', action, id),
/AuditLog is append-only/
);
`
},
async (root) => {
const violations = await findS1ScopeViolations(root);
assertNoViolations(violations, "AuditLog append-only raw SQL trigger assertion");
}
);
await withFixture(
{
"apps/api/src/modules/projects/dynamic-select.ts": `
const tableName = process.env.READ_TABLE ?? '"Job"';
const query = "SELECT * FROM " + tableName + " WHERE id = $1";
await db.$queryRawUnsafe(query, id);
`
},
async (root) => {
const violations = await findS1ScopeViolations(root);
assertNoViolations(violations, "dynamic raw SQL SELECT");
}
);
await withFixture(
{
"apps/api/src/modules/projects/nonraw-wrapper.ts": [
"const nonRaw = (sql) => sql;",
"const f1 = nonRaw;",
"const f2 = f1;",
"f2('DELETE FROM \"AuditLog\" WHERE id = $1');",
"",
"const ops = { execRaw: nonRaw };",
"const { execRaw } = ops;",
'execRaw("DELETE FROM \\"AuditLog\\" WHERE id = $1");',
"",
"const ops2 = { execRaw: nonRaw };",
"ops2.execRaw.call(null, 'DELETE FROM \"AuditLog\" WHERE id = $1');",
"",
"const shorthand = nonRaw;",
"const opsShorthand = { shorthand };",
"opsShorthand.shorthand('DELETE FROM \"AuditLog\" WHERE id = $1');",
"const assignedBox = Object.assign({}, { shorthand });",
"assignedBox.shorthand('DELETE FROM \"AuditLog\" WHERE id = $1');",
"",
"const arr = [nonRaw];",
"arr[0]('UPDATE \"Job\" SET \"status\" = $1');",
"const arr2 = [1, nonRaw];",
"const [, arrExec] = arr2;",
"arrExec('DELETE FROM \"AuditLog\" WHERE id = $1');",
"let assignedArrExec;",
"([, assignedArrExec] = arr2);",
"assignedArrExec('DELETE FROM \"AuditLog\" WHERE id = $1');",
"",
"function raw() { return nonRaw; }",
"raw()('DELETE FROM \"AuditLog\" WHERE id = $1');",
"",
"function getBox() { return { raw: nonRaw, nested: { exec: nonRaw }, arr: [nonRaw] }; }",
"const { raw: boxedRaw, nested: { exec: nestedBoxedRaw } } = getBox();",
"boxedRaw('DELETE FROM \"AuditLog\" WHERE id = $1');",
"nestedBoxedRaw('DELETE FROM \"AuditLog\" WHERE id = $1');",
"const boxedAlias = getBox().raw;",
"boxedAlias('DELETE FROM \"AuditLog\" WHERE id = $1');",
"getBox().arr[0]('UPDATE \"Job\" SET \"status\" = $1');",
"",
"function getParenBox() { return ({ raw: nonRaw, arr: [nonRaw] }); }",
"getParenBox().raw('DELETE FROM \"AuditLog\" WHERE id = $1');",
"getParenBox().arr[0]('UPDATE \"Job\" SET \"status\" = $1');",
"",
"const nested = { inner: { exec: nonRaw } };",
"const { inner: { exec } } = nested;",
"exec('DELETE FROM \"AuditLog\" WHERE id = $1');",
"",
"const rawArrow = () => nonRaw;",
"rawArrow()('DELETE FROM \"AuditLog\" WHERE id = $1');",
"const blockArrow = () => { return nonRaw; };",
"blockArrow()('DELETE FROM \"AuditLog\" WHERE id = $1');",
"const logicalNonRaw = maybe || nonRaw;",
"logicalNonRaw('DELETE FROM \"AuditLog\" WHERE id = $1');",
"const ternaryNonRaw = ok ? nonRaw : nonRaw;",
"ternaryNonRaw('UPDATE \"Job\" SET \"status\" = $1');",
"const conditionalNonRaw = ok ? nonRaw : safe;",
"conditionalNonRaw('DELETE FROM \"AuditLog\" WHERE id = $1');",
"const typedRaw = (): typeof nonRaw => nonRaw;",
"typedRaw()('DELETE FROM \"AuditLog\" WHERE id = $1');",
"const typedBox = (): { raw: typeof nonRaw } => ({ raw: nonRaw });",
"typedBox().raw('UPDATE \"Job\" SET \"status\" = $1');",
"",
"const rawExpression = function () { return nonRaw; };",
"rawExpression()('DELETE FROM \"AuditLog\" WHERE id = $1');",
"",
"const ordinary = { inner: { update: nonRaw } };",
"const { inner: { update } } = ordinary;",
'update({ where: { id }, data: { status: "active" } });',
"const deleteJob = (args) => args;",
"await deleteJob({ where: { id } });",
"const deleteBox = Object.assign({}, { deleteJob });",
"await deleteBox.deleteJob({ where: { id } });",
"const getDeleteMany = () => { return deleteJob; };",
'await getDeleteMany()({ where: { status: "running" } });',
"const safeJobs = { delete: (args) => args };",
"const otherJobs = { delete: (args) => args };",
"const jobs = ok ? safeJobs : otherJobs;",
"await jobs.delete({ where: { id } });",
"const safeDelete = (args) => args;",
"const otherSafeDelete = (args) => args;",
"const safeDel = ok ? safeDelete : otherSafeDelete;",
"await safeDel.call(db.job, { where: { id } });",
"await nonRaw<number>('DELETE FROM \"AuditLog\" WHERE id = $1', id);",
'await import(`../${"safe-module"}/index.js`);',
'const seg = "safe-module";',
"await import(`../${seg}/index.js`);"
].join("\n")
},
async (root) => {
const violations = await findS1ScopeViolations(root);
assertNoViolations(violations, "non raw wrapper aliases");
}
);
await withFixture(
{
"apps/api/src/modules/projects/job-cleanup.spec.ts": `
const runId = "scope-test";
await prisma.job.deleteMany({ where: { id: { startsWith: runId } } });
await db.job.deleteMany({ where: { id: { startsWith: runId } } });
`
},
async (root) => {
const violations = await findS1ScopeViolations(root);
assertNoViolations(violations, "test-only Job runId cleanup");
}
);
await withFixture(
{
"apps/api/src/modules/projects/dynamic-select-unicode-quoted.ts": `
const tableName = process.env.READ_TABLE ?? 'U&"J\\\\006Fb"';
const query = "SELECT * FROM " + tableName + " WHERE id = $1";
await db.$queryRawUnsafe(query, id);
`
},
async (root) => {
const violations = await findS1ScopeViolations(root);
assertNoViolations(violations, "dynamic raw SQL SELECT with unicode quoted identifier");
}
);
await withFixture(
{
"apps/api/src/modules/projects/non-raw-reflect-apply.ts": `
const nonRaw = (sql) => sql;
Reflect.apply(nonRaw, null, ['DELETE FROM "AuditLog" WHERE id = $1']);
Function.prototype.call.call(nonRaw, null, 'DELETE FROM "AuditLog" WHERE id = $1');
const c = Function.prototype.call;
const ra = Reflect.apply;
c.call(nonRaw, null, 'DELETE FROM "AuditLog" WHERE id = $1', id);
ra(nonRaw, null, ['UPDATE "Job" SET "status" = $1 WHERE id = $2', status, id]);
Reflect.apply.call(Reflect, nonRaw, null, ["DELETE FROM \\"AuditLog\\" WHERE id = $1", id]);
const boundReflectApply = Reflect.apply.bind(Reflect);
boundReflectApply(nonRaw, null, ["UPDATE \\"Job\\" SET \\"status\\" = $1 WHERE id = $2", status, id]);
const nullBoundReflectApply = Reflect.apply.bind(null);
nullBoundReflectApply(nonRaw, null, ["UPDATE \\"Job\\" SET \\"status\\" = $1 WHERE id = $2", status, id]);
const boundCall = Function.prototype.call.bind(Function.prototype.call);
boundCall(nonRaw, null, "DELETE FROM \\"AuditLog\\" WHERE id = $1", id);
Function.prototype.call.apply(nonRaw, [null, 'DELETE FROM "AuditLog" WHERE id = $1', id]);
Function.prototype.apply.apply(nonRaw, [null, ['UPDATE "Job" SET "status" = $1 WHERE id = $2', status, id]]);
Function.prototype.call.bind(nonRaw)(null, 'DELETE FROM "AuditLog" WHERE id = $1');
Function.prototype.apply.bind(nonRaw)(null, ['UPDATE "Job" SET "status" = $1']);
const boundNonRaw = Function.prototype.call.bind(nonRaw);
boundNonRaw(null, 'DELETE FROM "AuditLog" WHERE id = $1');
Function.prototype.call?.bind(nonRaw)(null, 'DELETE FROM "AuditLog" WHERE id = $1');
Function.prototype["call"].bind(nonRaw)(null, 'DELETE FROM "AuditLog" WHERE id = $1');
Reflect["apply"](nonRaw, null, ['DELETE FROM "AuditLog" WHERE id = $1']);
Reflect["ap" + "ply"](nonRaw, null, ['UPDATE "Job" SET "status" = $1']);
const bracketBoundCall = Function.prototype["call"].bind(Function.prototype["call"]);
bracketBoundCall(nonRaw, null, 'DELETE FROM "AuditLog" WHERE id = $1');
const optionalBoundExec = Function.prototype.call.bind(nonRaw);
const optionalBoundReflectApply = Reflect.apply.bind(Reflect);
const optionalPlainReflectApply = Reflect.apply;
bracketBoundCall?.(nonRaw, null, 'DELETE FROM "AuditLog" WHERE id = $1');
optionalBoundExec?.(null, 'DELETE FROM "AuditLog" WHERE id = $1');
optionalBoundReflectApply?.(nonRaw, null, ['UPDATE "Job" SET "status" = $1']);
optionalPlainReflectApply?.(nonRaw, null, ['DELETE FROM "AuditLog" WHERE id = $1']);
nonRaw["call"](null, 'DELETE FROM "AuditLog" WHERE id = $1');
nonRaw["apply"](null, ['UPDATE "Job" SET "status" = $1']);
nonRaw["bind"](null)('DELETE FROM "AuditLog" WHERE id = $1');
const f1 = nonRaw;
const f2 = f1;
f2('DELETE FROM "AuditLog" WHERE id = $1');
const ops = { execRaw: nonRaw };
ops.execRaw.call(null, 'DELETE FROM "AuditLog" WHERE id = $1');
`
},
async (root) => {
const violations = await findS1ScopeViolations(root);
assertNoViolations(violations, "non-raw Reflect.apply and Function.prototype.call");
}
);
await withFixture(
{
"apps/api/prisma/migrations/20260602000002_job_schema_ddl/migration.sql": `
CREATE TABLE "Job" ("id" TEXT PRIMARY KEY, "status" TEXT NOT NULL);
CREATE INDEX "Job_status_idx" ON "Job" ("status");
`
},
async (root) => {
const violations = await findS1ScopeViolations(root);
assertNoViolations(violations, "Job schema DDL migration");
}
);
await withFixture(
{
"apps/api/src/modules/creation-agent/messages/index.ts": `
export type PromptRecord = { id: string };
export type AIGameDesignDraft = { id: string };
export type DesignSection = { id: string };
export type DesignSectionPatch = { id: string };
export type GameIR = { id: string };
export type GameIRArtifact = { id: string };
export const route = "creation-agent/messages";
export const compileRoute = "compile-game-ir";
`
},
async (root) => {
const violations = await findScopeViolations(root, "S2");
assertNoViolations(violations, "S2 approved creation workbench terms");
}
);
await withFixture(
{
"apps/api/src/modules/game-ir/index.ts": `
export type GameIR = { id: string };
export type GameIRArtifact = { id: string };
export const compiler = "compile-game-ir";
`,
"packages/shared-contracts/src/game-ir.ts": `
export type DesignSection = { id: string };
export type DesignSectionPatch = { id: string };
`
},
async (root) => {
const violations = await findScopeViolations(root, "S2");
assertNoViolations(violations, "S2 approved GameIR and shared-contracts paths");
}
);
await withFixture(
{
"apps/api/src/prisma.schema.spec.ts": `
export type PromptRecord = { id: string };
export type AIGameDesignDraft = { id: string };
export type DesignSection = { id: string };
export type DesignSectionPatch = { id: string };
`
},
async (root) => {
const violations = await findScopeViolations(root, "S2");
assertNoViolations(violations, "S2 approved Prisma schema contract test terms");
}
);
await withFixture(
{
"apps/api/src/prisma.schema.spec.ts": `
export type PromptRecord = { id: string };
await db.job.update({ where: { id }, data: { status: "failed" } });
`
},
async (root) => {
const violations = await findScopeViolations(root, "S2");
assertHasReason(
violations,
"JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
"S2 Prisma schema contract test still keeps JobExecutionStore safety scanner"
);
}
);
await withFixture(
{
"apps/api/src/modules/simulation/game-ir.ts": "export interface GameIR { id: string }"
},
async (root) => {
const violations = await findScopeViolations(root, "S2");
assertHasReason(violations, "LATER_PHASE_SCOPE_LEAK", "S2 blocks GameIR in simulation path");
}
);
await withFixture(
{
"apps/api/src/modules/simulation/design-section.ts": "export interface DesignSection { id: string }"
},
async (root) => {
const violations = await findScopeViolations(root, "S2");
assertHasReason(violations, "LATER_PHASE_SCOPE_LEAK", "S2 blocks DesignSection in simulation path");
}
);
await withFixture(
{
"apps/api/src/modules/web-runtime/game-ir.ts": "export interface GameIR { id: string }",
"packages/shared-contracts/src/conversion/game-ir.ts": "export interface GameIRArtifact { id: string }"
},
async (root) => {
const violations = await findScopeViolations(root, "S2");
assertHasReason(violations, "LATER_PHASE_SCOPE_LEAK", "S2 blocks approved terms in denied later-phase paths");
}
);
await withFixture(
{
"apps/api/src/modules/web-runtime/index.ts": "export interface MiniGameProject { id: string }",
"apps/api/src/modules/events/batch.ts": 'export const route = "/events/batch";'
},
async (root) => {
const violations = await findScopeViolations(root, "S2");
assertHasReason(violations, "LATER_PHASE_SCOPE_LEAK", "S2 still blocks S3-S8 scope leaks");
}
);
await withFixture(
{
"apps/api/src/modules/creation-agent/messages/job.ts":
'await db.job.update({ where: { id }, data: { status: "failed" } });'
},
async (root) => {
const violations = await findScopeViolations(root, "S2");
assertHasReason(
violations,
"JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE",
"S2 keeps S1 JobExecutionStore safety scanner"
);
}
);
await withFixture(
{
"apps/api/src/modules/creation-agent/session.ts": `
export class MainCreationAgentSessionService {}
export class AgentTaskRunner {}
`
},
async (root) => {
const violations = await findScopeViolations(root, "S2");
assertNoViolations(violations, "S2 approved S1 creation agent anchors");
}
);
await withFixture(
{
"apps/api/src/modules/creation-agent/review.ts": "export class ReviewRecordWorkflow {}"
},
async (root) => {
const violations = await findScopeViolations(root, "S2");
assertHasReason(violations, "S1_ANCHOR_SCOPE_MISUSE", "S2 still blocks review/lifecycle anchors");
}
);
await withFixture(
{
"apps/api/src/modules/game-config/index.ts": "export type GameConfig = { id: string };",
"apps/api/src/modules/game-logic/index.ts": "export type GameLogicModule = { id: string };",
"apps/api/src/modules/simulation/index.ts": "export type SimulationRuleSet = { id: string };"
},
async (root) => {
const s1Violations = await findScopeViolations(root, "S1");
const s2Violations = await findScopeViolations(root, "S2");
assertHasReason(s1Violations, "LATER_PHASE_SCOPE_LEAK", "S1 still blocks S3 approved terms and paths");
assertHasReason(s2Violations, "LATER_PHASE_SCOPE_LEAK", "S2 still blocks S3 approved terms and paths");
}
);
await withFixture(
{
"apps/api/src/modules/game-config/index.ts": `
export type GameConfig = { id: string };
export type RuntimeSdkContract = { version: string };
export const route = "compile-game-config";
`,
"apps/api/src/modules/game-logic/index.ts": `
export type GameLogicModule = { id: string };
export type SimulationRuleSet = { id: string };
export type NpcBehaviorGraph = { id: string };
export const route = "validate-game-logic";
`,
"apps/api/src/modules/simulation/index.ts": `
export type ValidationReport = { id: string };
export const moduleName = "game-config/game-logic/simulation";
`,
"packages/shared-contracts/src/game-config.ts": "export type GameConfig = { id: string };",
"packages/shared-contracts/src/game-logic-module.ts": "export type GameLogicModule = { id: string };",
"packages/shared-contracts/src/runtime-sdk-contract.ts": "export type RuntimeSdkContract = { version: string };"
},
async (root) => {
const violations = await findScopeViolations(root, "S3");
assertNoViolations(violations, "S3 approved simulation slice terms and paths");
}
);
await withFixture(
{
"apps/api/src/modules/web-runtime/index.ts": `
export type GamePackage = { id: string };
export type MiniGameProject = { id: string };
export type MiniGameCodeConversion = { id: string };
export type ConversionReport = { id: string };
export type FeedItem = { id: string };
export type GameDailyStats = { id: string };
export type CreatorDailyStats = { id: string };
export const routes = ["/events/batch", "publish-review-feed", "channel-readiness", "deploy-readiness"];
`
},
async (root) => {
const violations = await findScopeViolations(root, "S3");
assertHasReason(violations, "LATER_PHASE_SCOPE_LEAK", "S3 still blocks S4-S8 scope leaks");
}
);
await withFixture(
{
"apps/api/src/modules/game-config/job.ts":
'await db.job.update({ where: { id }, data: { status: "failed" } });',
"apps/api/src/modules/game-logic/audit.ts":
"await db.auditLog.delete({ where: { id } });",
"apps/api/src/modules/simulation/raw.ts": `
const tableName = process.env.JOB_TABLE ?? '"Job"';
const fieldName = '"status"';
const query = "UPDATE " + tableName + " SET " + fieldName + " = $1 WHERE id = $2";
await db.$executeRawUnsafe(query, status, id);
`
},
async (root) => {
const violations = await findScopeViolations(root, "S3");
assertHasReason(violations, "JOB_STATUS_WRITE_OUTSIDE_EXECUTION_STORE", "S3 keeps JobExecutionStore safety scanner");
assertHasReason(violations, "AUDIT_UPDATE_DELETE_FORBIDDEN", "S3 keeps AuditLog append-only scanner");
assertHasReason(violations, "DYNAMIC_RAW_SQL_WRITE_FORBIDDEN", "S3 keeps dynamic raw SQL scanner");
}
);
}
try {
const phase = parsePhaseArg(process.argv.slice(2));
await runSelfTests();
const violations = await findScopeViolations(process.cwd(), phase);
if (violations.length > 0) {
console.error(`${phase} scope check failed:`);
for (const violation of violations) {
console.error(`- ${formatViolation(violation)}`);
}
process.exitCode = 1;
} else {
console.log(`${phase} scope check passed.`);
}
} catch (error) {
console.error(error instanceof Error ? error.message : String(error));
process.exitCode = 1;
}