31 lines
1.4 KiB
TypeScript
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();
|
|
}
|