86 lines
2.7 KiB
TypeScript
86 lines
2.7 KiB
TypeScript
import { isVersionCompatible } from "../../src/application/policies/compatibility.ts";
|
|
import { verifyContractSet } from "../../src/contracts/contract-set.ts";
|
|
import type { InstalledContractPackageIdentity } from "../../src/contracts/external-contract-runtime.ts";
|
|
import type { ReleaseArtifact } from "../../src/contracts/release-artifacts.ts";
|
|
import { compareReleaseToRuntime } from "../../src/contracts/release-tokens.ts";
|
|
|
|
export type ReleaseRuntimeCoherenceInput = Readonly<{
|
|
release: ReleaseArtifact;
|
|
runtime: Readonly<{
|
|
BUILD_ID: string;
|
|
CONFIG_SCHEMA_VERSION: string;
|
|
API_CONTRACT_VERSION?: string;
|
|
RELEASE_ID: string;
|
|
}>;
|
|
contractPackages: readonly InstalledContractPackageIdentity[];
|
|
}>;
|
|
|
|
export type ReleaseRuntimeCoherence = Readonly<{
|
|
compatible: boolean;
|
|
mismatches: readonly string[];
|
|
warnings: readonly string[];
|
|
}>;
|
|
|
|
/**
|
|
* Verifies the runtime identity using the release schema's own contract model.
|
|
* V1 retains the scalar compatibility policy. V2 has no scalar projection: its
|
|
* identity is the exact compiled package tuple set and canonical set digest.
|
|
*/
|
|
export async function verifyReleaseRuntimeCoherence(
|
|
input: ReleaseRuntimeCoherenceInput,
|
|
): Promise<ReleaseRuntimeCoherence> {
|
|
if (input.release.schemaVersion === 1) {
|
|
if (input.runtime.API_CONTRACT_VERSION === undefined) {
|
|
return compareWithoutContractScalar(input, ["apiContractVersion"]);
|
|
}
|
|
return compareReleaseToRuntime(input.release, {
|
|
...input.runtime,
|
|
API_CONTRACT_VERSION: input.runtime.API_CONTRACT_VERSION,
|
|
});
|
|
}
|
|
|
|
const comparison = compareWithoutContractScalar(input);
|
|
const contractSet = await verifyContractSet({
|
|
expected: input.contractPackages,
|
|
manifest: input.release.contractSet,
|
|
});
|
|
if (contractSet.ok) return comparison;
|
|
|
|
const mismatches = Object.freeze([
|
|
...comparison.mismatches,
|
|
contractSet.code,
|
|
]);
|
|
return Object.freeze({
|
|
compatible: false,
|
|
mismatches,
|
|
warnings: comparison.warnings,
|
|
});
|
|
}
|
|
|
|
function compareWithoutContractScalar(
|
|
input: Pick<ReleaseRuntimeCoherenceInput, "release" | "runtime">,
|
|
initialMismatches: readonly string[] = [],
|
|
): ReleaseRuntimeCoherence {
|
|
const mismatches = [...initialMismatches];
|
|
if (input.release.buildId !== input.runtime.BUILD_ID) {
|
|
mismatches.push("buildId");
|
|
}
|
|
if (
|
|
!isVersionCompatible(
|
|
input.release.configSchemaVersion,
|
|
input.runtime.CONFIG_SCHEMA_VERSION,
|
|
)
|
|
) {
|
|
mismatches.push("configSchemaVersion");
|
|
}
|
|
const warnings =
|
|
input.release.releaseId === input.runtime.RELEASE_ID
|
|
? []
|
|
: ["releaseId"];
|
|
return Object.freeze({
|
|
compatible: mismatches.length === 0,
|
|
mismatches: Object.freeze(mismatches),
|
|
warnings: Object.freeze(warnings),
|
|
});
|
|
}
|