// every worker's writes come through here, and a run is only a few thousand frames const KEPT = 50_000; export class Frames { private records = new Map(); private kept = 0; protected forgotUpTo = -1; private waiting = new Set<() => void>(); seen(uri: string) { return this.records.get(uri) ?? []; } // wake on the next frame so a check passes the moment it lands changed(ms: number) { return new Promise((wake) => { const done = () => { clearTimeout(timer); this.waiting.delete(done); wake(); }; const timer = setTimeout(done, ms); this.waiting.add(done); }); } protected wake() { for (const wake of this.waiting) wake(); } protected append(uri: string, frame: T) { const seen = this.seen(uri); seen.push(frame); this.records.set(uri, seen); this.kept++; while (this.kept > KEPT) { const [oldest, dropped] = this.records.entries().next().value!; this.records.delete(oldest); this.kept -= dropped.length; this.forgotUpTo = Math.max(this.forgotUpTo, dropped.at(-1)!.id); } } }