refactor(javascript): own application node merging in the graph domain

This commit is contained in:
morluto
2026-10-22 11:11:42 +08:00
parent 63fe0eb5db
commit 51e0df97e9
4 changed files with 112 additions and 94 deletions
@@ -4,6 +4,7 @@ import {
createJavaScriptApplicationEdge,
createJavaScriptApplicationGraph,
createJavaScriptApplicationNode,
mergeJavaScriptApplicationNodes,
javascriptApplicationGraphSchema,
parseJavaScriptApplicationGraph,
serializeJavaScriptApplicationGraph,
@@ -23,6 +24,58 @@ import {
sourceMapReference,
unknownEvidence,
} from "./javascriptApplicationGraph.fixture.js";
it("merges observations by entity identity without changing source nodes", () => {
const evidence = artifactEvidence(APPLICATION_GRAPH_DIGESTS.asar, "main.js");
const first = contentNode(evidence, { selected: "first" });
const second = contentNode(evidence, { selected: "second" });
const reversedIdentity = {
sha256: APPLICATION_GRAPH_DIGESTS.asar,
stability: "global-exact" as const,
strategy: "content-digest" as const,
};
const merged = mergeJavaScriptApplicationNodes([
first,
{ ...second, identity: reversedIdentity },
first,
]);
expect(merged).toHaveLength(1);
const node = firstOf(merged, "merged node");
expect(node.node_id).toBe(first.node_id);
expect(node.observations.map(({ properties }) => properties)).toEqual(
expect.arrayContaining([{ selected: "first" }, { selected: "second" }]),
);
expect(node.observations).toHaveLength(2);
expect(first.observations).toHaveLength(1);
expect(second.observations).toHaveLength(1);
const graph = graphForNode(node);
expect(parseJavaScriptApplicationGraph(graph)).toEqual(graph);
});
it.each(["kind", "identity"] as const)(
"rejects colliding entity IDs with a different %s before merging observations",
(field) => {
const node = contentNode(
artifactEvidence(APPLICATION_GRAPH_DIGESTS.asar, "main.js"),
);
const conflicting =
field === "kind"
? { ...node, kind: "javascript-asset" as const }
: {
...node,
identity: {
strategy: "content-digest" as const,
stability: "global-exact" as const,
sha256: "f".repeat(64),
},
};
expect(() => mergeJavaScriptApplicationNodes([node, conflicting])).toThrow(
"Colliding JAG node identifiers disagree",
);
expect(node.observations).toHaveLength(1);
},
);
const createLargeNativeExportGraph = (): JavaScriptApplicationGraph => {
const properties = Object.fromEntries(
Array.from({ length: 1_001 }, (_, index) => [