| 1 | type RuntimeStatusState = { |
| 2 | runtimeStatusEpoch?: string; |
| 3 | runtimeStatusSeq?: number; |
| 4 | runtimeStatusSnapshotAt?: number; |
| 5 | }; |
| 6 | type RuntimeStatusSnapshot = { |
| 7 | runtimeEpoch?: string; |
| 8 | turnEventSeq?: number; |
| 9 | snapshotAt?: number; |
| 10 | }; |
| 11 | |
| 12 | export function runtimeStatusSnapshotIsStale(state: RuntimeStatusState, snapshot: RuntimeStatusSnapshot): boolean { |
| 13 | const incomingEpoch = snapshot.runtimeEpoch?.trim(); |
| 14 | const storedEpoch = state.runtimeStatusEpoch?.trim(); |
| 15 | if (incomingEpoch && storedEpoch && incomingEpoch !== storedEpoch) return false; |
| 16 | if (snapshot.turnEventSeq === undefined || state.runtimeStatusSeq === undefined) return false; |
| 17 | if (snapshot.turnEventSeq < state.runtimeStatusSeq) return true; |
| 18 | // A rejected optimistic send need not append a backend event. A fresh read |
| 19 | // at the same sequence can reconcile it; duplicate and out-of-order reads |
| 20 | // cannot override a more recent snapshot. |
| 21 | return snapshot.turnEventSeq === state.runtimeStatusSeq && (snapshot.snapshotAt === undefined || |
| 22 | (state.runtimeStatusSnapshotAt !== undefined && snapshot.snapshotAt <= state.runtimeStatusSnapshotAt)); |
| 23 | } |
| 24 |