refactor(javascript): retire synchronous artifact graph runner

This commit is contained in:
morluto committed 2026-10-24 23:33:09 +08:00
1 parent 798450638e
commit 8c6cddcffb
9 files changed
+43 -51

No files matched your search

@@ -1,7 +1,6 @@
import { ELECTRON_IDENTITY_LIMITATION } from "../../domain/javascript/javascriptElectronMemberWrites.js";
import type { ArtifactInventorySnapshot } from "../../domain/artifactInventorySnapshot.js";
import {
createJavaScriptApplicationGraph,
sealTransferredJavaScriptApplicationGraphSteps,
type JavaScriptApplicationGraph,
} from "../../domain/javascript/javascriptApplicationGraph.js";
@@ -50,18 +49,6 @@ import {
collectElectronIpcRecords,
} from "./ElectronBoundaryAnalysis.js";
/** Project artifact and AST facts into JavaScript Application Graph. */
export const buildJavaScriptArtifactGraph = (
snapshot: ArtifactInventorySnapshot,
fileSet: JavaScriptArtifactFileSet,
analysis: JavaScriptModuleArtifactAnalysis,
): JavaScriptApplicationGraph =>
createJavaScriptApplicationGraph(
completeSynchronously(
buildJavaScriptArtifactGraphInputSteps(snapshot, fileSet, analysis),
),
);
/**
* Build a validated application graph with cooperative immutable ownership.
* Constructing the graph input also runs in steps: projecting every module
@@ -81,13 +68,6 @@ export function* buildImmutableJavaScriptArtifactGraphSteps(
return yield* sealTransferredJavaScriptApplicationGraphSteps(input);
}
const completeSynchronously = <Value>(steps: Iterator<void, Value>): Value => {
for (;;) {
const step = steps.next();
if (step.done === true) return step.value;
}
};
function* buildJavaScriptArtifactGraphInputSteps(
snapshot: ArtifactInventorySnapshot,
fileSet: JavaScriptArtifactFileSet,
@@ -20,7 +20,8 @@ import { createJavaScriptArtifactReader } from "../../../../src/artifacts/javasc
import { createJavaScriptSourceAnalysis } from "../../../../src/artifacts/javascript/JavaScriptSourceAnalysis.js";
import { analyzeAndAdoptJavaScriptArtifactFiles } from "../../../../src/application/javascript/JavaScriptArtifactAnalysis.js";
import { createJavaScriptSemanticGraphProjection } from "../../../../src/application/javascript/JavaScriptSemanticGraphBuilder.js";
import { buildJavaScriptArtifactGraph } from "../../../../src/application/javascript/JavaScriptArtifactGraphBuilder.js";
import { immutableArtifactGraph } from "../../../fixtures/javascriptArtifactGraph.js";
import type { JavaScriptApplicationGraph } from "../../../../src/domain/javascript/javascriptApplicationGraph.js";
import { writeFixtureFiles } from "../../../support/javascriptApplicationFixture.js";
describe("JavaScript source selection cancellation boundary", () => {
@@ -175,7 +176,7 @@ it("resolves lazy package and JSON facts without completing their skipped observ
),
).toBe(false);
const graph = buildJavaScriptArtifactGraph(
const graph = await immutableArtifactGraph(
snapshot,
files,
outcome.analysis,
@@ -226,7 +227,7 @@ it("resolves lazy package and JSON facts without completing their skipped observ
});
const unknownScopeObservation = (
graph: ReturnType<typeof buildJavaScriptArtifactGraph>,
graph: JavaScriptApplicationGraph,
path: string,
) =>
graph.nodes
@@ -235,7 +236,7 @@ const unknownScopeObservation = (
.find(({ properties }) => properties.path === path);
const expectSkippedStructuredSource = (
graph: ReturnType<typeof buildJavaScriptArtifactGraph>,
graph: JavaScriptApplicationGraph,
path: string,
operation: string,
): void => {
@@ -8,7 +8,7 @@ import { scanCanonicalArtifactInventory } from "../../../fixtures/artifactInvent
import { readJavaScriptArtifactFiles } from "../../../support/javascriptApplicationScope.js";
import { createJavaScriptArtifactReader } from "../../../../src/artifacts/javascript/JavaScriptArtifactReader.js";
import { analyzeJavaScriptArtifactFiles } from "../../../../src/application/javascript/JavaScriptArtifactAnalysis.js";
import { buildJavaScriptArtifactGraph } from "../../../../src/application/javascript/JavaScriptArtifactGraphBuilder.js";
import { immutableArtifactGraph } from "../../../fixtures/javascriptArtifactGraph.js";
it("keeps an endpoint uncertain after mutation through a declared inherited field", async () => {
const root = await createTestTempDirectory("rea-js-erased-field-");
@@ -28,7 +28,7 @@ it("keeps an endpoint uncertain after mutation through a declared inherited fiel
try {
const files = await readJavaScriptArtifactFiles(reader, snapshot);
const analysis = analyzeJavaScriptArtifactFiles(files);
const graph = buildJavaScriptArtifactGraph(snapshot, files, analysis);
const graph = await immutableArtifactGraph(snapshot, files, analysis);
const endpoints = graph.nodes.filter(({ kind }) => kind === "endpoint");
expect(endpoints).toHaveLength(1);
expect(endpoints[0]?.observations[0]).toMatchObject({
@@ -52,7 +52,7 @@ it("keeps ambiguous endpoint candidates inferred through artifact graph projecti
try {
const files = await readJavaScriptArtifactFiles(reader, snapshot);
const analysis = analyzeJavaScriptArtifactFiles(files);
const graph = buildJavaScriptArtifactGraph(snapshot, files, analysis);
const graph = await immutableArtifactGraph(snapshot, files, analysis);
const endpoints = graph.nodes.filter(({ kind }) => kind === "endpoint");
const candidate = endpoints.find(({ observations }) =>
observations.some(
@@ -122,7 +122,7 @@ it("preserves partial and exact endpoint identity through graph projection", asy
try {
const files = await readJavaScriptArtifactFiles(reader, snapshot);
const analysis = analyzeJavaScriptArtifactFiles(files);
const graph = buildJavaScriptArtifactGraph(snapshot, files, analysis);
const graph = await immutableArtifactGraph(snapshot, files, analysis);
const endpoints = graph.nodes.filter(({ kind }) => kind === "endpoint");
const partialNodes = endpoints.filter(({ observations }) =>
observations.some(
@@ -172,7 +172,7 @@ it("projects path data records as inferred route candidates", async () => {
try {
const files = await readJavaScriptArtifactFiles(reader, snapshot);
const analysis = analyzeJavaScriptArtifactFiles(files);
const graph = buildJavaScriptArtifactGraph(snapshot, files, analysis);
const graph = await immutableArtifactGraph(snapshot, files, analysis);
const route = (value: string) =>
graph.nodes
.find(
@@ -21,7 +21,7 @@ import {
analyzeJavaScriptArtifactFiles,
analyzeAndAdoptJavaScriptArtifactFiles,
} from "../../../../src/application/javascript/JavaScriptArtifactAnalysis.js";
import { buildJavaScriptArtifactGraph } from "../../../../src/application/javascript/JavaScriptArtifactGraphBuilder.js";
import { immutableArtifactGraph } from "../../../fixtures/javascriptArtifactGraph.js";
import {
buildJavaScriptSemanticGraph,
createJavaScriptSemanticGraphProjection,
@@ -212,7 +212,7 @@ it.each([
},
],
};
const graph = buildJavaScriptArtifactGraph(
const graph = await immutableArtifactGraph(
snapshot,
partial,
analyzeJavaScriptArtifactFiles(partial),
@@ -288,7 +288,7 @@ it.each(["unselected", "unavailable"] as const)(
},
],
};
const graph = buildJavaScriptArtifactGraph(
const graph = await immutableArtifactGraph(
snapshot,
partial,
analyzeJavaScriptArtifactFiles(partial),
@@ -345,7 +345,7 @@ it("keeps imported loadFile ownership unknown across an application projection o
),
).toBe(true);
// Exercise the production omission boundary without bypassing its memory guard.
const graph = buildJavaScriptArtifactGraph(snapshot, files, {
const graph = await immutableArtifactGraph(snapshot, files, {
...analysis,
files: analysis.files.map((file) =>
file.file.path === "shared/main.cjs"
@@ -421,7 +421,7 @@ it("keeps a loadFile owner unknown when a sibling manifest is unavailable in the
},
],
};
const graph = buildJavaScriptArtifactGraph(
const graph = await immutableArtifactGraph(
snapshot,
partial,
analyzeJavaScriptArtifactFiles(partial),
@@ -491,7 +491,7 @@ it("keeps excluded non-JavaScript files out of both semantic construction paths"
"package.json",
]);
const analysis = analyzeJavaScriptArtifactFiles(files);
const applicationGraph = buildJavaScriptArtifactGraph(
const applicationGraph = await immutableArtifactGraph(
snapshot,
files,
analysis,
@@ -597,8 +597,8 @@ it("preserves graph commitments and export shapes through worker adoption", asyn
}
if (outcome.interruption !== null) throw outcome.interruption;
const compact = outcome.analysis;
const graph = buildJavaScriptArtifactGraph(snapshot, files, full);
expect(buildJavaScriptArtifactGraph(snapshot, files, compact)).toEqual(
const graph = await immutableArtifactGraph(snapshot, files, full);
expect(await immutableArtifactGraph(snapshot, files, compact)).toEqual(
graph,
);
const semantic = projection.finish(snapshot.manifest.root_sha256, graph);
@@ -672,7 +672,7 @@ it("preserves graph commitments and export shapes through worker adoption", asyn
rejectedInvalidTransfer = true;
}
}
const decodedGraph = buildJavaScriptArtifactGraph(snapshot, files, {
const decodedGraph = await immutableArtifactGraph(snapshot, files, {
...compact,
files: compact.files.map((analyzed) => {
const module = decodedModules.get(analyzed.file.path);
@@ -754,7 +754,7 @@ it("reports semantic value resource limits in application graph coverage", async
try {
const files = await readJavaScriptArtifactFiles(reader, snapshot);
const analysis = analyzeJavaScriptArtifactFiles(files);
const graph = buildJavaScriptArtifactGraph(snapshot, files, analysis);
const graph = await immutableArtifactGraph(snapshot, files, analysis);
expect(graph.coverage).toMatchObject({
status: "partial",
@@ -14,7 +14,7 @@ import { scanCanonicalArtifactInventory } from "../../../fixtures/artifactInvent
import { readJavaScriptArtifactFiles } from "../../../support/javascriptApplicationScope.js";
import { createJavaScriptArtifactReader } from "../../../../src/artifacts/javascript/JavaScriptArtifactReader.js";
import { analyzeJavaScriptArtifactFiles } from "../../../../src/application/javascript/JavaScriptArtifactAnalysis.js";
import { buildJavaScriptArtifactGraph } from "../../../../src/application/javascript/JavaScriptArtifactGraphBuilder.js";
import { immutableArtifactGraph } from "../../../fixtures/javascriptArtifactGraph.js";
import {
findExportNode,
@@ -29,7 +29,7 @@ const relationshipGraph = async (root: string) => {
const reader = createJavaScriptArtifactReader(root, "directory");
try {
const files = await readJavaScriptArtifactFiles(reader, snapshot);
return buildJavaScriptArtifactGraph(
return await immutableArtifactGraph(
snapshot,
files,
analyzeJavaScriptArtifactFiles(files),
@@ -4,7 +4,7 @@ import { join } from "node:path";
import { expect, it } from "vitest";
import { analyzeJavaScriptArtifactFiles } from "../../../../src/application/javascript/JavaScriptArtifactAnalysis.js";
import { buildJavaScriptArtifactGraph } from "../../../../src/application/javascript/JavaScriptArtifactGraphBuilder.js";
import { immutableArtifactGraph } from "../../../fixtures/javascriptArtifactGraph.js";
import { resolveArtifactPathByContext } from "../../../../src/application/javascript/JavaScriptArtifactPathResolution.js";
import { createJavaScriptArtifactReader } from "../../../../src/artifacts/javascript/JavaScriptArtifactReader.js";
import { scanCanonicalArtifactInventory } from "../../../fixtures/artifactInventory.js";
@@ -46,7 +46,7 @@ it.each(
expect(
analysis.packages.find(({ path }) => path === "b/package.json"),
).toMatchObject({ main_status: "invalid", main_entrypoint: "" });
const graph = buildJavaScriptArtifactGraph(snapshot, files, analysis);
const graph = await immutableArtifactGraph(snapshot, files, analysis);
const roles = graph.nodes.filter(
({ kind }) => kind === "electron-renderer",
);
@@ -123,7 +123,7 @@ it.each(
resolution_status: valid ? "resolved" : "unavailable",
resolved_path: valid ? "b/index.js" : null,
});
const graph = buildJavaScriptArtifactGraph(snapshot, files, analysis);
const graph = await immutableArtifactGraph(snapshot, files, analysis);
const role = graph.nodes.find(({ kind }) => kind === "electron-renderer");
expect(role?.observations).toEqual(
expect.arrayContaining([
@@ -7,7 +7,7 @@ import { scanCanonicalArtifactInventory } from "../../fixtures/artifactInventory
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { readJavaScriptArtifactFiles } from "../../support/javascriptApplicationScope.js";
import { analyzeJavaScriptArtifactFiles } from "../../../src/application/javascript/JavaScriptArtifactAnalysis.js";
import { buildJavaScriptArtifactGraph } from "../../../src/application/javascript/JavaScriptArtifactGraphBuilder.js";
import { immutableArtifactGraph } from "../../fixtures/javascriptArtifactGraph.js";
import { createJavaScriptArtifactReader } from "../../../src/artifacts/javascript/JavaScriptArtifactReader.js";
import type {
JavaScriptJsonModuleObservation,
@@ -55,7 +55,7 @@ it("uses the first structured observation when paths repeat", async () => {
)
throw new TypeError("Expected structured artifact observations");
const graph = buildJavaScriptArtifactGraph(snapshot, files, {
const graph = await immutableArtifactGraph(snapshot, files, {
...analysis,
packages: [
packageObservation,
@@ -138,7 +138,7 @@ it("preserves unknown omitted-key count for unavailable JSON", async () => {
try {
const files = await readJavaScriptArtifactFiles(reader, snapshot);
const analysis = analyzeJavaScriptArtifactFiles(files);
const graph = buildJavaScriptArtifactGraph(snapshot, files, analysis);
const graph = await immutableArtifactGraph(snapshot, files, analysis);
const configFile = graph.nodes.find(({ observations }) =>
observations.some(
@@ -183,7 +183,7 @@ it("preserves unknown omitted-key count for unselected JSON", async () => {
);
const analysis = analyzeJavaScriptArtifactFiles(files);
const graph = buildJavaScriptArtifactGraph(snapshot, files, analysis);
const graph = await immutableArtifactGraph(snapshot, files, analysis);
const configFile = graph.nodes.find(({ observations }) =>
observations.some(
@@ -228,7 +228,7 @@ it("preserves unknown omitted-key count for unselected uppercase JSON", async ()
);
const analysis = analyzeJavaScriptArtifactFiles(files);
const graph = buildJavaScriptArtifactGraph(snapshot, files, analysis);
const graph = await immutableArtifactGraph(snapshot, files, analysis);
const configFile = graph.nodes.find(({ observations }) =>
observations.some(
+11
View File
@@ -0,0 +1,11 @@
import { completeJavaScriptAnalysisSteps } from "../../src/application/javascript/JavaScriptAnalysisControl.js";
import { buildImmutableJavaScriptArtifactGraphSteps } from "../../src/application/javascript/JavaScriptArtifactGraphBuilder.js";
import type { JavaScriptApplicationGraph } from "../../src/domain/javascript/javascriptApplicationGraph.js";
/** Drive the production immutable application graph builder for boundary tests. */
export const immutableArtifactGraph = (
...args: Parameters<typeof buildImmutableJavaScriptArtifactGraphSteps>
): Promise<JavaScriptApplicationGraph> =>
completeJavaScriptAnalysisSteps(
buildImmutableJavaScriptArtifactGraphSteps(...args),
);
@@ -2,7 +2,7 @@ import { expect, it, onTestFinished, vi } from "vitest";
import { createJavaScriptArtifactReader } from "../../src/artifacts/javascript/JavaScriptArtifactReader.js";
import { analyzeJavaScriptArtifactFiles } from "../../src/application/javascript/JavaScriptArtifactAnalysis.js";
import { buildJavaScriptArtifactGraph } from "../../src/application/javascript/JavaScriptArtifactGraphBuilder.js";
import { immutableArtifactGraph } from "../fixtures/javascriptArtifactGraph.js";
import type {
JavaScriptArtifactFile,
JavaScriptArtifactFileSet,
@@ -121,7 +121,7 @@ it("reuses parsed package and JSON facts while building the public graph", async
return originalParse(text);
});
const graph = buildJavaScriptArtifactGraph(snapshot, files, analysis);
const graph = await immutableArtifactGraph(snapshot, files, analysis);
expect(
graph.edges.find(
({ relation, properties }) =>