| 1 | export function resolveTurnStartedAt(current: number, reported: number | undefined, now = Date.now()): number { |
| 2 | if (typeof reported === "number" && Number.isFinite(reported) && reported > 0 && reported <= now) return reported; |
| 3 | if (Number.isFinite(current) && current > 0 && current <= now) return current; |
| 4 | return now; |
| 5 | } |
| 6 | |
| 7 | // Snapshots may complete out of order, so a live clock can advance but never rewind. |
| 8 | export function resolveSnapshotTurnStartedAt(current: number, reported: number | undefined, now = Date.now()): number { |
| 9 | const resolved = resolveTurnStartedAt(current, reported, now); |
| 10 | if (!Number.isFinite(current) || current <= 0 || current > now) return resolved; |
| 11 | return Math.max(current, resolved); |
| 12 | } |
| 13 | |
| 14 | // Both values come from the same monotonic clock; ties are conservatively stale. |
| 15 | export function snapshotPredatesTurnLifecycle(observedAt: number | undefined, snapshotAt: number | undefined): boolean { |
| 16 | if (snapshotAt === undefined || observedAt === undefined) return false; |
| 17 | return snapshotAt <= observedAt; |
| 18 | } |
| 19 |