import assert from "node:assert/strict"; import { clearUiState, loadPowerOffCheckpoint, loadUiState, savePowerOffCheckpoint, saveUiState } from "./opfs-ui-state.mjs"; class FakeAccess { constructor(file) { this.file = file; } truncate(size) { this.file.bytes = this.file.bytes.slice(0, size); } write(bytes, { at = 0 } = {}) { const next = new Uint8Array(Math.max(this.file.bytes.length, at + bytes.byteLength)); next.set(this.file.bytes); next.set(bytes, at); this.file.bytes = next; return bytes.byteLength; } flush() {} close() {} } class FakeFile { constructor() { this.bytes = new Uint8Array(); } async getFile() { const bytes = this.bytes; return { text: async () => new TextDecoder().decode(bytes) }; } async createSyncAccessHandle() { return new FakeAccess(this); } } class FakeDirectory { constructor() { this.directories = new Map(); this.files = new Map(); } async getDirectoryHandle(name, { create = false } = {}) { if (!this.directories.has(name)) { if (!create) { const error = new Error("not found"); error.name = "NotFoundError"; throw error; } this.directories.set(name, new FakeDirectory()); } return this.directories.get(name); } async getFileHandle(name, { create = false } = {}) { if (!this.files.has(name)) { if (!create) { const error = new Error("not found"); error.name = "NotFoundError"; throw error; } this.files.set(name, new FakeFile()); } return this.files.get(name); } async removeEntry(name) { if (!this.files.delete(name) && !this.directories.delete(name)) { const error = new Error("not found"); error.name = "NotFoundError"; throw error; } } } class FakeStorage { constructor() { this.root = new FakeDirectory(); } async getDirectory() { return this.root; } } const storage = new FakeStorage(); const contextId = "linuxcnc-2.10.0-pre0-wasm-m1-py-c1"; const first = await saveUiState({ storage, contextId, key: "xyzac-trt", state: { view: "p", zoom: 1, program: { path: "demo.ngc", source: "G21\nM2\n" } } }); assert.equal(first.status, "verified"); assert.equal(first.storage, "opfs"); assert.equal((await loadUiState({ storage, contextId, key: "xyzac-trt" })).state.view, "p"); const second = await saveUiState({ storage, contextId, key: "xyzac-trt", state: { view: "z", zoom: 1.2 } }); assert.equal(second.generation, 2); assert.equal((await loadUiState({ storage, contextId, key: "xyzac-trt" })).state.view, "z"); const root = storage.root.directories.get("linuxcnc-web-wasm"); const project = root.directories.get("ui-state").directories.get("xyzac-trt"); project.files.get("state-a.json").bytes = new TextEncoder().encode("{corrupt"); const fallback = await loadUiState({ storage, contextId, key: "xyzac-trt" }); assert.equal(fallback.status, "verified"); assert.equal(fallback.state.view, "p"); const checkpoint = { schema: "xyzac-power-off-checkpoint.v1", simulation_tick: 4, state: { program: { source_text: "G21\nM2\n" } }, state_hash: "test" }; assert.equal((await savePowerOffCheckpoint({ storage, contextId, checkpoint })).status, "verified"); assert.deepEqual((await loadPowerOffCheckpoint({ storage, contextId })).checkpoint, checkpoint); assert.equal((await clearUiState({ storage, contextId, key: "xyzac-trt" })).status, "verified"); assert.equal((await loadUiState({ storage, contextId, key: "xyzac-trt" })).status, "empty"); console.log(JSON.stringify({ schema: "opfs-ui-state.v1", status: "verified", storage: "dedicated_worker_fake_opfs", fallback_after_corruption: true, power_off_checkpoint: true }));