// every worker's writes come through here, and a run is only a few thousand frames
const KEPT = 50_000;
export class Frames<T extends { id: number }> {
private records = new Map<string, T[]>();
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<void>((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);
}
}
}