Replace recursive getter histories with a query-owned worklist and memo. Bound precise expansion at 512 states and widen growing receiver cycles; retain escaped roots and constructed children rather than silently dropping suffixes. Carry the resource limit through semantic coverage, worker transport and graph Evidence while preserving direct writes, imports and unrelated literal facts. The small source probe reduced 65/326 paths to one widened reference for five and six getters. The original large Claude artifact was not rerun. Validate receiver/alias/mutation controls and compiled CLI/MCP consumers. Refs #3839, #3848 Co-authored-by: Yi Han <[email protected]>
106 lines
3.5 KiB
TypeScript
106 lines
3.5 KiB
TypeScript
import { expect, it } from "vitest";
|
|
|
|
import { analyzeJavaScriptStaticSource } from "../../domain/javascript/javascriptStaticAnalysis.js";
|
|
import {
|
|
javaScriptWorkerModuleHeaderSchema,
|
|
javaScriptWorkerModuleRecordSchema,
|
|
javaScriptWorkerStaticAnalysisSchema,
|
|
} from "./JavaScriptAnalysisWorkerSchemas.js";
|
|
import { analyzeJavaScriptSemantics } from "../../domain/javascript/javascriptSemanticAnalysis.js";
|
|
import { getterExpansionSource } from "../../../tests/fixtures/javascriptGetterExpansion.js";
|
|
|
|
it("transfers getter expansion limits and unknown bindings across the worker boundary", () => {
|
|
const ir = analyzeJavaScriptSemantics(getterExpansionSource());
|
|
const header = { coverage: ir.coverage, limitations: ir.limitations };
|
|
expect(
|
|
javaScriptWorkerModuleHeaderSchema.parse(
|
|
JSON.parse(JSON.stringify(header)) as unknown,
|
|
),
|
|
).toEqual(header);
|
|
for (const value of ir.bindings) {
|
|
const record = { kind: "binding", value };
|
|
expect(
|
|
javaScriptWorkerModuleRecordSchema.parse(
|
|
JSON.parse(JSON.stringify(record)) as unknown,
|
|
),
|
|
).toEqual(record);
|
|
}
|
|
expect(ir.coverage.resourceLimits).toEqual(["getter-expansion"]);
|
|
});
|
|
|
|
it("retains candidate endpoint interpretation across the worker boundary", () => {
|
|
const facts = analyzeJavaScriptStaticSource(`
|
|
client.get("settingKey");
|
|
fetch("/api");
|
|
`);
|
|
expect(facts.endpoints).toEqual([
|
|
expect.objectContaining({
|
|
value: "settingKey",
|
|
interpretation: "candidate",
|
|
}),
|
|
expect.objectContaining({ value: "/api" }),
|
|
]);
|
|
expect(facts.endpoints[1]).not.toHaveProperty("interpretation");
|
|
const serialized = JSON.parse(JSON.stringify(facts)) as unknown;
|
|
expect(javaScriptWorkerStaticAnalysisSchema.parse(serialized)).toEqual(facts);
|
|
expect(
|
|
javaScriptWorkerStaticAnalysisSchema.safeParse({
|
|
...facts,
|
|
endpoints: facts.endpoints.map((endpoint) => ({
|
|
...endpoint,
|
|
interpretation: "confirmed",
|
|
})),
|
|
}).success,
|
|
).toBe(false);
|
|
});
|
|
|
|
it.each(["babel-ast-v1", "babel-ast-v2"])(
|
|
"versions fingerprints through the worker schema and rejects %s",
|
|
(oldAlgorithm) => {
|
|
const facts =
|
|
analyzeJavaScriptStaticSource(`globalThis.webpackChunkApp.push([[1], {
|
|
17(module, exports) { const key = selectKey(); exports.read = value[key]; }
|
|
}]);`);
|
|
expect(facts.bundler_registrations[0]?.modules[0]).toMatchObject({
|
|
structural_fingerprint_algorithm: "babel-ast-v3",
|
|
structural_fingerprint_sha256: expect.stringMatching(/^[a-f0-9]{64}$/u),
|
|
});
|
|
expect(
|
|
javaScriptWorkerStaticAnalysisSchema.safeParse(
|
|
JSON.parse(JSON.stringify(facts)) as unknown,
|
|
).success,
|
|
).toBe(true);
|
|
const old = {
|
|
...facts,
|
|
bundler_registrations: facts.bundler_registrations.map(
|
|
(registration) => ({
|
|
...registration,
|
|
modules: registration.modules.map((module) => ({
|
|
...module,
|
|
structural_fingerprint_algorithm: oldAlgorithm,
|
|
})),
|
|
}),
|
|
),
|
|
};
|
|
expect(javaScriptWorkerStaticAnalysisSchema.safeParse(old).success).toBe(
|
|
false,
|
|
);
|
|
},
|
|
);
|
|
|
|
it("accepts candidate endpoint interpretations from static analysis", () => {
|
|
const facts = analyzeJavaScriptStaticSource(`http.request("POST", target);`);
|
|
expect(facts.endpoints).toContainEqual(
|
|
expect.objectContaining({
|
|
kind: "network",
|
|
value: "POST",
|
|
interpretation: "candidate",
|
|
}),
|
|
);
|
|
expect(
|
|
javaScriptWorkerStaticAnalysisSchema.safeParse(
|
|
JSON.parse(JSON.stringify(facts)) as unknown,
|
|
).success,
|
|
).toBe(true);
|
|
});
|