import type { ErrorCode } from "./error"; export const LIBRARY_OVERRIDE_FRESHNESS_SCHEMA = 1 as const; export const LIBRARY_OVERRIDE_COMMIT_OPERATION = "COMMIT_OVERRIDE" as const; export interface OverrideFreshnessStateIR { schemaVersion: typeof LIBRARY_OVERRIDE_FRESHNESS_SCHEMA; sourceLibraryId: string; sourceGeneration: number; sourceRevision: number; dependencyClosureSha256: string; invalidationToken: string; localDataBlockId: string; referenceSourceDataBlockId: string; hierarchyRootDataBlockId: string; owner: "LOCAL_OVERRIDE"; readOnly: false; referenceReadOnly: true; } export interface OverrideFreshnessRequestIR { schemaVersion: typeof LIBRARY_OVERRIDE_FRESHNESS_SCHEMA; operation: typeof LIBRARY_OVERRIDE_COMMIT_OPERATION; sourceLibraryId: string; sourceGeneration: number; sourceRevision: number; dependencyClosureSha256: string; invalidationToken: string; baseRevision: number; localDataBlockId: string; referenceSourceDataBlockId: string; hierarchyRootDataBlockId: string; owner: "LOCAL_OVERRIDE"; readOnly: false; referenceReadOnly: true; } export interface OverrideFreshnessDecisionIR { status: "READY" | "BLOCKED"; code: ErrorCode | null; } export class OverrideFreshnessValidationError extends Error { readonly code: ErrorCode; readonly path?: string; constructor(code: ErrorCode, message: string, path?: string) { super(`${code}: ${message}`); this.name = "OverrideFreshnessValidationError"; this.code = code; this.path = path; } } const LIBRARY_ID = /^library:[a-f0-9]{64}$/; const SHA256 = /^[a-f0-9]{64}$/; const TOKEN = /^override-token:[a-f0-9]{64}$/; const DATA_BLOCK_ID = /^[A-Za-z0-9][A-Za-z0-9:._/ -]{0,255}$/; function record(value: unknown, path: string): Record { if (typeof value !== "object" || value === null || Array.isArray(value)) throw new OverrideFreshnessValidationError("TASK_VALIDATION_FAILED", `${path} must be an object`, path); return value as Record; } function exactKeys(value: Record, expected: readonly string[], path: string): void { const actual = Object.keys(value).sort(); const allowed = [...expected].sort(); if (actual.length !== allowed.length || actual.some((key, index) => key !== allowed[index])) throw new OverrideFreshnessValidationError("TASK_VALIDATION_FAILED", `${path} contains undeclared fields`, path); } function integer(value: unknown, path: string): number { if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) throw new OverrideFreshnessValidationError("TASK_VALIDATION_FAILED", `${path} must be a safe integer >= 0`, path); return value; } function id(value: unknown, path: string): string { if (typeof value !== "string" || !DATA_BLOCK_ID.test(value)) throw new OverrideFreshnessValidationError("TASK_VALIDATION_FAILED", `${path} is invalid`, path); return value; } function digest(value: unknown, path: string): string { if (typeof value !== "string" || !SHA256.test(value)) throw new OverrideFreshnessValidationError("TASK_VALIDATION_FAILED", `${path} must be a SHA-256 digest`, path); return value; } function library(value: unknown, path: string): string { if (typeof value !== "string" || !LIBRARY_ID.test(value)) throw new OverrideFreshnessValidationError("TASK_VALIDATION_FAILED", `${path} must be a library identity`, path); return value; } function token(value: unknown, path: string): string { if (typeof value !== "string" || !TOKEN.test(value)) throw new OverrideFreshnessValidationError("TASK_VALIDATION_FAILED", `${path} must be an invalidation token`, path); return value; } function ownership(value: Record, path: string): void { if (value.owner !== "LOCAL_OVERRIDE" || value.readOnly !== false || value.referenceReadOnly !== true) throw new OverrideFreshnessValidationError("LINKED_DATA_MUTATION_BLOCKED", `${path} ownership semantics are invalid`, path); } const COMMON_KEYS = ["schemaVersion", "sourceLibraryId", "sourceGeneration", "sourceRevision", "dependencyClosureSha256", "invalidationToken", "localDataBlockId", "referenceSourceDataBlockId", "hierarchyRootDataBlockId", "owner", "readOnly", "referenceReadOnly"] as const; export function parseOverrideFreshnessState(value: unknown): OverrideFreshnessStateIR { const state = record(value, "state"); exactKeys(state, COMMON_KEYS, "state"); if (state.schemaVersion !== LIBRARY_OVERRIDE_FRESHNESS_SCHEMA) throw new OverrideFreshnessValidationError("PROTOCOL_MISMATCH", "Unsupported override freshness state schema", "schemaVersion"); ownership(state, "state"); const sourceLibraryId = library(state.sourceLibraryId, "sourceLibraryId"); return { schemaVersion: LIBRARY_OVERRIDE_FRESHNESS_SCHEMA, sourceLibraryId, sourceGeneration: integer(state.sourceGeneration, "sourceGeneration"), sourceRevision: integer(state.sourceRevision, "sourceRevision"), dependencyClosureSha256: digest(state.dependencyClosureSha256, "dependencyClosureSha256"), invalidationToken: token(state.invalidationToken, "invalidationToken"), localDataBlockId: id(state.localDataBlockId, "localDataBlockId"), referenceSourceDataBlockId: id(state.referenceSourceDataBlockId, "referenceSourceDataBlockId"), hierarchyRootDataBlockId: id(state.hierarchyRootDataBlockId, "hierarchyRootDataBlockId"), owner: "LOCAL_OVERRIDE", readOnly: false, referenceReadOnly: true, }; } export function parseOverrideFreshnessRequest(value: unknown): OverrideFreshnessRequestIR { const request = record(value, "request"); exactKeys(request, [...COMMON_KEYS, "operation", "baseRevision"], "request"); if (request.schemaVersion !== LIBRARY_OVERRIDE_FRESHNESS_SCHEMA) throw new OverrideFreshnessValidationError("PROTOCOL_MISMATCH", "Unsupported override freshness request schema", "schemaVersion"); if (request.operation !== LIBRARY_OVERRIDE_COMMIT_OPERATION) throw new OverrideFreshnessValidationError("TASK_VALIDATION_FAILED", "override commit operation is invalid", "operation"); ownership(request, "request"); return { schemaVersion: LIBRARY_OVERRIDE_FRESHNESS_SCHEMA, operation: LIBRARY_OVERRIDE_COMMIT_OPERATION, sourceLibraryId: library(request.sourceLibraryId, "sourceLibraryId"), sourceGeneration: integer(request.sourceGeneration, "sourceGeneration"), sourceRevision: integer(request.sourceRevision, "sourceRevision"), dependencyClosureSha256: digest(request.dependencyClosureSha256, "dependencyClosureSha256"), invalidationToken: token(request.invalidationToken, "invalidationToken"), baseRevision: integer(request.baseRevision, "baseRevision"), localDataBlockId: id(request.localDataBlockId, "localDataBlockId"), referenceSourceDataBlockId: id(request.referenceSourceDataBlockId, "referenceSourceDataBlockId"), hierarchyRootDataBlockId: id(request.hierarchyRootDataBlockId, "hierarchyRootDataBlockId"), owner: "LOCAL_OVERRIDE", readOnly: false, referenceReadOnly: true, }; } export function gateOverrideFreshness(stateValue: unknown, requestValue: unknown): OverrideFreshnessDecisionIR { let state: OverrideFreshnessStateIR; let request: OverrideFreshnessRequestIR; try { state = parseOverrideFreshnessState(stateValue); request = parseOverrideFreshnessRequest(requestValue); } catch (error) { return { status: "BLOCKED", code: error instanceof OverrideFreshnessValidationError ? error.code : "TASK_VALIDATION_FAILED" }; } if (request.baseRevision !== state.sourceRevision || request.sourceLibraryId !== state.sourceLibraryId || request.sourceGeneration !== state.sourceGeneration || request.sourceRevision !== state.sourceRevision || request.dependencyClosureSha256 !== state.dependencyClosureSha256 || request.invalidationToken !== state.invalidationToken) { return { status: "BLOCKED", code: "REVISION_CONFLICT" }; } if (request.localDataBlockId !== state.localDataBlockId || request.referenceSourceDataBlockId !== state.referenceSourceDataBlockId || request.hierarchyRootDataBlockId !== state.hierarchyRootDataBlockId) { return { status: "BLOCKED", code: "ASSET_SOURCE_HASH_MISMATCH" }; } return { status: "READY", code: null }; }