Files
linuxcncWebWASM/web/worker/xyzac-command-ledger.mjs

55 lines
2.0 KiB
JavaScript

import { OperationLedger, LEDGER_SCHEMA, LEDGER_PHASES } from "./operation-ledger.mjs";
import { fnv1a64 } from "./replay.mjs";
export const XYZAC_COMMAND_LEDGER_SCHEMA = "xyzac-command-ledger.v1";
const TERMINAL_PHASES = new Set(["committed", "aborted", "failed"]);
export class XyzacCommandLedger extends OperationLedger {
constructor(entries = []) {
super(entries);
this.scope = "selected_xyzac_simulation";
}
prepare(input) {
if (["HELLO", "CAPABILITIES", "SNAPSHOT_REQUEST", "CANCEL"].includes(input?.kind)) return { status: "untracked", replay: false, entry: null };
const result = super.prepare(input);
if (result.entry) {
result.entry.scope = this.scope;
result.entry.intent_hash = result.entry.intent_hash ?? fnv1a64({ kind: input.kind, payload: input.payload ?? {}, operation_id: input.operation_id });
}
return result;
}
acknowledge(operationId, response) {
return this.verify(operationId, response);
}
result(operationId, response, seq) {
this.acknowledge(operationId, response);
return this.commit(operationId, seq);
}
pending() {
return [...this.entries.values()]
.filter((entry) => !TERMINAL_PHASES.has(entry.phase))
.sort((left, right) => left.seq - right.seq)
.map((entry) => structuredClone(entry));
}
reconcileAll() {
return this.pending().map((entry) => ({ operation_id: entry.operation_id, status: entry.phase, entry }));
}
timeoutReconcile(operationId) {
const reconciliation = this.reconcile(operationId);
return { status: "reconcile_required", operation_id: operationId, retry_allowed: false, reconciliation };
}
audit() {
const exported = super.export();
const pending = this.pending();
const unsigned = { schema: XYZAC_COMMAND_LEDGER_SCHEMA, operation_schema: LEDGER_SCHEMA, phases: [...LEDGER_PHASES], scope: this.scope, entries: exported.entries, pending_count: pending.length };
return { ...unsigned, state_hash: fnv1a64(unsigned) };
}
}