export const CORRECTIVE_CASE_SCHEMA = 1 as const; export type CaseSurface = "DESKTOP" | "WASM"; export type CommandStatus = "FINISHED" | "BLOCKED" | "CANCELLED" | "UNSUPPORTED" | "MALFORMED" | "EXTERNAL_REQUIRED"; export type MainMutation = "CHANGED" | "UNCHANGED"; export type TargetKind = "ASSET_LIBRARY" | "DATABLOCK" | "MESH" | "MODIFIER" | "SCENE"; export type EditMode = "READ_ONLY" | "EDITABLE"; export type Authorization = "NONE" | "USER_EDIT" | "SYSTEM"; export type SourceKind = "LOCAL" | "EXTERNAL" | "PACKED"; export type ResourceProvider = "NONE" | "OPFS" | "EXTERNAL_LIBRARY" | "PACKED_BLEND"; export interface CommandContext { mainId: string; targetKind: TargetKind; targetId?: string; editMode: EditMode; authorization: Authorization; source: { kind: SourceKind; sha256: string }; resourceProvider?: ResourceProvider; } export interface MainMutationCommand { schemaVersion: typeof CORRECTIVE_CASE_SCHEMA; requestId: string; commandType: string; baseRevision: number; context: CommandContext; payload: Record; } export interface MainMutationResult { schemaVersion: typeof CORRECTIVE_CASE_SCHEMA; requestId: string; status: CommandStatus; code: string; baseRevision: number; mainRevisionBefore: number; mainRevisionAfter: number; mainMutation: MainMutation; delta: Record | null; persistence: Record | null; error: Record | null; } export interface SurfaceReceipt { schemaVersion: typeof CORRECTIVE_CASE_SCHEMA; surface: CaseSurface; requestId: string; commandType: string; context: CommandContext | null; result: MainMutationResult; } export interface SharedCaseReceipt { schemaVersion: typeof CORRECTIVE_CASE_SCHEMA; requestId: string; commandType: string; desktop: SurfaceReceipt; wasm: SurfaceReceipt; parity: "MATCH" | "MISMATCH"; differences: string[]; } export interface SurfaceCaseOptions { command: unknown; mainRevision: number; execute: (command: MainMutationCommand) => MainMutationResult | Promise; } export class CorrectiveCaseRunnerError extends Error { readonly code: string; constructor(code: string, message: string) { super(`${code}: ${message}`); this.name = "CorrectiveCaseRunnerError"; this.code = code; } } const SHA256 = /^[a-f0-9]{64}$/u; const REQUEST_ID = /^[A-Za-z0-9._:-]{1,256}$/u; const COMMAND_TYPE = /^[a-z][A-Za-z0-9]{0,127}$/u; const STATUS_CODES: Readonly> = { FINISHED: "OK", BLOCKED: "REVISION_CONFLICT", CANCELLED: "CANCELLED", UNSUPPORTED: "COMMAND_UNSUPPORTED", MALFORMED: "COMMAND_MALFORMED", EXTERNAL_REQUIRED: "RESOURCE_REQUIRED", }; const STATUS_CODE_PAIRS = new Set([ "FINISHED:OK", "BLOCKED:REVISION_CONFLICT", "BLOCKED:INVALID_CONTEXT", "CANCELLED:CANCELLED", "UNSUPPORTED:COMMAND_UNSUPPORTED", "MALFORMED:COMMAND_MALFORMED", "EXTERNAL_REQUIRED:RESOURCE_REQUIRED", ]); const STATUSES = new Set(Object.keys(STATUS_CODES) as CommandStatus[]); const TARGET_KINDS = new Set(["ASSET_LIBRARY", "DATABLOCK", "MESH", "MODIFIER", "SCENE"]); const EDIT_MODES = new Set(["READ_ONLY", "EDITABLE"]); const AUTHORIZATIONS = new Set(["NONE", "USER_EDIT", "SYSTEM"]); const SOURCE_KINDS = new Set(["LOCAL", "EXTERNAL", "PACKED"]); const RESOURCE_PROVIDERS = new Set(["NONE", "OPFS", "EXTERNAL_LIBRARY", "PACKED_BLEND"]); function record(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value); } function exactKeys(value: Record, allowed: ReadonlySet, label: string): void { for (const key of Object.keys(value)) { if (!allowed.has(key)) throw new CorrectiveCaseRunnerError("COMMAND_MALFORMED", `${label}.${key} is not allowed`); } } function boundedText(value: unknown, label: string, pattern: RegExp, maximum = 256): string { if (typeof value !== "string" || value.length === 0 || value.length > maximum || !pattern.test(value)) { throw new CorrectiveCaseRunnerError("COMMAND_MALFORMED", `${label} is invalid`); } return value; } function nonNegativeInteger(value: unknown, label: string): number { if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) { throw new CorrectiveCaseRunnerError("COMMAND_MALFORMED", `${label} must be a non-negative integer`); } return value; } function commandContext(value: unknown): CommandContext { if (!record(value)) throw new CorrectiveCaseRunnerError("COMMAND_MALFORMED", "context must be an object"); exactKeys(value, new Set(["mainId", "targetKind", "targetId", "editMode", "authorization", "source", "resourceProvider"]), "context"); const mainId = boundedText(value.mainId, "context.mainId", /^.{1,256}$/u); if (typeof value.targetKind !== "string" || !TARGET_KINDS.has(value.targetKind as TargetKind)) { throw new CorrectiveCaseRunnerError("COMMAND_MALFORMED", "context.targetKind is invalid"); } const targetId = value.targetId === undefined ? undefined : boundedText(value.targetId, "context.targetId", /^.{1,256}$/u); if (typeof value.editMode !== "string" || !EDIT_MODES.has(value.editMode as EditMode)) { throw new CorrectiveCaseRunnerError("COMMAND_MALFORMED", "context.editMode is invalid"); } if (typeof value.authorization !== "string" || !AUTHORIZATIONS.has(value.authorization as Authorization)) { throw new CorrectiveCaseRunnerError("COMMAND_MALFORMED", "context.authorization is invalid"); } if (!record(value.source) || Object.keys(value.source).some((key) => !["kind", "sha256"].includes(key))) { throw new CorrectiveCaseRunnerError("COMMAND_MALFORMED", "context.source is invalid"); } if (typeof value.source.kind !== "string" || !SOURCE_KINDS.has(value.source.kind as SourceKind) || typeof value.source.sha256 !== "string" || !SHA256.test(value.source.sha256)) { throw new CorrectiveCaseRunnerError("COMMAND_MALFORMED", "context.source provenance is invalid"); } const resourceProvider = value.resourceProvider === undefined ? undefined : value.resourceProvider; if (resourceProvider !== undefined && (typeof resourceProvider !== "string" || !RESOURCE_PROVIDERS.has(resourceProvider as ResourceProvider))) { throw new CorrectiveCaseRunnerError("COMMAND_MALFORMED", "context.resourceProvider is invalid"); } return { mainId, targetKind: value.targetKind as TargetKind, ...(targetId === undefined ? {} : { targetId }), editMode: value.editMode as EditMode, authorization: value.authorization as Authorization, source: { kind: value.source.kind as SourceKind, sha256: value.source.sha256 }, ...(resourceProvider === undefined ? {} : { resourceProvider: resourceProvider as ResourceProvider }), }; } export function validateCommand(value: unknown): MainMutationCommand { if (!record(value)) throw new CorrectiveCaseRunnerError("COMMAND_MALFORMED", "command must be an object"); exactKeys(value, new Set(["schemaVersion", "requestId", "commandType", "baseRevision", "context", "payload"]), "command"); if (value.schemaVersion !== CORRECTIVE_CASE_SCHEMA) throw new CorrectiveCaseRunnerError("COMMAND_MALFORMED", "schemaVersion is unsupported"); const requestId = boundedText(value.requestId, "requestId", REQUEST_ID); const commandType = boundedText(value.commandType, "commandType", COMMAND_TYPE); const baseRevision = nonNegativeInteger(value.baseRevision, "baseRevision"); const context = commandContext(value.context); if (!record(value.payload)) throw new CorrectiveCaseRunnerError("COMMAND_MALFORMED", "payload must be an object"); for (const key of ["requestId", "baseRevision", "context"]) { if (Object.prototype.hasOwnProperty.call(value.payload, key)) { throw new CorrectiveCaseRunnerError("COMMAND_MALFORMED", `payload cannot override ${key}`); } } return { schemaVersion: CORRECTIVE_CASE_SCHEMA, requestId, commandType, baseRevision, context, payload: { ...value.payload } }; } function resultObject(value: unknown): Record { if (!record(value)) throw new CorrectiveCaseRunnerError("COMMAND_MALFORMED", "result must be an object"); exactKeys(value, new Set(["schemaVersion", "requestId", "status", "code", "baseRevision", "mainRevisionBefore", "mainRevisionAfter", "mainMutation", "delta", "persistence", "error"]), "result"); return value; } function optionalObject(value: unknown, label: string): Record | null { if (value === undefined || value === null) return null; if (!record(value)) throw new CorrectiveCaseRunnerError("COMMAND_MALFORMED", `${label} must be an object or null`); return { ...value }; } export function normalizeResult(value: unknown, command: MainMutationCommand, mainRevisionBefore: number): MainMutationResult { const result = resultObject(value); if (result.schemaVersion !== CORRECTIVE_CASE_SCHEMA || result.requestId !== command.requestId || result.baseRevision !== command.baseRevision) { throw new CorrectiveCaseRunnerError("COMMAND_MALFORMED", "result identity is not bound to the command"); } nonNegativeInteger(result.mainRevisionBefore, "mainRevisionBefore"); nonNegativeInteger(result.mainRevisionAfter, "mainRevisionAfter"); if (result.mainRevisionBefore !== mainRevisionBefore) throw new CorrectiveCaseRunnerError("COMMAND_MALFORMED", "result before revision is not current"); if (typeof result.status !== "string" || !STATUSES.has(result.status as CommandStatus) || typeof result.code !== "string" || !STATUS_CODE_PAIRS.has(`${result.status}:${result.code}`)) { throw new CorrectiveCaseRunnerError("COMMAND_MALFORMED", "status and code are not a closed pair"); } const status = result.status as CommandStatus; const mutation = result.mainMutation; const expectedMutation: MainMutation = status === "FINISHED" ? "CHANGED" : "UNCHANGED"; if (mutation !== expectedMutation || result.mainRevisionAfter !== mainRevisionBefore + (status === "FINISHED" ? 1 : 0)) { throw new CorrectiveCaseRunnerError("COMMAND_MALFORMED", "result revision or mutation invariant failed"); } const delta = optionalObject(result.delta, "delta"); const persistence = optionalObject(result.persistence, "persistence"); const error = optionalObject(result.error, "error"); if (status !== "FINISHED" && (delta !== null || persistence !== null)) { throw new CorrectiveCaseRunnerError("COMMAND_MALFORMED", "failure result cannot claim delta or persistence"); } return { schemaVersion: CORRECTIVE_CASE_SCHEMA, requestId: command.requestId, status, code: STATUS_CODES[status], baseRevision: command.baseRevision, mainRevisionBefore, mainRevisionAfter: result.mainRevisionAfter, mainMutation: expectedMutation, delta, persistence, error, }; } function failure(command: MainMutationCommand | null, status: Exclude, before: number, message: string, code = STATUS_CODES[status]): MainMutationResult { const requestId = command?.requestId ?? "invalid-request"; const baseRevision = command?.baseRevision ?? before; return { schemaVersion: CORRECTIVE_CASE_SCHEMA, requestId, status, code, baseRevision, mainRevisionBefore: before, mainRevisionAfter: before, mainMutation: "UNCHANGED", delta: null, persistence: null, error: { code, message }, }; } function commandIdentity(value: unknown): { requestId: string; commandType: string } { const input = record(value) ? value : {}; return { requestId: typeof input.requestId === "string" && REQUEST_ID.test(input.requestId) ? input.requestId : "invalid-request", commandType: typeof input.commandType === "string" && COMMAND_TYPE.test(input.commandType) ? input.commandType : "malformed", }; } function receipt(surface: CaseSurface, command: MainMutationCommand | null, result: MainMutationResult): SurfaceReceipt { const identity = command ? { requestId: command.requestId, commandType: command.commandType } : commandIdentity(result); return { schemaVersion: CORRECTIVE_CASE_SCHEMA, surface, requestId: identity.requestId, commandType: identity.commandType, context: command?.context ?? null, result, }; } async function runSurface(surface: CaseSurface, options: SurfaceCaseOptions): Promise { const before = nonNegativeInteger(options.mainRevision, "mainRevision"); let command: MainMutationCommand; try { command = validateCommand(options.command); } catch (error) { const identity = commandIdentity(options.command); const malformed = failure(null, "MALFORMED", before, error instanceof Error ? error.message : String(error)); malformed.requestId = identity.requestId; return receipt(surface, null, malformed); } if (command.baseRevision !== before) return receipt(surface, command, failure(command, "BLOCKED", before, "base revision does not match current Main")); if (command.context.editMode !== "EDITABLE" || command.context.authorization === "NONE") { return receipt(surface, command, failure(command, "BLOCKED", before, "context cannot authorize a Main mutation", "INVALID_CONTEXT")); } if (command.context.source.kind === "EXTERNAL" && (!command.context.resourceProvider || command.context.resourceProvider === "NONE")) { return receipt(surface, command, failure(command, "EXTERNAL_REQUIRED", before, "external source requires a resource provider")); } try { return receipt(surface, command, normalizeResult(await options.execute(command), command, before)); } catch (error) { const message = error instanceof Error ? error.message : String(error); const code = error instanceof CorrectiveCaseRunnerError ? error.code : "COMMAND_UNSUPPORTED"; const status = code === "RESOURCE_REQUIRED" ? "EXTERNAL_REQUIRED" : code === "CANCELLED" ? "CANCELLED" : code === "REVISION_CONFLICT" || code === "INVALID_CONTEXT" ? "BLOCKED" : code === "COMMAND_MALFORMED" ? "MALFORMED" : "UNSUPPORTED"; return receipt(surface, command, failure(command, status, before, message, code === "INVALID_CONTEXT" ? code : STATUS_CODES[status])); } } export function runDesktopCase(options: SurfaceCaseOptions): Promise { return runSurface("DESKTOP", options); } export function runWasmCase(options: SurfaceCaseOptions): Promise { return runSurface("WASM", options); } function coreReceipt(receiptValue: SurfaceReceipt): Record { const result = receiptValue.result; return { requestId: receiptValue.requestId, commandType: receiptValue.commandType, status: result.status, code: result.code, baseRevision: result.baseRevision, mainRevisionBefore: result.mainRevisionBefore, mainRevisionAfter: result.mainRevisionAfter, mainMutation: result.mainMutation, delta: result.delta, persistence: result.persistence, }; } function stable(value: unknown): unknown { if (Array.isArray(value)) return value.map(stable); if (record(value)) return Object.fromEntries(Object.keys(value).sort().map((key) => [key, stable(value[key])])); return value; } export function compareReceipts(desktop: SurfaceReceipt, wasm: SurfaceReceipt): { parity: "MATCH" | "MISMATCH"; differences: string[] } { const left = coreReceipt(desktop); const right = coreReceipt(wasm); const differences = Object.keys(left).filter((key) => JSON.stringify(stable(left[key])) !== JSON.stringify(stable(right[key]))).map((key) => `${key}: desktop=${JSON.stringify(stable(left[key]))} wasm=${JSON.stringify(stable(right[key]))}`); return { parity: differences.length === 0 ? "MATCH" : "MISMATCH", differences }; } export async function runSharedCase(command: unknown, mainRevision: number, executors: { desktop: SurfaceCaseOptions["execute"]; wasm: SurfaceCaseOptions["execute"] }): Promise { const desktop = await runDesktopCase({ command, mainRevision, execute: executors.desktop }); const wasm = await runWasmCase({ command, mainRevision, execute: executors.wasm }); const comparison = compareReceipts(desktop, wasm); return { schemaVersion: CORRECTIVE_CASE_SCHEMA, requestId: desktop.requestId, commandType: desktop.commandType, desktop, wasm, ...comparison, }; }