Submatter/apps/web/components/chat/mutation-recovery.ts

46 lines
1.6 KiB
TypeScript
Raw Normal View History

import type { CatchUpWindow } from "./chat-utils";
type Page<T> = { messages: T[]; next: string; through: string; hasMore: boolean };
export function createMutationRecovery<T>(
fetchPage: (cursor: CatchUpWindow) => Promise<Page<T>>,
apply: (messages: T[]) => void,
initialCursor: CatchUpWindow = { after: "0" },
onCursor?: (cursor: CatchUpWindow) => void,
) {
let cursor: CatchUpWindow = { ...initialCursor };
let stopped = false;
let active: Promise<void> | 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<void> {
if (stopped) return Promise.resolve();
if (active) { requested = true; return active; }
active = drain().finally(() => { active = undefined; });
return active;
},
stop() { stopped = true; },
};
}