71 lines
2.8 KiB
TypeScript
71 lines
2.8 KiB
TypeScript
// The part of the fixture world that MOVES. Reads and the stream have to agree about it, otherwise
|
|
// the fixture models a server the contract forbids: one that answers a read with a revision lower
|
|
// than a frame it has already emitted. That fixture existed here, and it hid a real client defect —
|
|
// a refetch on focus rolled the live progress backwards, and the fixture could not tell us.
|
|
|
|
import { book } from './book';
|
|
|
|
const initialEta = book.progress.eta_seconds ?? null;
|
|
|
|
/**
|
|
* Seconds per unit, taken from the hand-written book's own numbers: its estimate covers the units it
|
|
* has left. A book of another size gets an estimate of its own from this — a fixture that handed a
|
|
* twelve-unit book the two-and-a-half hours of a 4276-unit one was printing a number nothing in it
|
|
* supported (found by the adversarial review).
|
|
*/
|
|
const secondsPerUnit =
|
|
(initialEta ?? 0) / Math.max(1, book.progress.draft.total - book.progress.draft.done);
|
|
|
|
export const live = {
|
|
revision: 1841,
|
|
draftDone: book.progress.draft.done,
|
|
draftTotal: book.progress.draft.total,
|
|
etaSeconds: initialEta,
|
|
/** What the estimate was when the run began; the one below is derived from it and the progress. */
|
|
etaAtStart: initialEta,
|
|
};
|
|
|
|
/**
|
|
* A run that starts from nothing — a book just uploaded and just parsed. The counters belong to the
|
|
* run and not to the fixture that was written by hand, so a world where the book arrives from the
|
|
* form has to be able to set them.
|
|
*/
|
|
export function begin(draftTotal: number): void {
|
|
live.draftDone = 0;
|
|
live.draftTotal = draftTotal;
|
|
live.etaAtStart = Math.round(draftTotal * secondsPerUnit);
|
|
live.etaSeconds = live.etaAtStart;
|
|
}
|
|
|
|
/** One burst of the run. Counters JUMP: the contract lets the server coalesce frames. */
|
|
export function advance(step: number): void {
|
|
live.revision += 3;
|
|
live.draftDone = Math.min(live.draftTotal, live.draftDone + 7 + step * 4);
|
|
// Derived from the progress rather than counted down by a constant: an estimate that falls as the
|
|
// work is done is what the field means, and it then fits a book of any size.
|
|
live.etaSeconds =
|
|
live.etaAtStart === null
|
|
? null
|
|
: Math.max(0, Math.round(live.etaAtStart * (1 - live.draftDone / live.draftTotal)));
|
|
}
|
|
|
|
/** The book as it stands right now — what a read must answer while the run is going. */
|
|
export function liveBook() {
|
|
return {
|
|
...book,
|
|
progress: {
|
|
draft: { done: live.draftDone, total: live.draftTotal },
|
|
edit: book.progress.edit,
|
|
eta_seconds: live.etaSeconds,
|
|
},
|
|
};
|
|
}
|
|
|
|
/** Tests share one module instance; a run left half-played would leak into the next test. */
|
|
export function rewind(): void {
|
|
live.revision = 1841;
|
|
live.draftDone = book.progress.draft.done;
|
|
live.draftTotal = book.progress.draft.total;
|
|
live.etaSeconds = initialEta;
|
|
live.etaAtStart = initialEta;
|
|
}
|