Resolve stable lexical and URL targets using shared effects, and preserve unresolved request expressions as partial facts. Carry their uncertainty through workers, artifact graphs, and the CLI. Scope partial endpoint identity to the source site. Refs: #3451, #3534 Co-authored-by: Hemang Varshney <[email protected]>
119 lines
4.0 KiB
TypeScript
119 lines
4.0 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);
|
|
});
|
|
|
|
it("preserves partial request targets through the strict worker schema", () => {
|
|
const facts = analyzeJavaScriptStaticSource("fetch(endpointUrl);");
|
|
expect(facts.endpoints).toContainEqual(
|
|
expect.objectContaining({
|
|
kind: "network",
|
|
value: "endpointUrl",
|
|
resolution: "partial",
|
|
}),
|
|
);
|
|
const serialized = JSON.parse(JSON.stringify(facts)) as unknown;
|
|
expect(javaScriptWorkerStaticAnalysisSchema.parse(serialized)).toEqual(facts);
|
|
});
|