| 1 | import type { RuntimeProjection, RuntimeState, createRuntimeStateStore } from "./runtimeStateStore"; |
| 2 | |
| 3 | function validState(state: RuntimeState) { |
| 4 | if (!state || state.schemaVersion !== 1) return Boolean(state); |
| 5 | return Boolean(state.runtimeEpoch) && Number.isSafeInteger(state.revision) && state.revision > 0 |
| 6 | && ["idle", "executing", "finishing", "closed"].includes(state.phase) |
| 7 | && [state.running, state.pendingPrompt, state.cancelRequested, state.cancellable].every(value => typeof value === "boolean") |
| 8 | && Number.isSafeInteger(state.backgroundJobs) && state.backgroundJobs >= 0; |
| 9 | } |
| 10 | |
| 11 | export function acceptRuntimeState(store: ReturnType<typeof createRuntimeStateStore>, next: RuntimeProjection, authoritative = false): "accepted" | "duplicate" | "stale" | "conflict" { |
| 12 | const snapshot = store.getSnapshot(); |
| 13 | if (!next?.epoch || !Number.isSafeInteger(next.revision) || !Array.isArray(next.sessions) || !Array.isArray(next.topics)) return "stale"; |
| 14 | if (snapshot && snapshot.epoch !== next.epoch && !authoritative) return "conflict"; |
| 15 | if (snapshot?.epoch === next.epoch) { |
| 16 | if (next.revision < snapshot.revision) return "stale"; |
| 17 | if (next.revision === snapshot.revision) { |
| 18 | if (JSON.stringify(next) !== JSON.stringify(snapshot)) return "conflict"; |
| 19 | store.commit(snapshot); |
| 20 | return "duplicate"; |
| 21 | } |
| 22 | } |
| 23 | if (next.sessions.some(session => !validState(session?.state))) return "conflict"; |
| 24 | // Preserve per-session identity across unrelated updates. |
| 25 | const old = new Map(snapshot?.sessions.map(session => [`${session.tabId}\0${session.sessionPath}`, session])); |
| 26 | const committed = { ...next, topics: structuredClone(next.topics), sessions: next.sessions.map(session => { |
| 27 | const previous = old.get(`${session.tabId}\0${session.sessionPath}`); |
| 28 | return previous && JSON.stringify(previous) === JSON.stringify(session) ? previous : Object.freeze({ ...session, state: Object.freeze({ ...session.state }) }); |
| 29 | }) }; |
| 30 | store.commit(committed); |
| 31 | return "accepted"; |
| 32 | } |
| 33 |