textmachine/frontend/src/api/revision.ts

31 lines
1.4 KiB
TypeScript

// The stream half of the revision discipline; the READ half is `freshest()` in `queries.ts`.
//
// The split is deliberate. A frame has nothing to compare itself against, so its high-water mark
// has to be kept here. A read does — the query cache, which the stream patches — and keeping a
// second copy of it here was how progress rolled backwards: the copy went stale the moment a frame
// landed, and a read was compared against the copy instead of against what was on screen.
// One entry per book being watched, holding a number. Dropped when the subscription closes.
const applied = new Map<string, number>();
/**
* Whether a stream frame carries anything new. Equal revisions pass: one transaction is one
* revision but SEVERAL frames, and rejecting equality would drop the siblings of the frame already
* applied — the same reason catch-up reads `>=` and not `>`.
*/
export function acceptFrame(scope: string, revision: number): boolean {
const last = applied.get(scope);
if (last !== undefined && revision < last) return false;
applied.set(scope, revision);
return true;
}
/** Drops the mark for a scope — after a resync there is nothing to compare against. */
export function forget(scope: string): void {
applied.delete(scope);
}
/** Test seam: the registry is module state, and a leftover revision would leak between tests. */
export function forgetAll(): void {
applied.clear();
}