refactor(javascript): own paired graph evidence resolution

This commit is contained in:
morluto
2026-10-24 01:11:57 +08:00
parent 6cd3fa09b1
commit f276bcbf6c
4 changed files with 83 additions and 38 deletions
@@ -37,6 +37,7 @@ import {
applicationGraphEvidenceInputError,
parseApplicationGraphEvidence,
parseNativeApplicationEvidence,
type ApplicationGraphEvidenceSource,
} from "./JavaScriptApplicationEvidenceGraph.js";
import {
createApplicationFeatureTraceEvidence,
@@ -81,6 +82,36 @@ const resolveWorkflowEvidence = (
: ok(raw);
};
/** Resolve both selections before authenticating either graph, retaining diagnostic inputs. */
const resolveApplicationGraphPair = (
operation: string,
input: { readonly left: unknown; readonly right: unknown },
lookup: EvidenceLookup | undefined,
): Result<
{
readonly left: ApplicationGraphEvidenceSource;
readonly right: ApplicationGraphEvidenceSource;
readonly selected: { readonly left: unknown; readonly right: unknown };
},
AnalysisError
> => {
const selectedLeft = resolveWorkflowEvidence(input.left, lookup);
if (!selectedLeft.ok) return selectedLeft;
const selectedRight = resolveWorkflowEvidence(input.right, lookup);
if (!selectedRight.ok) return selectedRight;
const left = parseApplicationGraphEvidence(selectedLeft.value, ["left"]);
if (!left.ok)
return err(applicationGraphEvidenceInputError(operation, left.error));
const right = parseApplicationGraphEvidence(selectedRight.value, ["right"]);
if (!right.ok)
return err(applicationGraphEvidenceInputError(operation, right.error));
return ok({
left: left.value,
right: right.value,
selected: { left: selectedLeft.value, right: selectedRight.value },
});
};
/** Authenticate Evidence and derive one bounded cross-layer feature trace. */
export const traceApplicationFeatureEvidence = (
rawInput: unknown,
@@ -163,23 +194,10 @@ export const compareApplicationVersionsEvidence = (
cause: request.error,
}),
);
const selectedLeft = resolveWorkflowEvidence(request.data.left, lookup);
if (!selectedLeft.ok) return selectedLeft;
const selectedRight = resolveWorkflowEvidence(request.data.right, lookup);
if (!selectedRight.ok) return selectedRight;
const input = {
...request.data,
left: selectedLeft.value,
right: selectedRight.value,
};
const leftResult = parseApplicationGraphEvidence(input.left, ["left"]);
if (!leftResult.ok)
return err(applicationGraphEvidenceInputError(operation, leftResult.error));
const rightResult = parseApplicationGraphEvidence(input.right, ["right"]);
if (!rightResult.ok)
return err(
applicationGraphEvidenceInputError(operation, rightResult.error),
);
const pair = resolveApplicationGraphPair(operation, request.data, lookup);
if (!pair.ok) return pair;
const { left, right } = pair.value;
const input = { ...request.data, ...pair.value.selected };
const leftNativeResult = parseNativeApplicationEvidence(
input.left_native_observations,
["left_native_observations"],
@@ -196,8 +214,6 @@ export const compareApplicationVersionsEvidence = (
return err(
applicationGraphEvidenceInputError(operation, rightNativeResult.error),
);
const left = leftResult.value;
const right = rightResult.value;
const leftNative = leftNativeResult.value;
const rightNative = rightNativeResult.value;
const identities = applicationVersionIdentitiesSchema.safeParse({
@@ -297,25 +313,10 @@ export const compareJavaScriptExportShapesEvidence = (
cause: request.error,
}),
);
const selectedLeft = resolveWorkflowEvidence(request.data.left, lookup);
if (!selectedLeft.ok) return selectedLeft;
const selectedRight = resolveWorkflowEvidence(request.data.right, lookup);
if (!selectedRight.ok) return selectedRight;
const input = {
...request.data,
left: selectedLeft.value,
right: selectedRight.value,
};
const leftResult = parseApplicationGraphEvidence(input.left, ["left"]);
if (!leftResult.ok)
return err(applicationGraphEvidenceInputError(operation, leftResult.error));
const rightResult = parseApplicationGraphEvidence(input.right, ["right"]);
if (!rightResult.ok)
return err(
applicationGraphEvidenceInputError(operation, rightResult.error),
);
const left = leftResult.value;
const right = rightResult.value;
const pair = resolveApplicationGraphPair(operation, request.data, lookup);
if (!pair.ok) return pair;
const { left, right } = pair.value;
const input = { ...request.data, ...pair.value.selected };
const identities = javaScriptExportShapeIdentitiesSchema.safeParse({
left: left.evidence,
right: right.evidence,
@@ -239,6 +239,35 @@ const checkMalformedEvidenceBeforeIdentities = async ({
details: { issues: [{ path, reason: "invalid_type" }] },
});
}
const missingLeft = {
kind: "retained-evidence",
evidence_id: `ev_${"a".repeat(64)}`,
};
const missingRight = {
kind: "retained-evidence",
evidence_id: `ev_${"b".repeat(64)}`,
};
for (const [command, extra] of [
["compare-application-versions", {}],
["compare-javascript-export-shapes", selectors],
] as const) {
// Lookup of both selected sources precedes graph authentication.
for (const [left, right, evidenceId] of [
[malformed, missingRight, missingRight.evidence_id],
[missingLeft, missingRight, missingLeft.evidence_id],
] as const) {
const rejected = await runJson(cli, root, command, {
left,
right,
...extra,
});
expect(rejected.exitCode).toBe(1);
expect(rejected.json).toMatchObject({
code: "evidence_integrity_mismatch",
details: { evidence_id: evidenceId, reason: "missing" },
});
}
}
};
cliTest(
@@ -198,6 +198,8 @@ describe("complete comparison projections", () => {
]);
const compared = compareApplicationVersionsEvidence({ left, right });
if (!compared.ok) throw compared.error;
expect(compared.value.sources[0]).toBe(left);
expect(compared.value.sources[1]).toBe(right);
const result = compared.value.result;
expectFullComparisonResultSchema(result);
const modules = result.items.filter(
@@ -7,6 +7,7 @@ import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { analyzeJavaScriptApplication } from "../../support/javascriptApplicationScope.js";
import { parseApplicationGraphEvidence } from "../../../src/application/javascript/JavaScriptApplicationEvidenceGraph.js";
import { compareJavaScriptExportShapesEvidence } from "../../../src/application/javascript/JavaScriptApplicationWorkflowService.js";
import { compareJavaScriptExportShapes } from "../../../src/domain/javascript/javascriptExportShapeComparison.js";
import {
javaScriptExportShapeComparisonResultSchema,
@@ -40,6 +41,18 @@ describe("JavaScript export return-shape comparison", () => {
const first = compare(left, right);
const second = compare(left, right);
const execution = compareJavaScriptExportShapesEvidence({
left: left.evidence,
right: right.evidence,
left_module_path: "parser.mjs",
left_export_name: "default",
right_module_path: "parser.mjs",
right_export_name: "default",
});
if (!execution.ok) throw execution.error;
expect(execution.value.result).toEqual(first);
expect(execution.value.sources[0]).toBe(left.evidence);
expect(execution.value.sources[1]).toBe(right.evidence);
expect(first.comparison_id).toBe(second.comparison_id);
expect(() =>