export interface CaptureClock { now(): number; } export interface CaptureClockSources { wallNow: () => number; monotonicNow: () => number; } /** * Create an epoch-millisecond clock that advances from a monotonic source. * * Date.now() establishes the epoch once. Subsequent reads use the monotonic * delta, so an operating-system clock correction cannot move captured * evidence backwards or introduce a discontinuity between an event and media. */ export function createCaptureClock(sources: CaptureClockSources): CaptureClock { const epochAnchorMs = sources.wallNow(); const monotonicAnchorMs = sources.monotonicNow(); return { now: () => Math.round(epochAnchorMs + (sources.monotonicNow() - monotonicAnchorMs)), }; } function monotonicNow(): number { return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now(); } /** One shared browser clock for events, recordings, and snapshots. */ export const systemCaptureClock = createCaptureClock({ wallNow: Date.now, monotonicNow, });