fix(mongo): parse newline chained find calls

This commit is contained in:
t8y2
2026-06-24 23:21:06 +08:00
parent b89df5a43c
commit bc49bbbbfa
4 changed files with 68 additions and 25 deletions
+19 -7
View File
@@ -921,10 +921,11 @@ export function evaluateMongoAggregateSafety(command: MongoAggregateCommand, opt
}
function parseCollectionMethodTarget(source: string, method: string): { collection: string; methodCallIndex: number } | null {
const direct = new RegExp(`^db\\.([A-Za-z_$][\\w$]*)\\.${method}\\s*\\(`).exec(source);
if (direct) return { collection: direct[1], methodCallIndex: source.indexOf(`.${method}`) };
const quoted = new RegExp(`^db\\.getCollection\\(\\s*(['"])([^'"]+)\\1\\s*\\)\\.${method}\\s*\\(`).exec(source);
if (quoted) return { collection: quoted[2], methodCallIndex: source.indexOf(`.${method}`) };
const escapedMethod = escapeRegExp(method);
const direct = new RegExp(`^db\\s*\\.\\s*([A-Za-z_$][\\w$]*)\\s*\\.\\s*${escapedMethod}\\s*\\(`).exec(source);
if (direct) return { collection: direct[1], methodCallIndex: findChainedMethodCallIndex(source, method) };
const quoted = new RegExp(`^db\\s*\\.\\s*getCollection\\s*\\(\\s*(['"])([^'"]+)\\1\\s*\\)\\s*\\.\\s*${escapedMethod}\\s*\\(`).exec(source);
if (quoted) return { collection: quoted[2], methodCallIndex: findChainedMethodCallIndex(source, method) };
return null;
}
@@ -936,14 +937,25 @@ function parseMethodArgs(source: string, methodCallIndex: number): string[] | nu
}
function readChainedCallArgument(chain: string, method: string): string | undefined {
const pattern = new RegExp(`\\.${method}\\s*\\(`, "g");
const match = pattern.exec(chain);
const match = chainedMethodCallPattern(method).exec(chain);
if (!match) return undefined;
const openIndex = match.index + match[0].lastIndexOf("(");
const openIndex = chain.indexOf("(", match.index);
const closeIndex = findMatchingParen(chain, openIndex);
return closeIndex < 0 ? undefined : chain.slice(openIndex + 1, closeIndex);
}
function findChainedMethodCallIndex(source: string, method: string): number {
return chainedMethodCallPattern(method).exec(source)?.index ?? -1;
}
function chainedMethodCallPattern(method: string): RegExp {
return new RegExp(`\\.\\s*${escapeRegExp(method)}\\s*\\(`, "g");
}
function escapeRegExp(value: string): string {
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function readChainedIntegerArgument(chain: string, method: string, fallback: number): number | null {
const arg = readChainedCallArgument(chain, method);
if (arg === undefined) return fallback;