import { clearPowerOffCheckpoint, clearUiState, loadPowerOffCheckpoint, loadUiState, savePowerOffCheckpoint, saveUiState, } from "./opfs-ui-state.mjs"; import { DEFAULT_MACHINE, machineDefinition } from "../workspace/machine-definitions.mjs"; const UI_COMMANDS = new Set(["UI_STATE_SAVE", "UI_STATE_LOAD", "UI_STATE_CLEAR"]); function object(value) { return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {}; } function resultEnvelope(input, payload) { return { schema_version: 1, kind: "UI_STATE_RESULT", context_id: input.context_id, session_id: input.session_id, seq: input.seq, operation_id: input.operation_id ?? null, payload: { ...payload, command: input.kind, operation_id: input.operation_id ?? null }, }; } export function isUiStateCommand(input) { return UI_COMMANDS.has(input?.kind); } export async function handleWorkerMessage(session, input) { if (isUiStateCommand(input)) { // UI cache is a Worker storage service, not a LinuxCNC command. It never // consumes the WASM protocol sequence or changes task/motion authority. const payload = object(input.payload); const options = { contextId: input.context_id, key: payload.key ?? session.machineDefinition?.id ?? DEFAULT_MACHINE.id }; const result = input.kind === "UI_STATE_SAVE" ? await saveUiState({ ...options, state: payload.state }) : input.kind === "UI_STATE_LOAD" ? await loadUiState(options) : await clearUiState(options); return resultEnvelope(input, result); } let request = input; let recoveryNotice = null; const configuration = object(input.payload).configuration; const requestedMachine = machineDefinition(configuration?.entrypoint); const key = requestedMachine?.id ?? session.machineDefinition?.id ?? DEFAULT_MACHINE.id; // Automatic restoration belongs to a new Worker session. Explicit INI // changes and machine switches must not be overwritten by saved state. if (input?.kind === "LOAD_WORKSPACE" && !session.machineDefinition && (!configuration?.entrypoint || requestedMachine) && !object(input.payload).recovery_checkpoint) { const persisted = await loadPowerOffCheckpoint({ contextId: input.context_id, key }); if (persisted.status === "verified" && persisted.checkpoint) { try { if (configuration && session.core && typeof session.validateRecoveryGeometry === "function") session.validateRecoveryGeometry(persisted.checkpoint.state, configuration); request = { ...input, payload: { ...object(input.payload), recovery_checkpoint: persisted.checkpoint } }; } catch (error) { recoveryNotice = { status: "ignored", code: String(error?.message ?? error), reason: "saved_checkpoint_incompatible_with_current_configuration" }; } } } const response = session.accept(request); const payload = object(response?.payload); if (recoveryNotice) payload.recovery_persistence = recoveryNotice; if (input?.kind === "POWER" && response?.kind === "COMMAND_RESULT" && payload.status === "verified") { if (payload.active === false && payload.recovery_checkpoint) { const persisted = await savePowerOffCheckpoint({ contextId: input.context_id, checkpoint: payload.recovery_checkpoint, key }); payload.recovery_persistence = persisted; } else if (payload.active === true) { payload.recovery_persistence = await clearPowerOffCheckpoint({ contextId: input.context_id, key }); } } return response; } export { UI_COMMANDS };