From cf346368c482d9b99595256857329482d857c36c Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Sun, 11 Oct 2026 01:54:36 +0800 Subject: [PATCH] perf(javascript): reuse graph buckets and query indexes Append overlapping observations to node buckets instead of recopying groups, and share one node index across semantic seed admission and traversal. Preserve merged provenance and absent-seed behavior. --- .../javascriptRuntimeReconciliationGraph.ts | 7 +++++-- .../javascriptSemanticGraphTraversal.test.ts | 14 +++++++++++++ .../javascript/javascriptSemanticQuery.ts | 20 +++++++------------ .../javascriptRuntimeReconciliation.test.ts | 12 ++++++++++- 4 files changed, 37 insertions(+), 16 deletions(-) diff --git a/src/domain/javascript/javascriptRuntimeReconciliationGraph.ts b/src/domain/javascript/javascriptRuntimeReconciliationGraph.ts index d3c8f371fd..864b9d6f79 100644 --- a/src/domain/javascript/javascriptRuntimeReconciliationGraph.ts +++ b/src/domain/javascript/javascriptRuntimeReconciliationGraph.ts @@ -212,8 +212,11 @@ const graphCoverage = ( const mergeNodes = (nodes: readonly ApplicationNode[]): ApplicationNode[] => { const grouped = new Map(); - for (const node of nodes) - grouped.set(node.node_id, [...(grouped.get(node.node_id) ?? []), node]); + for (const node of nodes) { + const group = grouped.get(node.node_id); + if (group === undefined) grouped.set(node.node_id, [node]); + else group.push(node); + } const merged = [...grouped.values()].map((group) => { const first = group[0]; if (first === undefined) throw new TypeError("Empty JAG node group"); diff --git a/src/domain/javascript/javascriptSemanticGraphTraversal.test.ts b/src/domain/javascript/javascriptSemanticGraphTraversal.test.ts index e42af295f1..b73a83ee39 100644 --- a/src/domain/javascript/javascriptSemanticGraphTraversal.test.ts +++ b/src/domain/javascript/javascriptSemanticGraphTraversal.test.ts @@ -316,6 +316,20 @@ it("returns every relevant unknown inline", () => { expect(result.unknowns).toHaveLength(1); }); +it("does not admit a semantic-node seed absent from the graph", () => { + const result = queryJavaScriptSemanticGraph(fixtureGraph(), { + seed: { kind: "semantic-node", node_id: `jsrg_node_${"f".repeat(64)}` }, + direction: "forward-influence", + }); + + expect(result).toMatchObject({ + status: "no-match", + seed_node_ids: [], + nodes: [], + summary: { total_seed_matches: 0, traversed_nodes: 0 }, + }); +}); + it("returns the complete deterministic forward influence result inline", () => { const graph = fixtureGraph(); const input = { diff --git a/src/domain/javascript/javascriptSemanticQuery.ts b/src/domain/javascript/javascriptSemanticQuery.ts index 6439d8954f..1870162570 100644 --- a/src/domain/javascript/javascriptSemanticQuery.ts +++ b/src/domain/javascript/javascriptSemanticQuery.ts @@ -30,10 +30,6 @@ interface Traversal { readonly modules: Set; } -interface SeedAdmission { - readonly nodeIds: string[]; -} - interface QueryResult { readonly retainedNodes: JavaScriptSemanticGraphNode[]; readonly retainedRelations: JavaScriptSemanticGraphRelation[]; @@ -47,10 +43,10 @@ export const queryJavaScriptSemanticGraph = ( const input = javaScriptSemanticQueryInputSchema.parse(rawInput); const queryId = queryIdentifier(graph, input); const seeds = resolveSeeds(graph, input); - const admission = admitSeeds(graph, seeds); - const retainedSeeds = admission.nodeIds; + const nodes = new Map(graph.nodes.map((node) => [node.node_id, node])); + const retainedSeeds = admitSeeds(nodes, seeds); const adjacency = buildAdjacency(graph.relations, input); - const traversal = traverse(graph, retainedSeeds, adjacency); + const traversal = traverse(nodes, retainedSeeds, adjacency); const result = createQueryResult(graph, traversal); const allRelevantUnknowns = relevantUnknownFrontiers( graph, @@ -122,17 +118,16 @@ const createQueryResult = ( }; const admitSeeds = ( - graph: JavaScriptSemanticGraph, + nodes: ReadonlyMap, seeds: readonly string[], -): SeedAdmission => { - const nodes = new Map(graph.nodes.map((node) => [node.node_id, node])); +): string[] => { const retained: string[] = []; for (const nodeId of seeds) { const node = nodes.get(nodeId); if (node === undefined) continue; retained.push(nodeId); } - return { nodeIds: retained }; + return retained; }; const buildAdjacency = ( @@ -222,11 +217,10 @@ const addEntry = ( }; const traverse = ( - graph: JavaScriptSemanticGraph, + nodes: ReadonlyMap, seeds: readonly string[], adjacency: ReadonlyMap, ): Traversal => { - const nodes = new Map(graph.nodes.map((node) => [node.node_id, node])); const nodeIds = new Set(seeds); const relationIds = new Set(); const functionIds = new Set(); diff --git a/tests/composition/runtime-observation/javascriptRuntimeReconciliation.test.ts b/tests/composition/runtime-observation/javascriptRuntimeReconciliation.test.ts index a6809156eb..ec59b35c7c 100644 --- a/tests/composition/runtime-observation/javascriptRuntimeReconciliation.test.ts +++ b/tests/composition/runtime-observation/javascriptRuntimeReconciliation.test.ts @@ -316,7 +316,7 @@ it("imports an operator-provided cache layer through an explicit file mapping", const application = await applicationFixture(); const cache = await createTestTempDirectory("rea-runtime-cache-static-"); const runtimeCache = await createTestTempDirectory("rea-runtime-cache-live-"); - const cacheSource = "export const cachedFeature = 'fixture';\n"; + const cacheSource = SOURCE; await mkdir(join(cache, "mapped")); await Promise.all([ writeFile(join(cache, "mapped", "chunk.js"), cacheSource), @@ -373,6 +373,16 @@ it("imports an operator-provided cache layer through an explicit file mapping", status: "unknown", reason: "layer-outside-runtime-scope", }); + const sharedFile = result.graph.nodes.find( + ({ observations }) => + observations.some(({ properties }) => properties.path === "app.js") && + observations.some( + ({ properties }) => properties.path === "mapped/chunk.js", + ), + ); + expect( + sharedFile?.observations.map(({ properties }) => properties.path), + ).toEqual(expect.arrayContaining(["app.js", "mapped/chunk.js"])); }); const applicationFixture = async (): Promise => {