import type { CatchUpWindow } from "./chat-utils"; type Page = { messages: T[]; next: string; through: string; hasMore: boolean }; export function createMutationRecovery( fetchPage: (cursor: CatchUpWindow) => Promise>, apply: (messages: T[]) => void, initialCursor: CatchUpWindow = { after: "0" }, onCursor?: (cursor: CatchUpWindow) => void, ) { let cursor: CatchUpWindow = { ...initialCursor }; let stopped = false; let active: Promise | undefined; let requested = false; async function drain() { do { requested = false; let more: boolean; do { const page = await fetchPage({ ...cursor }); if (stopped) return; if ( BigInt(page.through) < BigInt(cursor.after) || BigInt(page.next) < BigInt(cursor.after) || BigInt(page.next) > BigInt(page.through) || (cursor.through !== undefined && cursor.through !== page.through) || (page.hasMore && BigInt(page.next) <= BigInt(cursor.after)) ) throw new Error("Invalid mutation recovery window"); apply(page.messages); cursor = page.hasMore ? { after: page.next, through: page.through } : { after: page.through }; onCursor?.({ ...cursor }); more = page.hasMore; } while (more && !stopped); } while (requested && !stopped); } return { run(): Promise { if (stopped) return Promise.resolve(); if (active) { requested = true; return active; } active = drain().finally(() => { active = undefined; }); return active; }, stop() { stopped = true; }, }; }