Move panel temporary state to worker OPFS

This commit is contained in:
2026-09-03 14:03:32 +08:00
parent 0f6691a9fc
commit 2b1e249baf
22 changed files with 688 additions and 42 deletions

View File

@@ -1,5 +1,4 @@
import { CONTEXT_ID, PROTOCOL_SCHEMA, isEnvelope, type MessageEnvelope } from "./protocol";
import { attachPersistedPowerOffCheckpoint, clearPersistedPowerOffCheckpoint, persistPowerOffCheckpoint } from "./power-off-checkpoint-storage";
type Listener = (message: MessageEnvelope) => void;
@@ -20,6 +19,7 @@ export class ReactWorkerClient {
private readonly contextId: string;
private readonly sessionId: string;
private sequence = 0;
private uiSequence = 2000000000;
private listeners = new Set<Listener>();
private pending = new Map<number, { resolve: (message: MessageEnvelope) => void; reject: (error: Error) => void }>();
private readyPromise: Promise<void> | null = null;
@@ -32,10 +32,8 @@ export class ReactWorkerClient {
this.sessionId = sessionId;
this.worker.onmessage = (event: MessageEvent<unknown>) => {
if (!isEnvelope(event.data)) return;
const persistence = persistPowerOffCheckpoint(event.data);
if (persistence && event.data.payload && typeof event.data.payload === "object") Object.assign(event.data.payload, { recovery_persistence: persistence });
clearPersistedPowerOffCheckpoint(event.data);
for (const listener of this.listeners) listener(event.data);
const uiStateResponse = event.data.kind === "UI_STATE_RESULT";
if (!uiStateResponse) for (const listener of this.listeners) listener(event.data);
const pending = this.pending.get(event.data.seq);
if (pending) {
this.pending.delete(event.data.seq);
@@ -61,11 +59,20 @@ export class ReactWorkerClient {
subscribe(listener: Listener): () => void { this.listeners.add(listener); return () => this.listeners.delete(listener); }
send(kind: string, payload: unknown = {}, operationId?: string): Promise<MessageEnvelope> {
const nextPayload = kind === "LOAD_WORKSPACE" ? attachPersistedPowerOffCheckpoint(payload) : payload;
const message: MessageEnvelope = { schema_version: PROTOCOL_SCHEMA, kind, context_id: this.contextId, session_id: this.sessionId, seq: ++this.sequence, ...(operationId ? { operation_id: operationId } : {}), payload: nextPayload };
const message: MessageEnvelope = { schema_version: PROTOCOL_SCHEMA, kind, context_id: this.contextId, session_id: this.sessionId, seq: ++this.sequence, ...(operationId ? { operation_id: operationId } : {}), payload };
return new Promise((resolve, reject) => { this.pending.set(message.seq, { resolve, reject }); this.worker.postMessage(message); });
}
private sendUiState(kind: "UI_STATE_SAVE" | "UI_STATE_LOAD" | "UI_STATE_CLEAR", payload: Record<string, unknown>): Promise<MessageEnvelope> {
const seq = ++this.uiSequence;
const message: MessageEnvelope = { schema_version: PROTOCOL_SCHEMA, kind, context_id: this.contextId, session_id: this.sessionId, seq, operation_id: `ui-cache-${seq}`, payload };
return new Promise((resolve, reject) => { this.pending.set(seq, { resolve, reject }); this.worker.postMessage(message); });
}
saveUiState(state: Record<string, unknown>, key = "xyzac-trt"): Promise<MessageEnvelope> { return this.sendUiState("UI_STATE_SAVE", { key, state }); }
loadUiState(key = "xyzac-trt"): Promise<MessageEnvelope> { return this.sendUiState("UI_STATE_LOAD", { key }); }
clearUiState(key = "xyzac-trt"): Promise<MessageEnvelope> { return this.sendUiState("UI_STATE_CLEAR", { key }); }
hello(): Promise<MessageEnvelope> { return this.send("HELLO", { supported_schema_versions: [PROTOCOL_SCHEMA], requested_features: ["snapshot", "trajectory"] }); }
attach(): Promise<MessageEnvelope> { return this.send("SESSION_ATTACH", {}, "react-session-attach"); }
ensureSession(): Promise<void> {