import assert from "node:assert/strict"; import { MemoryStorage, WorkspaceJournal } from "../workspace/journal.mjs"; import { createWorkspacePackage } from "../workspace/migration.mjs"; import { ReplayTimeline, fnv1a64 } from "./replay.mjs"; import { RecoveryRuntime } from "./recovery-runtime.mjs"; const contextId = "linuxcnc-2.10.0-pre0-wasm-m1-py-c1"; const timeline = new ReplayTimeline(); timeline.appendEvent(1, 0, "SESSION_READY", {}); timeline.checkpoint(1, 0, { tick: 0, position: 0 }); const journalStorage = new MemoryStorage(); const journal = new WorkspaceJournal(journalStorage, { projectId: "recovery-s3" }); journal.prepare("commit-1", [{ path: "config/main.ini", data: "[EMC]\n" }]); journal.commit("commit-1"); const runtime = new RecoveryRuntime({ contextId }); const saved = runtime.saveCheckpoint({ timeline, state: { tick: 0, position: 0 }, operations: [{ operation_id: "pending", status: "awaiting_ack", seq: 1, kind: "SIM_STEP" }] }); assert.equal(saved.status, "verified"); assert.equal(runtime.markCrash().status, "verified"); const events = [{ seq: 2, tick: 4, kind: "SIM_STEP", payload: { cycles: 4 }, payload_hash: fnv1a64({ cycles: 4 }) }]; const recovered = runtime.restart({ events, reconcile: (operation) => ({ ...operation, status: "aborted" }), apply: (state, event) => ({ tick: event.tick, position: state.position + event.payload.cycles }) }); assert.equal(recovered.status, "verified"); assert.equal(recovered.recovery.status, "aborted"); assert.equal(recovered.recovery.state.position, 4); assert.equal(recovered.recovery.state_hash, fnv1a64(recovered.recovery.state)); const packageValue = createWorkspacePackage({ contextId, timeline: new ReplayTimeline({ schema: "replay-timeline.v1", events: { schema: "event-log.v1", cursor: 1, tick: 0, events: timeline.events.events.slice(0, 1) }, snapshots: { schema: "snapshot.v1", snapshots: [timeline.snapshots.latest()] } }), checkpoint: saved.checkpoint, journal: journal.records(), committedSeq: 1 }); assert.equal(runtime.createOffline(packageValue, [{ path: "ui/index.html", data: "
offline
" }, { path: "wasm/sim.wasm", data: "wasm" }]).status, "verified"); assert.equal(runtime.verifyOffline([{ path: "ui/index.html", data: "
offline
" }, { path: "wasm/sim.wasm", data: "wasm" }], { workspacePackage: packageValue }).result.status, "ready"); assert.equal(runtime.verifyOffline(["ui/index.html"], { workspacePackage: packageValue, online: true }).result.status, "network_required"); assert.equal(runtime.createOffline(packageValue, [{ path: "../escape", data: "bad" }]).code, "offline_resource_path"); const quotaJournal = new WorkspaceJournal(new MemoryStorage(), { projectId: "quota", maxBytes: 3 }); assert.throws(() => quotaJournal.prepare("too-large", [{ path: "a", data: "1234" }]), /workspace_journal_quota/); const restored = new RecoveryRuntime({ contextId }); assert.deepEqual(restored.restore(runtime.export()), runtime.export()); console.log(JSON.stringify({ schema: "recovery-s3-sample.v1", status: "verified", crash_count: runtime.state.crash_count, restart_count: runtime.state.restart_count, recovery_hash: runtime.state.recovery_hash, package_hash: runtime.state.package_hash, state_hash: runtime.export().state_hash }));