// Ordering rules remain plugin-private because revisions are vendor runtime state. import type { XgridsCameraPreviewState, XgridsK1State } from "./api"; function monotonicInteger(value: number | null | undefined): number | null { return typeof value === "number" && Number.isInteger(value) && value >= 0 ? value : null; } function deviceSessionScope(state: XgridsK1State): string | null { const sessionId = state.device_session?.device_session_id; return typeof sessionId === "string" && sessionId.trim() ? sessionId : null; } interface RuntimeSnapshotStamp { startedAtMonotonicNs: bigint | null; startedAtEpochMs: number | null; runtimeId: string; revision: number; } function monotonicNanoseconds(value: string | null | undefined): bigint | null { if (typeof value !== "string" || !/^(0|[1-9][0-9]*)$/.test(value)) return null; try { return BigInt(value); } catch { return null; } } function runtimeSnapshotStamp(state: XgridsK1State): RuntimeSnapshotStamp | null { const startedAt = state.snapshot_runtime_started_at_utc; const startedAtMonotonicNs = monotonicNanoseconds( state.snapshot_runtime_started_monotonic_ns, ); const runtimeId = state.snapshot_runtime_id; const revision = monotonicInteger(state.snapshot_revision); if ( typeof runtimeId !== "string" || !runtimeId.trim() || revision === null ) { return null; } const parsedEpochMs = typeof startedAt === "string" ? Date.parse(startedAt) : Number.NaN; const startedAtEpochMs = Number.isFinite(parsedEpochMs) ? parsedEpochMs : null; if (startedAtMonotonicNs === null && startedAtEpochMs === null) return null; return { startedAtMonotonicNs, startedAtEpochMs, runtimeId, revision }; } function stampedSnapshotIsAtLeastAsNew( current: RuntimeSnapshotStamp, incoming: RuntimeSnapshotStamp, ): boolean { if (incoming.runtimeId === current.runtimeId) { return incoming.revision >= current.revision; } if (incoming.startedAtMonotonicNs !== null || current.startedAtMonotonicNs !== null) { if (incoming.startedAtMonotonicNs === null) return false; if (current.startedAtMonotonicNs === null) return true; return incoming.startedAtMonotonicNs > current.startedAtMonotonicNs; } if ( incoming.startedAtEpochMs !== null && current.startedAtEpochMs !== null && incoming.startedAtEpochMs !== current.startedAtEpochMs ) { return incoming.startedAtEpochMs > current.startedAtEpochMs; } // Legacy UTC-only process identities with equal timestamps cannot be // ordered safely. Keep the already accepted authority. return false; } function cameraSnapshotIsAtLeastAsNew( current: XgridsCameraPreviewState, incoming: XgridsCameraPreviewState, ): boolean { const currentRevision = monotonicInteger(current.revision); const incomingRevision = monotonicInteger(incoming.revision); if (currentRevision !== null || incomingRevision !== null) { if (incomingRevision === null) return false; if (currentRevision === null) return true; if (incomingRevision !== currentRevision) return incomingRevision > currentRevision; } const currentGeneration = monotonicInteger(current.generation); const incomingGeneration = monotonicInteger(incoming.generation); if (currentGeneration === null) return true; if (incomingGeneration === null) return false; return incomingGeneration >= currentGeneration; } /** * Select an authoritative runtime snapshot without allowing asynchronous REST, * mutation, or event responses to roll camera preview state backwards. * * Revision is the primary ordering key. In particular, a stop snapshot with a * newer revision and a null generation is valid and must replace the active * generation. Generation is only a tie-breaker when revisions are equal or * absent. */ export function selectMonotonicXgridsState( current: XgridsK1State | null, incoming: XgridsK1State, ): XgridsK1State { if (!current) return incoming; const currentStamp = runtimeSnapshotStamp(current); const incomingStamp = runtimeSnapshotStamp(incoming); if (currentStamp || incomingStamp) { if (!currentStamp) return incoming; if (!incomingStamp) return current; return stampedSnapshotIsAtLeastAsNew(currentStamp, incomingStamp) ? incoming : current; } if (current && deviceSessionScope(current) !== deviceSessionScope(incoming)) { return incoming; } if (!current?.camera_preview) return incoming; if (!incoming.camera_preview) return current; // Backend snapshots are atomic. Reject the whole stale response instead of // combining camera/catalog/metrics fields captured at different moments. return cameraSnapshotIsAtLeastAsNew(current.camera_preview, incoming.camera_preview) ? incoming : current; }