gesso-framework 0.6.8 → 0.6.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/dist/agent/index.d.ts +1 -1
- package/dist/agent/index.js +3 -3
- package/dist/{app-bMsNOs1H.js → app-6S4_PPB0.js} +2 -2
- package/dist/{app-bMsNOs1H.js.map → app-6S4_PPB0.js.map} +1 -1
- package/dist/{index-D5bW2GUt.d.ts → index-oHQAJMqR.d.ts} +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +7 -7
- package/dist/{persisted-i6TIWX8R.js → persisted-IXHP0d-X.js} +2 -2
- package/dist/{persisted-i6TIWX8R.js.map → persisted-IXHP0d-X.js.map} +1 -1
- package/dist/remote/index.d.ts +2 -2
- package/dist/remote/index.js +1 -1
- package/dist/{remote-gRdwYo-q.js → remote-BEGRJ1V3.js} +24 -5
- package/dist/remote-BEGRJ1V3.js.map +1 -0
- package/dist/{serveChannels-Cs22-p8X.js → serveChannels-CFzq0Hz3.js} +2 -2
- package/dist/{serveChannels-Cs22-p8X.js.map → serveChannels-CFzq0Hz3.js.map} +1 -1
- package/dist/{ui-Bs6ywsAA.d.ts → ui-e4RHm8Mt.d.ts} +10 -2
- package/dist/{webmcp-F3OOy73w.js → webmcp-Dsp4W-pi.js} +2 -2
- package/dist/{webmcp-F3OOy73w.js.map → webmcp-Dsp4W-pi.js.map} +1 -1
- package/dist/worker/index.d.ts +2 -2
- package/dist/worker/index.js +2 -2
- package/package.json +2 -2
- package/dist/remote-gRdwYo-q.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persisted-i6TIWX8R.js","names":[],"sources":["../src/Input.ts","../src/InternalState.ts","../src/derive.ts","../src/computed.ts","../src/select.ts","../src/resource.ts","../src/mutate.ts","../src/debounce.ts","../src/channel/pick.ts","../src/storage/StorageAdapter.ts","../src/undo/UndoStack.ts","../src/undo/undoable.ts","../src/storage/OpfsStorage.ts","../src/storage/IndexedDbStorage.ts","../src/storage/persisted.ts"],"sourcesContent":["import { BehaviorSubject, type Observable, Subject } from 'rxjs';\n\n/**\n * Reactive cell holding a component input.\n *\n * Inputs must be cells because render() runs exactly once. A component\n * that read a plain input value during render would capture it for the\n * life of the instance, so a parent supplying new props under a\n * dynamic subtree would update the field while the rendered tree kept\n * showing the original value.\n *\n * The cell is written by the component host only: it accepts whatever\n * the parent passed, subscribing it first when the parent passed an\n * Observable. `value` is deliberately read-only, so `this.label.value =\n * x` inside a component is a compile error rather than a silent\n * violation of the one-way data flow.\n */\nexport class InputCell<T> extends BehaviorSubject<T> {\n /**\n * What to call this cell in a warning: `Component.prop`, or\n * `channel.key`. Set by whoever creates it; a cell without one is\n * reported as \"a cell\".\n */\n label: string | undefined;\n /** The component whose body read `.value`, for the stale-read warning below. */\n private snapshotBy: string | null = null;\n private warnedStale = false;\n /** Everything emitted through `emit`, created on the first `events` read. */\n private emitted: Subject<unknown> | null = null;\n\n constructor(initialValue: T) {\n super(initialValue);\n }\n\n /**\n * Fires the output this cell stands for.\n *\n * An output is a cell whose value is what the parent gave to be\n * called: a function, or a target made by `into(subject)`. `emit`\n * calls it with the arguments, and also pushes the first argument\n * through `events`, so the component can fire from three places\n * without passing the cell around and a parent that wants a stream\n * can have one. A parent that passed nothing is fine: the call\n * simply reaches nobody.\n */\n emit(...args: EmitArgs<T>): void {\n const handler = super.getValue() as unknown;\n if (typeof handler === 'function') {\n (handler as (...values: unknown[]) => void)(...args);\n }\n this.emitted?.next(args[0]);\n }\n\n /** What `emit` has fired, as a stream: the first argument of each call. */\n get events(): Observable<EmitValue<T>> {\n if (this.emitted === null) {\n this.emitted = new Subject<unknown>();\n }\n return this.emitted.asObservable() as Observable<EmitValue<T>>;\n }\n\n override get value(): T {\n trackRead(this);\n // A read a `computed` is collecting is not the body's snapshot: the\n // computed follows this cell from here on and carries the change,\n // and a body that read the computed itself is the computed's own\n // stale watch to report. Recording it here as well warned, wrongly,\n // for a screen row whose `computed` input had let go with the row.\n if (bodyOf !== null && tracking === null && this.snapshotBy === null) {\n this.snapshotBy = bodyOf;\n }\n return super.getValue();\n }\n\n /**\n * The one place the run-once model goes quietly wrong is a body that\n * reads `props.x.value`, uses the value to build the tree, and never\n * hears that it changed. Nothing crashes; the screen is simply stale.\n * So a cell remembers being read while a body ran, and if it later\n * changes with nobody subscribed to it, it says so once. A cell that\n * something is following is fine: the follower carries the change.\n */\n override next(value: T): void {\n if (this.snapshotBy !== null && !this.warnedStale && !this.observed && !Object.is(value, super.getValue())) {\n this.warnedStale = true;\n warnStaleRead(this.snapshotBy, this.label, super.getValue(), value);\n }\n super.next(value);\n }\n}\n\n/** The component whose function body is running, while one is. */\nlet bodyOf: string | null = null;\n\n/** The component whose body is running, for a cell that wants to remember being read there. */\nexport function currentBody(): string | null {\n return bodyOf;\n}\n\n/**\n * Anything with a current value that can be followed: an input, an\n * internal state, a channel view key, a computed. What `computed`\n * collects as it runs.\n */\nexport interface ReadableCell<T> extends Observable<T> {\n readonly value: T;\n}\n\n/** The set a running `computed` is collecting its reads into, while one is. */\nlet tracking: Set<ReadableCell<unknown>> | null = null;\n\n/** Records a `.value` read for whatever `computed` is running, if one is. */\nexport function trackRead(cell: ReadableCell<unknown>): void {\n tracking?.add(cell);\n}\n\n/** Runs `run` with every `.value` read on the way recorded into `into`. */\nexport function withTracking<T>(into: Set<ReadableCell<unknown>>, run: () => T): T {\n const previous = tracking;\n tracking = into;\n try {\n return run();\n } finally {\n tracking = previous;\n }\n}\n\n/**\n * Runs a component's body with its name on record, so a `.value` read\n * inside it can be told apart from one in an event handler later, which\n * is the ordinary way to read the current value and warns about nothing.\n */\nexport function withBodyOf<T>(tag: string, run: () => T): T {\n const previous = bodyOf;\n bodyOf = tag;\n try {\n return run();\n } finally {\n bodyOf = previous;\n }\n}\n\nfunction warnStaleRead(tag: string, label: string | undefined, from: unknown, to: unknown): void {\n const what = label === undefined ? 'a cell' : `\\`${label}\\``;\n const change = describeChange(from, to);\n console.warn(\n `Component '${tag}' read ${what} with .value while its body ran, and nothing is following that cell. ` +\n `It has since changed ${change}, and whatever was built from the first value still shows it. ` +\n `A component body runs once: bind the cell instead (pass it, or pipe it, into the prop it feeds), ` +\n `or give the component a key so a new value builds a new one.`\n );\n}\n\n/**\n * \"from X to Y\", or for two plain objects the first field that differs,\n * because two objects that print alike for sixty characters say nothing\n * about what actually moved.\n */\nfunction describeChange(from: unknown, to: unknown): string {\n if (isPlainObject(from) && isPlainObject(to)) {\n for (const key of new Set([...Object.keys(from), ...Object.keys(to)])) {\n const before = from[key];\n const after = to[key];\n if (!Object.is(before, after) && safeJson(before) !== safeJson(after)) {\n return `at .${key}, from ${describe(before)} to ${describe(after)}`;\n }\n }\n return 'to an equal-looking object';\n }\n return `from ${describe(from)} to ${describe(to)}`;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction safeJson(value: unknown): string {\n try {\n return JSON.stringify(value) ?? String(value);\n } catch {\n return String(value);\n }\n}\n\nfunction describe(value: unknown): string {\n let text: string;\n try {\n text = typeof value === 'function' ? 'a function' : (JSON.stringify(value) ?? String(value));\n } catch {\n text = String(value);\n }\n return text.length > 60 ? `${text.slice(0, 57)}...` : text;\n}\n\n/**\n * Creates a component input cell.\n *\n * Inside a class component the argument is the default the cell holds\n * until the parent supplies a value:\n *\n * @Input() label = input('Count');\n *\n * Inside a functional component the props are already cells; the\n * two-argument form derives a cell that replaces `undefined` with a\n * fallback, which is how an optional prop gets its default:\n *\n * function Counter(inputs: Inputs<{ label?: string }>) {\n * const label = input(inputs.label, 'Count'); // InputCell<string>\n * return Text({ text: label });\n * }\n *\n * The derived cell follows the source for the life of the component\n * and completes when the source does, so it needs no teardown.\n */\nexport function input<T>(initialValue: T): InputCell<T>;\nexport function input<T>(source: InputCell<T | undefined>, fallback: T): InputCell<T>;\nexport function input<T>(first: T | InputCell<T | undefined>, fallback?: T): InputCell<T> {\n if (arguments.length < 2 || !(first instanceof InputCell)) {\n return new InputCell(first as T);\n }\n const source = first as InputCell<T | undefined>;\n const withFallback = (value: T | undefined): T => (value === undefined ? (fallback as T) : value);\n const derived = new InputCell<T>(withFallback(source.value));\n derived.label = source.label;\n source.subscribe({\n next: value => derived.next(withFallback(value)),\n complete: () => derived.complete()\n });\n return derived;\n}\n\n/** The arguments `emit` takes for a cell holding a handler of type `T`. */\nexport type EmitArgs<T> = NonNullable<T> extends (...args: infer A) => void ? A : never;\n\n/** What `events` carries for a cell holding a handler of type `T`: the handler's first argument. */\nexport type EmitValue<T> = NonNullable<T> extends (first: infer V, ...rest: never[]) => void ? V : void;\n\n/**\n * A component's output: an input cell that holds whatever the parent\n * gave to be called, fired through `emit`. Every function-typed member\n * of a component's inputs is one, so on a component declared with\n * `onOpen: (id: string) => void`, `inputs.onOpen.emit(id)` is how the\n * component speaks, and a parent may pass a function or `into(subject)`.\n * The name is for a class field: `@Output() changed = output<[number]>()`.\n */\nexport type OutputCell<A extends unknown[]> = InputCell<((...args: A) => void) | undefined>;\n\n/**\n * An output a class component declares as a field:\n *\n * @Output() changed = output<[value: number]>();\n *\n * The host wires the parent's handler into it exactly as it wires an\n * input, and `this.changed.emit(next)` fires it.\n */\nexport function output<A extends unknown[]>(): OutputCell<A> {\n return new InputCell<((...args: A) => void) | undefined>(undefined);\n}\n\nconst OUTPUT_TARGET: unique symbol = Symbol('gesso:output-target');\n\n/**\n * A parent's way of receiving an output as a stream rather than a call:\n *\n * const opened = new Subject<string>();\n * <Card onOpen={into(opened)} />\n *\n * Wrapped rather than passed bare, because a bare `Subject` is an\n * Observable and would be read as an *input* the parent is feeding the\n * child, which is the opposite direction.\n */\nexport interface OutputTarget<V> {\n readonly [OUTPUT_TARGET]: { next(value: V): void };\n}\n\nexport function into<V>(target: { next(value: V): void }): OutputTarget<V> {\n return { [OUTPUT_TARGET]: target };\n}\n\nexport function isOutputTarget(value: unknown): value is OutputTarget<unknown> {\n return typeof value === 'object' && value !== null && OUTPUT_TARGET in value;\n}\n\n/** The receiver an `into()` target wraps. */\nexport function outputTargetOf<V>(target: OutputTarget<V>): { next(value: V): void } {\n return target[OUTPUT_TARGET];\n}\n","import { BehaviorSubject } from 'rxjs';\nimport { trackRead } from './Input';\n\n/**\n * A component's own state: originated here, and never crossing the\n * barrier.\n *\n * The writable counterpart to `InputCell`. The two are the same\n * `BehaviorSubject` and differ by one accessor — this one has a\n * `.value` setter — and that difference is the whole semantics:\n *\n * internalState() never crosses I write it\n * input() crosses inward someone else writes it\n *\n * Named on that axis deliberately. It used to be `state()`, which\n * described *what* a thing was while `input()` described *where it\n * came from*; two names on two axes made neither of them tell you\n * anything about the other. `internalState(products)` reads as a\n * mistake at the call site in a way `state(products)` never did.\n *\n * For values that originate on this thread and die with the component:\n * a tooltip's open flag, a caret, a scroll offset, the active tab.\n * Anything that survives a reload, or that another screen cares about,\n * is application state and belongs on a channel. Anything derived from\n * other cells is a `computed`.\n *\n * It is not only for components. The thread that owns a channel's data\n * writes cells too, and wrote them as a `BehaviorSubject` mirrored\n * into an `asObservable()` because this was reachable only through the\n * framework's main entry. `gesso-framework/worker` is the same cell\n * with none of the renderer behind it, so an application worker holds\n * one cell rather than a subject and a copy of it, and reads it with\n * `.value` in a `computed` rather than listing it in a\n * `combineLatest`. \"Internal\" still means what it says there: written\n * here, and crossing the barrier only as the plain data a view key\n * publishes.\n */\nexport class InternalState<T> extends BehaviorSubject<T> {\n /** What to call this cell in a warning or the inspector; optional. */\n label: string | undefined;\n constructor(initialValue: T) {\n super(initialValue);\n }\n\n override get value(): T {\n trackRead(this);\n return super.getValue();\n }\n\n override set value(next: T) {\n this.next(next);\n }\n}\n\n/**\n * Creates a reactive state cell.\n *\n * Usage inside a component:\n *\n * private readonly count = state(0);\n *\n * increment() {\n * this.count.value++;\n * }\n */\nexport function internalState<T>(initialValue: T, label?: string): InternalState<T> {\n const state = new InternalState(initialValue);\n if (label !== undefined) {\n state.label = label;\n }\n return state;\n}\n","import { combineLatest, distinctUntilChanged, map, type Observable } from 'rxjs';\n\nimport { structurallyEqual } from './channel/structuralEquals';\n\n/** How `derive` decides that a new value is not a change. */\nexport type Equality<T> = 'reference' | 'structural' | ((a: T, b: T) => boolean);\n\nexport interface DeriveOptions<T> {\n /**\n * `reference` (the default) is `Object.is`; `structural` compares\n * plain data by content, so a projection that builds a fresh object\n * of the same shape does not re-bind everything reading it; a\n * function is your own rule.\n */\n readonly equal?: Equality<T>;\n}\n\ntype Values<S extends readonly Observable<unknown>[]> = {\n [K in keyof S]: S[K] extends Observable<infer V> ? V : never;\n};\n\n/**\n * One value from several, kept equal to `project` of the latest of each\n * source, and emitted only when it changes.\n *\n * This is `combineLatest(...).pipe(map(...), distinctUntilChanged())`,\n * which is what nearly every derived binding in a component body wants\n * and what nearly every one had to write out. Sources are cells or any\n * Observables; the result is what a prop takes.\n *\n * const playing = derive([queue.view.playlistId, audio.state], (id, state) =>\n * id === card.id && state.status === 'playing'\n * );\n *\n * @deprecated Write it as a `computed`, which is the one derivation the\n * documentation teaches. A cell is read with\n * `.value` and anything else through the `read` the function is handed,\n * so the sources are the reads themselves and there is no list beside\n * the expression to keep in step with it:\n *\n * const playing = computed(read =>\n * queue.view.playlistId.value === card.id && read(audio.state).status === 'playing'\n * );\n *\n * It still works and nothing that uses it needs changing today.\n */\nexport function derive<S extends readonly Observable<unknown>[], T>(\n sources: readonly [...S],\n project: (...values: Values<S>) => T,\n options: DeriveOptions<T> = {}\n): Observable<T> {\n const equal = equalityOf(options.equal ?? 'reference');\n return combineLatest(sources as unknown as Observable<unknown>[]).pipe(\n map(values => project(...(values as Values<S>))),\n distinctUntilChanged(equal)\n );\n}\n\n/** The comparison an `Equality` names. Shared with `computed`. */\nexport function equalityOf<T>(equal: Equality<T>): (a: T, b: T) => boolean {\n if (equal === 'reference') {\n return Object.is;\n }\n if (equal === 'structural') {\n return (a, b) => structurallyEqual(a, b);\n }\n return equal;\n}\n","import { Observable, Subject, Subscription } from 'rxjs';\nimport { equalityOf, type Equality } from './derive';\nimport { currentBody, trackRead, withTracking, type ReadableCell } from './Input';\n\nexport interface ComputedOptions<T> {\n /** How a new result is judged unchanged; `reference` by default. */\n readonly equal?: Equality<T>;\n /** What to call this cell in a warning. */\n readonly label?: string;\n}\n\n/**\n * Reads a stream that is not a cell, inside a `computed`.\n *\n * A cell is read with `.value`; a plain Observable has no current value\n * to read, so the function is handed this instead:\n *\n * const playing = computed(read => read(audio.actions) === 'play');\n *\n * It answers with the stream's latest value and records the stream as a\n * source, so the computed follows it exactly as it follows a cell. A\n * cell passed to it is simply read, which means one call site works for\n * either and a service that later turns a stream into a cell breaks\n * nothing.\n */\nexport type ReadSource = <V>(source: Observable<V>) => V;\n\n/**\n * A cell whose value is a function of other cells.\n *\n * `computed(() => quantity.value * price.value)` reads like the value it\n * is. The cells its function reads through `.value` are its sources,\n * found by running the function and watching what it touches, so there\n * is no list to keep in step with the expression; a read the function\n * did not make this time is a source it no longer has.\n *\n * A stream that is not a cell is read through the `read` the function\n * is handed: `computed(read => read(stream).status)` follows the stream\n * as it follows a cell. That is what makes this the only derivation an\n * application needs, whether or not the thing it derives from happens\n * to have a current value of its own.\n *\n * It is a cell and nothing else. Bound to a prop it is an Observable\n * like every other cell, so a component written with it and one written\n * with `pipe` compose without translation. Read in a handler with\n * `.value` it is the current result, computed on the spot if nothing is\n * following it. RxJS is underneath and nothing here replaces it.\n *\n * Nothing runs until someone asks. With no subscriber the function runs\n * only when `.value` is read; with one, the cell follows its sources and\n * emits a result when it differs from the last by `equal`. When the last\n * subscriber leaves it lets go of its sources, so a computed made in a\n * component body dies with the component's bindings and needs no\n * disposal of its own.\n *\n * Reading it with `.value` while a component body runs is the same\n * snapshot an input read there is, and it warns the same way: once, if\n * it later changes with nobody following.\n */\nexport class ComputedCell<T> extends Observable<T> implements ReadableCell<T> {\n label: string | undefined;\n private readonly equal: (a: T, b: T) => boolean;\n private readonly changes = new Subject<T>();\n private sources = new Set<ReadableCell<unknown>>();\n private cached!: T;\n private hasValue = false;\n private upstream: Subscription | null = null;\n private subscribers = 0;\n private attaching = false;\n private snapshotBy: string | null = null;\n private warnedStale = false;\n /** Follows the sources after a body read, only to notice the change the body will not. */\n private staleWatch: Subscription | null = null;\n\n constructor(\n private readonly compute: (read: ReadSource) => T,\n options: ComputedOptions<T> = {}\n ) {\n super(subscriber => {\n this.subscribers++;\n // A follower makes the body-read watch moot: the change reaches the screen.\n this.staleWatch?.unsubscribe();\n this.staleWatch = null;\n if (this.upstream === null) {\n this.attach();\n }\n subscriber.next(this.cached);\n const following = this.changes.subscribe(subscriber);\n return () => {\n following.unsubscribe();\n this.subscribers--;\n if (this.subscribers === 0) {\n this.detach();\n }\n };\n });\n this.equal = equalityOf(options.equal ?? 'reference');\n this.label = options.label;\n }\n\n /** The current result: kept by the sources while followed, computed now when not. */\n get value(): T {\n trackRead(this);\n if (this.upstream === null) {\n this.recompute();\n }\n const body = currentBody();\n if (body !== null && this.snapshotBy === null) {\n this.snapshotBy = body;\n this.watchForStaleRead();\n }\n return this.cached;\n }\n\n /**\n * A body read the value once; if a source now changes with nothing\n * following this cell, the screen built from that read is stale and\n * nobody would know. So the sources are watched for exactly that, and\n * the watch ends with the warning or with a real subscriber arriving.\n */\n private watchForStaleRead(): void {\n this.staleWatch = new Subscription();\n let settling = true;\n for (const source of this.sources) {\n this.staleWatch.add(\n source.subscribe(() => {\n if (settling || this.observed) {\n return;\n }\n if (this.recompute()) {\n this.warnStale();\n this.staleWatch?.unsubscribe();\n this.staleWatch = null;\n }\n })\n );\n }\n settling = false;\n }\n\n private warnStale(): void {\n if (this.snapshotBy === null || this.warnedStale) {\n return;\n }\n this.warnedStale = true;\n console.warn(\n `Component '${this.snapshotBy}' read ${this.label === undefined ? 'a computed cell' : `\\`${this.label}\\``} with .value while its body ran, ` +\n `and nothing is following that cell. It has since changed, and whatever was built from the first value still shows it. ` +\n `A component body runs once: bind the cell instead.`\n );\n }\n\n /** Whether anything is following this cell; the stale-read warning's question. */\n get observed(): boolean {\n return this.subscribers > 0;\n }\n\n /** Runs the function, watching what it reads; true when the result changed. */\n private recompute(): boolean {\n const touched = new Set<ReadableCell<unknown>>();\n const next = withTracking(touched, () => this.compute(readSource));\n this.sources = touched;\n const changed = !this.hasValue || !this.equal(this.cached, next);\n this.hasValue = true;\n this.cached = next;\n return changed;\n }\n\n /** Follows the current sources, re-running on any change to them. */\n private attach(): void {\n this.attaching = true;\n this.recompute();\n this.upstream = new Subscription();\n for (const source of this.sources) {\n this.upstream.add(source.subscribe(() => this.onSourceChanged()));\n }\n this.attaching = false;\n }\n\n private detach(): void {\n this.upstream?.unsubscribe();\n this.upstream = null;\n }\n\n private onSourceChanged(): void {\n if (this.attaching) {\n return; // a cell replays its current value on subscribe; that is not a change\n }\n const before = new Set(this.sources);\n const changed = this.recompute();\n if (!sameSet(before, this.sources)) {\n // The function read different cells this time: follow those.\n this.detach();\n this.attaching = true;\n this.upstream = new Subscription();\n for (const source of this.sources) {\n this.upstream.add(source.subscribe(() => this.onSourceChanged()));\n }\n this.attaching = false;\n }\n if (changed) {\n this.changes.next(this.cached);\n }\n }\n}\n\n/**\n * A cell computed from what its function reads. See `ComputedCell`.\n *\n * const total = computed(() => quantity.value * PRICE * RATES[currency.value]);\n * <text text={computed(() => String(count.value))} />\n *\n * Cells are read with `.value`; anything else is read through the\n * `read` the function is given, which follows it the same way:\n *\n * const late = computed(read => read(clock) > deadline);\n */\nexport function computed<T>(compute: (read: ReadSource) => T, options: ComputedOptions<T> = {}): ComputedCell<T> {\n return new ComputedCell(compute, options);\n}\n\n/**\n * The cell standing for a stream, one per stream.\n *\n * Anything that already has a current value is its own cell, so a\n * `read` of an input, an internal state or another computed costs a\n * property access and nothing more. Everything else gets a `StreamCell`\n * held against it here, so several computeds reading one stream share a\n * single subscription to it rather than opening one each.\n *\n * Weak on purpose: the entry is reachable only while the stream is, so\n * a stream made in a component body is collected with the component.\n */\nconst streamCells = new WeakMap<Observable<unknown>, StreamCell<unknown>>();\n\n/**\n * Whether a source's `.value` announces itself to the running computed,\n * decided once per source and remembered.\n *\n * Having a `value` is not enough. A framework cell records its reads\n * through `trackRead`, which is what lets a computed learn what it\n * depends on; a plain `BehaviorSubject` has a `value` too and records\n * nothing, so a computed that trusted the property would read it once\n * and never hear it change. That is exactly what happened to three\n * data-layer specs that fed a raw subject where the application feeds\n * a channel view. The probe reads `.value` once under a tracking set\n * of its own and asks whether the source turned up in it.\n */\nconst tracksReads = new WeakMap<Observable<unknown>, boolean>();\n\nfunction announcesItsReads(source: ReadableCell<unknown>): boolean {\n let known = tracksReads.get(source);\n if (known === undefined) {\n const seen = new Set<ReadableCell<unknown>>();\n withTracking(seen, () => void source.value);\n known = seen.has(source);\n tracksReads.set(source, known);\n }\n return known;\n}\n\nfunction cellFor<T>(source: Observable<T>): ReadableCell<T> {\n if ('value' in source && announcesItsReads(source as ReadableCell<T>)) {\n return source as ReadableCell<T>;\n }\n let cell = streamCells.get(source as Observable<unknown>);\n if (cell === undefined) {\n cell = new StreamCell(source as Observable<unknown>);\n streamCells.set(source as Observable<unknown>, cell);\n }\n return cell as ReadableCell<T>;\n}\n\n/** The `read` every computed's function is handed. */\nconst readSource: ReadSource = <V>(source: Observable<V>): V => cellFor(source).value;\n\n/**\n * A plain stream, seen as a cell.\n *\n * It holds the last value it saw and hands it to whoever asks, which is\n * the one thing a cell has and an Observable does not. While something\n * follows it, it follows the stream; when the last follower leaves it\n * lets go, so it costs nothing between uses and needs no disposal, on\n * the same terms as `ComputedCell`.\n *\n * A `.value` read with nothing following takes one synchronous\n * subscription and drops it again, which is how a `BehaviorSubject`\n * behind an `asObservable()`, or a `combineLatest` over such subjects,\n * answers with what it already holds. A stream that has nothing to say\n * synchronously answers `undefined` until its first emission arrives,\n * which is the honest answer: there is no value yet.\n */\nclass StreamCell<T> extends Observable<T> implements ReadableCell<T> {\n private last!: T;\n private followers = 0;\n private upstream: Subscription | null = null;\n private readonly changes = new Subject<T>();\n\n constructor(private readonly stream: Observable<T>) {\n super(subscriber => {\n this.followers++;\n if (this.upstream === null) {\n this.attach();\n }\n subscriber.next(this.last);\n const following = this.changes.subscribe(subscriber);\n return () => {\n following.unsubscribe();\n this.followers--;\n if (this.followers === 0) {\n this.upstream?.unsubscribe();\n this.upstream = null;\n }\n };\n });\n }\n\n get value(): T {\n trackRead(this);\n if (this.upstream === null) {\n const asking = this.stream.subscribe(value => {\n this.last = value;\n });\n asking.unsubscribe();\n }\n return this.last;\n }\n\n private attach(): void {\n this.upstream = this.stream.subscribe(value => {\n this.last = value;\n this.changes.next(value);\n });\n }\n}\n\nfunction sameSet<T>(a: ReadonlySet<T>, b: ReadonlySet<T>): boolean {\n if (a.size !== b.size) {\n return false;\n }\n for (const item of a) {\n if (!b.has(item)) {\n return false;\n }\n }\n return true;\n}\n","import type { Observable } from 'rxjs';\n\nimport { computed, type ComputedCell } from './computed';\nimport type { Equality } from './derive';\n\nexport interface SelectOptions<T> {\n /**\n * How a projected value is judged unchanged; `structural` by default,\n * because a projection nearly always builds a value rather than\n * passing one through, and a rebuilt equal one is not a change.\n */\n readonly equal?: Equality<T>;\n /** What to call the cell in a warning. */\n readonly label?: string;\n}\n\n/**\n * One field of a cell, or one projection of it, as a cell.\n *\n * const title = select(inputs.track, 'title');\n * const names = select(inputs.track, entry => entry?.tags ?? []);\n *\n * Reading four fields of one input costs four of these rather than four\n * `pipe(map(...), distinctUntilChanged(...))`, which is what the two\n * applications wrote by hand often enough to invent their own `field()`\n * and `text()` helpers for it.\n *\n * The source may be any Observable, not only a cell: `select` is a\n * `computed` over one source, and reads it the way a computed reads\n * anything. The comparison is structural by default, since the reason\n * for nearly every hand-written comparator is exactly this: a\n * projection that rebuilds an equal array or object each time should\n * not re-bind everything reading it.\n *\n * There is no `inputs.track.title` proxy. A cell is an Observable, so\n * its own members (`value`, `pipe`, `subscribe`, `source`) would shadow\n * the fields of anything projected through it, and a data type that\n * happens to have a field called `value` would read as the cell's\n * current value instead. `select` names the field explicitly and cannot\n * collide.\n */\nexport function select<T extends object, K extends keyof T>(\n source: Observable<T>,\n key: K,\n options?: SelectOptions<T[K]>\n): ComputedCell<T[K]>;\nexport function select<T, R>(\n source: Observable<T>,\n project: (value: T) => R,\n options?: SelectOptions<R>\n): ComputedCell<R>;\nexport function select(\n source: Observable<unknown>,\n keyOrProject: PropertyKey | ((value: unknown) => unknown),\n options: SelectOptions<unknown> = {}\n): ComputedCell<unknown> {\n const project =\n typeof keyOrProject === 'function'\n ? keyOrProject\n : // A stream that has not spoken yet has no value to read a field\n // off, and neither has a source that is legitimately null. Both\n // answer `undefined` rather than throwing on the first frame.\n (value: unknown) =>\n value === null || value === undefined ? undefined : (value as Record<PropertyKey, unknown>)[keyOrProject];\n return computed(read => project(read(source)), {\n equal: options.equal ?? 'structural',\n ...(options.label === undefined ? {} : { label: options.label })\n });\n}\n","import { Subscription, type Observable } from 'rxjs';\n\nimport type { ComputedCell } from './computed';\nimport type { ReadableCell } from './Input';\nimport { internalState, type InternalState } from './InternalState';\nimport { select } from './select';\n\n/**\n * What a request has come to, in the five words every screen needs.\n *\n * Chosen once so screens stop inventing them. The tree had three\n * enums saying nearly the same thing in different words\n * (`'loading' | 'ready' | 'empty' | 'failed'`,\n * `'idle' | 'loading' | 'ready' | 'missing' | 'failed'`,\n * `'idle' | 'searching' | 'done' | 'failed'`), which meant a screen\n * reading two of them had two vocabularies for one idea.\n *\n * - `idle`: nothing has been asked for. The key is `null`.\n * - `loading`: a request is out and there is nothing to show yet.\n * - `ready`: there is a value. A refresh may still be in the air, and\n * that is deliberately not a separate status: what is on screen is\n * real, and saying \"loading\" over it would be a lie.\n * - `missing`: the answer was that there is no such thing. A fetch\n * says so by resolving `null`.\n * - `failed`: the request could not be answered at all.\n *\n * `missing` and `failed` are reported only when there is nothing to\n * fall back on. A refresh that fails over a value already held leaves\n * it on screen and stays `ready`, because a stale answer is stale and\n * not wrong, and an empty page would be worse. The error is still\n * there to read.\n *\n * What is *not* here is `empty`. A list that loaded and has no rows is\n * `ready` with an empty array, which is a judgement about the value\n * rather than about the request, and the screen that cares makes it.\n */\nexport type ResourceStatus = 'idle' | 'loading' | 'ready' | 'missing' | 'failed';\n\n/**\n * A resource as one record, so it can be a channel view key.\n *\n * Plain data throughout: the error is its message rather than the\n * `Error`, because a message is what a screen shows and what survives\n * the barrier, and an `Error` is neither.\n */\nexport interface ResourceState<T> {\n readonly status: ResourceStatus;\n readonly value: T | null;\n readonly error: string | null;\n}\n\nexport interface ResourceOptions<K, T> {\n /**\n * What is already known for this key, shown at once while the\n * request runs behind it.\n *\n * This is the store-first rule in one function: a hit is on screen\n * synchronously and marked `ready` even though a request is on its\n * way, so a page opened twice is instant the second time and correct\n * a moment later. Answering `null` or `undefined` means nothing is\n * held, and the resource says `loading`.\n */\n readonly peek?: (key: K) => T | null | undefined;\n /** What to call this resource in a warning; optional. */\n readonly label?: string;\n}\n\n/**\n * A request, keyed, so a stale answer cannot win.\n *\n * The key says what to fetch, and every value the key source emits is\n * a request. An answer is published only if its request is still the\n * current one, which is the generation counter every screen that loads\n * anything was writing by hand, and the reason opening a page, going\n * back and opening another before the first answers does not end with\n * the first answer on screen.\n *\n * private readonly ref = internalState<PageRef | null>(null);\n * readonly page = resource(this.ref, ref => api.trackPage(ref));\n *\n * show(ref: PageRef | null): Promise<void> {\n * this.ref.value = ref;\n * return this.page.settled;\n * }\n *\n * A `null` key is \"nothing is being asked for\": the status is `idle`,\n * the value is `null`, and no fetch runs. That is what a screen showing\n * nothing yet actually means, and it saves every caller a branch.\n *\n * Unlike `computed`, a resource is eager: it follows its key from the\n * moment it is made, because a request is an effect and an effect that\n * waits for a subscriber is a request that never happens. It takes one\n * subscription to the key source for its whole life, however many\n * requests run through it, and `dispose()` gives that back.\n *\n * It is a helper and not a data layer. Nothing in the framework\n * requires one, a channel is reached exactly as it was, and an\n * application that would rather write its own is writing against the\n * same barrier this is written against.\n */\nexport class Resource<K, T> {\n /** The one cell everything else here is a projection of. */\n private readonly cell: InternalState<ResourceState<T>>;\n /** Bumped per request; an answer from an older one is dropped. */\n private requests = 0;\n private asked: K | null = null;\n private settling: Promise<void> = Promise.resolve();\n private readonly following: Subscription;\n\n /** Status, value and error as one record: what a channel view key takes. */\n readonly state: ReadableCell<ResourceState<T>>;\n readonly status: ComputedCell<ResourceStatus>;\n /** What is loaded, or `null` while there is nothing to show. */\n readonly value: ComputedCell<T | null>;\n /** Why the last request failed, as its message; `null` when it did not. */\n readonly error: ComputedCell<string | null>;\n\n constructor(\n key: Observable<K | null | undefined>,\n private readonly fetch: (key: K) => Promise<T | null>,\n private readonly options: ResourceOptions<K, T> = {}\n ) {\n this.cell = internalState<ResourceState<T>>({ status: 'idle', value: null, error: null }, options.label);\n this.state = this.cell;\n this.status = select(this.cell, 'status', named(options.label, 'status'));\n // Compared by identity rather than structurally: a fetch builds a\n // new value every time, and walking a page-sized object to\n // discover that is exactly the cost the differ already pays once.\n this.value = select(this.cell, 'value', { ...named(options.label, 'value'), equal: 'reference' });\n this.error = select(this.cell, 'error', named(options.label, 'error'));\n this.following = key.subscribe(next => this.request(next ?? null));\n }\n\n /** What is being asked for, for a caller that needs to guard on it. */\n get requested(): K | null {\n return this.asked;\n }\n\n /**\n * The request in the air, as a promise that resolves when it settles.\n *\n * Already resolved when nothing is in flight, so a caller that sets\n * the key and returns this reads as an ordinary async method. Each\n * request keeps its own promise, so a dropped one still resolves for\n * whoever is awaiting it; it simply changes nothing on the way.\n */\n get settled(): Promise<void> {\n return this.settling;\n }\n\n /**\n * Asks again for the same key, keeping what is on screen.\n *\n * This is the retry button. It does not clear the value the way a\n * new key does, because a person pressing retry is asking for the\n * thing they can already see to be brought up to date, and blanking\n * it first would be a worse answer than the stale one.\n */\n retry(): Promise<void> {\n if (this.asked === null) {\n return this.settling;\n }\n const generation = ++this.requests;\n const held = this.cell.value;\n if (held.value === null && held.status !== 'loading') {\n this.write({ status: 'loading', value: null, error: null });\n }\n this.settling = this.run(this.asked, generation);\n return this.settling;\n }\n\n /**\n * Replaces what is loaded, for a change made here rather than\n * fetched.\n *\n * A page of comments appended to the answer already held, an\n * optimistic edit: the resource holds the value, so something has to\n * be able to write it. It does not touch the request in flight, so a\n * refresh that lands afterwards still wins, which is what it should\n * do: it is the newer truth.\n */\n set(value: T): void {\n this.write({ status: 'ready', value, error: null });\n }\n\n /** Gives back the subscription to the key source. */\n dispose(): void {\n this.following.unsubscribe();\n }\n\n private request(key: K | null): void {\n this.asked = key;\n const generation = ++this.requests;\n if (key === null) {\n this.write({ status: 'idle', value: null, error: null });\n this.settling = Promise.resolve();\n return;\n }\n const held = this.options.peek?.(key) ?? null;\n this.write({ status: held === null ? 'loading' : 'ready', value: held, error: null });\n this.settling = this.run(key, generation);\n }\n\n private run(key: K, generation: number): Promise<void> {\n return this.fetch(key).then(\n answer => this.answered(generation, answer),\n (error: unknown) => this.refused(generation, error)\n );\n }\n\n private answered(generation: number, answer: T | null): void {\n if (generation !== this.requests) {\n return;\n }\n if (answer === null) {\n const held = this.cell.value.value;\n this.write({ status: held === null ? 'missing' : 'ready', value: held, error: null });\n return;\n }\n this.write({ status: 'ready', value: answer, error: null });\n }\n\n private refused(generation: number, error: unknown): void {\n if (generation !== this.requests) {\n return;\n }\n const held = this.cell.value.value;\n this.write({ status: held === null ? 'failed' : 'ready', value: held, error: messageOf(error) });\n }\n\n private write(next: ResourceState<T>): void {\n this.cell.value = next;\n }\n}\n\n/**\n * A keyed request with a status, a value, an error and a retry.\n *\n * const page = resource(ref, key => api.page(key), { peek: key => store.get(key) });\n * <Show when={computed(() => page.status.value === 'loading')}>{() => <Spinner />}</Show>\n *\n * See `Resource` for what each status means and when a stale value is\n * kept. The key is an Observable so that setting a cell is what asks\n * for something: `computed` and `internalState` both work, and so does\n * a channel view key or a router match.\n */\nexport function resource<K, T>(\n key: Observable<K | null | undefined>,\n fetch: (key: K) => Promise<T | null>,\n options: ResourceOptions<K, T> = {}\n): Resource<K, T> {\n return new Resource(key, fetch, options);\n}\n\nfunction named(base: string | undefined, part: string): { label?: string } {\n return base === undefined ? {} : { label: `${base}.${part}` };\n}\n\nfunction messageOf(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n","import { equalityOf, type Equality } from './derive';\nimport type { ReadableCell } from './Input';\nimport { internalState, type InternalState } from './InternalState';\n\nexport interface MutateOptions<T> {\n /**\n * How the rollback decides the cell still holds what was applied;\n * `structural` by default, because an optimistic change nearly\n * always builds a new list or a new record rather than passing one\n * through, so identity would never match and nothing would ever roll\n * back.\n */\n readonly equal?: Equality<T>;\n /** What to call the in-flight count in a warning; optional. */\n readonly label?: string;\n}\n\n/** A change that is made locally at once and confirmed afterwards. */\nexport interface Mutation<A> {\n /**\n * How many writes are in the air, for a saving indicator.\n *\n * A count rather than a flag, because two presses in quick\n * succession are two writes and a flag cleared by the first would\n * say the second had finished.\n */\n readonly pending: ReadableCell<number>;\n /**\n * Applies the change, commits it, and puts it back if the commit\n * refuses. Answers whether it stuck.\n */\n run(argument: A): Promise<boolean>;\n}\n\n/**\n * An optimistic change to a cell, with a rollback that does not fight\n * the person.\n *\n * private readonly favourites = internalState<readonly string[]>([]);\n * private readonly like = mutate(this.favourites, toggled, id => api.favourite(id));\n *\n * toggle(id: string): void {\n * void this.like.run(id);\n * }\n *\n * Three things happen and the order is the whole point. `apply` runs\n * first and writes the cell, so the screen changes on the press rather\n * than a round trip later. `commit` then does the real write. If it\n * rejects, or resolves `false`, the cell goes back to what it held\n * before.\n *\n * **The rollback is guarded.** It happens only while the cell still\n * holds exactly what `apply` wrote. Without that, a slow rejection\n * would fight a fast second press and the cell would end up saying the\n * opposite of the last thing anyone did, which is the guard every\n * optimistic screen writes by hand and half of them get wrong.\n *\n * The cell is named first, and not because it was first written\n * `mutate(apply, commit)`: the cell is what makes the guard possible.\n * A mutation handed only two functions can undo its own change but\n * cannot tell whether undoing it is still the right thing to do.\n *\n * A helper and not a data layer: nothing in the framework requires\n * one, and an application that would rather write the four lines out\n * is writing the same four lines this does.\n */\nexport function mutate<T, A = void>(\n cell: InternalState<T>,\n apply: (current: T, argument: A) => T,\n commit: (argument: A, applied: T) => Promise<unknown>,\n options: MutateOptions<T> = {}\n): Mutation<A> {\n const equal = equalityOf<T>(options.equal ?? 'structural');\n const inFlight = internalState(0, options.label);\n\n const revert = (before: T, applied: T): void => {\n if (!equal(cell.value, applied)) {\n // Changed again since, by whoever is using the screen. Their\n // change is newer than this answer and wins.\n return;\n }\n cell.value = before;\n };\n\n return {\n pending: inFlight,\n async run(argument: A): Promise<boolean> {\n const before = cell.value;\n const applied = apply(before, argument);\n cell.value = applied;\n inFlight.value = inFlight.value + 1;\n try {\n // `false` and a rejection mean the same thing: the write did\n // not happen. Anything else, `undefined` included, is a\n // commit that went through.\n if ((await commit(argument, applied)) === false) {\n revert(before, applied);\n return false;\n }\n return true;\n } catch {\n revert(before, applied);\n return false;\n } finally {\n inFlight.value = Math.max(0, inFlight.value - 1);\n }\n }\n };\n}\n","import {\n debounceTime,\n Observable,\n Subject,\n Subscription,\n tap,\n throttleTime,\n type MonoTypeOperatorFunction\n} from 'rxjs';\n\nimport { trackRead, type ReadableCell } from './Input';\n\n/**\n * A cell that lets its source through on a timer.\n *\n * The two operators below are the same machinery with a different\n * gate, and both are cells rather than plain streams on purpose: a\n * `computed` reads a cell with `.value` and follows it, so a debounced\n * search term composes with everything else in the dialect instead of\n * being the one value in a screen that has to be piped.\n *\n * It holds the last value the gate let through, which is what `.value`\n * answers while something is following it. With nothing following\n * there is no timer running to hold anything back, so `.value` reads\n * the source directly, which is the honest answer rather than a value\n * frozen at whatever moment the last follower left.\n *\n * A value equal to the one it already holds is not a change and is not\n * emitted, the same rule `computed` follows. Without it the first pass\n * of the gate after a subscription would repeat the value the\n * subscriber had just been handed.\n *\n * One subscription upstream however many followers it has, given back\n * when the last of them leaves, on the same terms as `ComputedCell`.\n */\nclass TimedCell<T> extends Observable<T> implements ReadableCell<T> {\n private current!: T;\n private hasCurrent = false;\n private followers = 0;\n private seeding = false;\n private upstream: Subscription | null = null;\n private readonly changes = new Subject<T>();\n\n constructor(\n private readonly stream: Observable<T>,\n private readonly gate: MonoTypeOperatorFunction<T>\n ) {\n super(subscriber => {\n this.followers++;\n if (this.upstream === null) {\n this.attach();\n }\n subscriber.next(this.current);\n const following = this.changes.subscribe(subscriber);\n return () => {\n following.unsubscribe();\n this.followers--;\n if (this.followers === 0) {\n this.upstream?.unsubscribe();\n this.upstream = null;\n }\n };\n });\n }\n\n get value(): T {\n trackRead(this);\n if (this.upstream === null) {\n const asking = this.stream.subscribe(value => {\n this.current = value;\n this.hasCurrent = true;\n });\n asking.unsubscribe();\n }\n return this.current;\n }\n\n private attach(): void {\n // Seeds the current value from whatever the source already holds,\n // so a cell behind a gate has a value on the first frame rather\n // than after the first delay. Only during the subscribe itself: a\n // value arriving later is a change, and absorbing it here would\n // let it past the gate without telling anybody.\n this.seeding = true;\n this.upstream = this.stream\n .pipe(\n tap(value => {\n if (this.seeding && !this.hasCurrent) {\n this.current = value;\n this.hasCurrent = true;\n }\n }),\n this.gate\n )\n .subscribe(value => {\n if (this.hasCurrent && Object.is(this.current, value)) {\n return;\n }\n this.current = value;\n this.hasCurrent = true;\n this.changes.next(value);\n });\n this.seeding = false;\n }\n}\n\n/**\n * A cell that follows its source once it has stopped moving.\n *\n * const query = internalState('');\n * const term = debounced(query, 200);\n * const results = computed(() => index.search(term.value));\n *\n * Nothing is emitted while values keep arriving; `ms` after the last\n * one, the last one is. This is what a search field wants and what\n * every search field in the tree was reaching the router without: a\n * keystroke is not a question, and a pause is.\n *\n * The value the source already held is there immediately, so a screen\n * built from this draws on the first frame rather than `ms` later.\n */\nexport function debounced<T>(source: Observable<T>, ms: number): ReadableCell<T> {\n return new TimedCell(source, debounceTime<T>(ms));\n}\n\n/**\n * A cell that follows its source at most once every `ms`.\n *\n * const position = throttled(scrollOffset, 100);\n *\n * The first value goes straight through and the last of a burst\n * follows at the end of the window, so a value that arrives while the\n * window is open is late rather than lost. That pairing is what makes\n * this usable for a position or a progress reading, where the\n * beginning and the end of a movement both matter and the middle does\n * not.\n *\n * Use this for something that is continuously true, and `debounced`\n * for something a person has finished saying.\n */\nexport function throttled<T>(source: Observable<T>, ms: number): ReadableCell<T> {\n return new TimedCell(source, throttleTime<T>(ms, undefined, { leading: true, trailing: true }));\n}\n","import { distinctUntilChanged, map, type Observable } from 'rxjs';\n\n/**\n * One key of a view model, as its own Observable, emitting only when\n * that key's value changes.\n *\n * `provide` and `serveChannels` want one Observable per view key, so\n * the differ can patch each key on its own, while a view model is most\n * naturally one Observable of one object. This is the seam between the\n * two, and every application worker was about to write it.\n */\nexport function pick<T, K extends keyof T>(source: Observable<T>, key: K): Observable<T[K]> {\n return source.pipe(\n map(value => value[key]),\n distinctUntilChanged()\n );\n}\n\n/**\n * Every key of a view model as its own Observable: the `view` a channel\n * source wants, from the one Observable a view model has.\n *\n * serveChannels([{ token: Queue, source: { view: pickKeys(queue.view, QUEUE_KEYS), commands } }])\n */\nexport function pickKeys<T extends object, K extends keyof T>(\n source: Observable<T>,\n keys: readonly K[]\n): { readonly [P in K]: Observable<T[P]> } {\n const out: Partial<{ [P in K]: Observable<T[P]> }> = {};\n for (const key of keys) {\n out[key] = pick(source, key);\n }\n return out as { readonly [P in K]: Observable<T[P]> };\n}\n","/**\n * What a store did with a request.\n *\n * Four words rather than an exception, because three of them are\n * ordinary outcomes an application routes around rather than faults it\n * reports. A browser that refuses storage in a private window, and a\n * quota that has run out, are both things a running application has to\n * carry on through, and making every caller wrap a `try` around a read\n * to discover which one happened is how storage code ends up assuming\n * success.\n *\n * - `ok`: the store answered. For a read that includes \"there is no\n * such record\", which is `value: null` and not a failure.\n * - `denied`: the platform will not let this origin store anything.\n * No OPFS in this browser, a private window that refuses IndexedDB,\n * `localStorage` blocked by a site setting. Permanent for the\n * session: nothing an application does will change the answer, so\n * asking again is wasted work.\n * - `full`: the quota is spent. Temporary, and worth trying again\n * after something has been given back, which is why it is not\n * `denied`.\n * - `failed`: anything else. A half-written file, a corrupt database,\n * a transaction that aborted for a reason the platform did not\n * explain.\n */\nexport type StorageOutcome = 'ok' | 'denied' | 'full' | 'failed';\n\n/** What came back from a read. */\nexport interface StorageRead {\n readonly outcome: StorageOutcome;\n /** The record, or null when there is none or the read did not answer. */\n readonly value: string | null;\n /** Why it did not answer, as a message; null when it did. */\n readonly error: string | null;\n}\n\n/**\n * Somewhere an application's state survives being closed.\n *\n * Four methods over text keyed by a string, which is the shape all\n * three implementations can actually keep. Bytes are deliberately not\n * in it: `localStorage` cannot hold them, so an interface that\n * promised them would be one the shell route could not implement, and\n * an application storing pictures wants a store of its own with an\n * eviction policy rather than this. the\n * artwork cache is that other thing.\n *\n * Nothing rejects. Every method answers with an outcome, and the\n * per-failure behaviour is the implementation's to document; the three\n * in this package all follow `classifyStorageError`.\n *\n * It is a shape and not a service. Nothing in the framework holds one,\n * `persisted` takes whichever one it is given, and an application with\n * its own store implements four methods rather than adopting anything.\n */\nexport interface StorageAdapter {\n /** Reads one record. */\n read(key: string): Promise<StorageRead>;\n /** Writes one record, replacing whatever was there. */\n write(key: string, value: string): Promise<StorageOutcome>;\n /** Removes one record. Removing what is not there is `ok`. */\n remove(key: string): Promise<StorageOutcome>;\n /** Every key this store holds, for an application that evicts its own. */\n keys(): Promise<readonly string[]>;\n}\n\n/**\n * Which of the four outcomes a thrown platform error is.\n *\n * The names are the ones the storage APIs actually throw.\n * `QuotaExceededError` is the DOM's word for full, and every browser\n * uses it for OPFS, IndexedDB and `localStorage` alike. `SecurityError`\n * and `NotAllowedError` are what a blocked origin gets. A missing API\n * (no `navigator.storage`, no `indexedDB`) is a `TypeError` here and\n * is `denied` for the same reason: nothing the application does will\n * produce a store.\n */\nexport function classifyStorageError(error: unknown): StorageOutcome {\n const name = error instanceof Error ? error.name : '';\n if (name === 'QuotaExceededError' || name === 'NS_ERROR_DOM_QUOTA_REACHED') {\n return 'full';\n }\n if (name === 'SecurityError' || name === 'NotAllowedError' || name === 'TypeError') {\n return 'denied';\n }\n return 'failed';\n}\n\n/** A thrown value as the message a screen could show. */\nexport function storageErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\n/** A read that could not answer, as one record. */\nexport function storageReadFailure(error: unknown): StorageRead {\n return { outcome: classifyStorageError(error), value: null, error: storageErrorMessage(error) };\n}\n\n/** A read that answered, whether or not it found anything. */\nexport function storageReadValue(value: string | null): StorageRead {\n return { outcome: 'ok', value, error: null };\n}\n\n/**\n * The same contract in memory, for specs and for a platform with no\n * store at all.\n *\n * Not a fallback anything installs on its own. An application that\n * would rather run with an unremembered session than fail says so by\n * passing one of these; one that would rather tell the person its\n * settings will not be kept reads the `denied` outcome and says so.\n * Choosing between those two on an application's behalf is exactly the\n * kind of decision the thread model keeps out of the framework.\n */\nexport class MemoryStorage implements StorageAdapter {\n private readonly records = new Map<string, string>();\n /** Written by a spec that wants to see what a full store does. */\n full = false;\n\n read(key: string): Promise<StorageRead> {\n return Promise.resolve(storageReadValue(this.records.get(key) ?? null));\n }\n\n write(key: string, value: string): Promise<StorageOutcome> {\n if (this.full) {\n return Promise.resolve('full');\n }\n this.records.set(key, value);\n return Promise.resolve('ok');\n }\n\n remove(key: string): Promise<StorageOutcome> {\n this.records.delete(key);\n return Promise.resolve('ok');\n }\n\n keys(): Promise<readonly string[]> {\n return Promise.resolve([...this.records.keys()]);\n }\n}\n","import { computed, type ComputedCell } from '../computed';\nimport { internalState, type InternalState } from '../InternalState';\nimport { select } from '../select';\n\n/** One undoable change, as the two functions that make and unmake it. */\nexport interface UndoTransaction {\n /**\n * What a menu item or a toast calls this change: \"Move Sunset\",\n * \"Remove Sunset from the queue\".\n *\n * A phrase rather than a sentence, because the words around it are\n * the caller's: an application writes \"Undo \" in front of it.\n */\n readonly label: string;\n /** Puts the world back the way it was before `redo` ran. */\n readonly undo: () => void;\n /** Makes the change again, from wherever `undo` left things. */\n readonly redo: () => void;\n /**\n * Merges this into the entry before it when the two keys match.\n *\n * A drag reports a crossing per row and a typed name reports a\n * keystroke, and neither is a change a person means to undo one step\n * at a time. Entries pushed one after another under the same key\n * become one entry: **the first one's `undo` and the last one's\n * `redo`**, with everything between them dropped.\n *\n * That merge rule is what makes the two functions above worth\n * writing as absolute moves rather than as deltas. `undo` should say\n * where the thing goes, not how far back it goes, because after\n * three more coalesced steps a delta is wrong and a position is\n * still right. See `endRun` for where a run stops.\n */\n readonly coalesce?: string;\n}\n\nexport interface UndoStackOptions {\n /**\n * How many entries are kept, oldest dropped first. Default 100.\n *\n * A bound rather than a growing list, for the reason\n * `EditableTextModel` has one: an application that is used for an\n * afternoon would otherwise hold every intermediate state of that\n * afternoon, and nobody undoes a hundred steps.\n */\n readonly limit?: number;\n /** What to call the stack in a devtools reading; optional. */\n readonly label?: string;\n}\n\n/** The two labels, as one record so a menu reads them in one go. */\ninterface UndoLabels {\n readonly undo: string | null;\n readonly redo: string | null;\n}\n\nconst NOTHING: UndoLabels = { undo: null, redo: null };\n\n/**\n * The application's undo, as a stack of named transactions.\n *\n * A component registers what it did and how to unmake it, and a menu,\n * a button or a keyboard shortcut drives the stack. Nothing here\n * listens to anything or knows what an application's state is: the two\n * functions in a transaction are the whole of the coupling, which is\n * why this can sit on either thread and why it does not become a data\n * layer.\n *\n * const undo = ctx.inject(UndoStack);\n *\n * queue.send.remove(at);\n * undo.push({\n * label: `Remove ${track.title}`,\n * undo: () => queue.send.addToQueue(track.id),\n * redo: () => queue.send.remove(at)\n * });\n *\n * ## How this relates to the undo inside a text field\n *\n * `EditableTextModel` has had its own undo since text became editable,\n * and the two are deliberately separate. A field's undo is a stack of\n * **snapshots of one string**, private to the field, and it has to be:\n * the model is the only thing that knows where the caret was, which\n * run of typing coalesces with which, and what an IME composition is\n * doing. This one is a stack of **inverse operations** over whatever\n * an application's state happens to be, and it cannot see inside a\n * field at all.\n *\n * They meet at one key press, and the rule there is that the focused\n * field wins: `registerUndoShortcuts` skips Mod+Z while something is\n * being typed into, so undo in a field undoes typing and undo\n * everywhere else undoes the application's last change. Merging the\n * two stacks would mean a keystroke and a queue reorder sharing one\n * history, which is not what either of them means.\n *\n * ## Reentrancy\n *\n * A push while an undo or a redo is running is dropped. Without it an\n * application whose edit path records itself would record the undo as\n * a new edit and the stack would never empty. Write the inverse\n * functions to call the state directly rather than through the same\n * path that records, and the guard never fires.\n */\nexport class UndoStack {\n private readonly entries: UndoTransaction[] = [];\n private readonly undone: UndoTransaction[] = [];\n private readonly limit: number;\n /** True while `undo()` or `redo()` is running one of the functions. */\n private running = false;\n /** Set by `endRun`, so the next push starts a new entry whatever its key. */\n private sealed = false;\n\n private readonly labels: InternalState<UndoLabels>;\n\n /** What undoing would undo, or null when there is nothing to undo. */\n readonly undoLabel: ComputedCell<string | null>;\n /** What redoing would redo, or null when there is nothing to redo. */\n readonly redoLabel: ComputedCell<string | null>;\n /** Whether there is anything to undo, for a menu item's `disabled`. */\n readonly canUndo: ComputedCell<boolean>;\n readonly canRedo: ComputedCell<boolean>;\n\n constructor(options: UndoStackOptions = {}) {\n this.limit = Math.max(1, options.limit ?? 100);\n this.labels = internalState<UndoLabels>(NOTHING, options.label);\n this.undoLabel = select(this.labels, 'undo');\n this.redoLabel = select(this.labels, 'redo');\n this.canUndo = computed(() => this.undoLabel.value !== null);\n this.canRedo = computed(() => this.redoLabel.value !== null);\n }\n\n /** How many entries are held, for a spec or a budget. */\n get size(): number {\n return this.entries.length;\n }\n\n /** How many redos are waiting, for a spec or a budget. */\n get redoSize(): number {\n return this.undone.length;\n }\n\n /**\n * Records a change that has already been made.\n *\n * Pushing is what discards the redo branch: making a change after\n * undoing two is the person choosing the other future, and keeping\n * the abandoned one would mean redoing into a state that no longer\n * follows from what is on screen.\n */\n push(transaction: UndoTransaction): void {\n if (this.running) {\n return;\n }\n this.undone.length = 0;\n const previous = this.entries[this.entries.length - 1];\n const sealed = this.sealed;\n this.sealed = false;\n if (\n !sealed &&\n previous !== undefined &&\n transaction.coalesce !== undefined &&\n previous.coalesce === transaction.coalesce\n ) {\n // The run's own undo, kept from its first step, and its newest\n // redo. Every step in between is released here, which is the\n // whole of what stops a drag across sixty rows retaining sixty\n // closures and the sixty values they close over.\n this.entries[this.entries.length - 1] = {\n label: transaction.label,\n undo: previous.undo,\n redo: transaction.redo,\n coalesce: transaction.coalesce\n };\n this.publish();\n return;\n }\n this.entries.push(transaction);\n while (this.entries.length > this.limit) {\n this.entries.shift();\n }\n this.publish();\n }\n\n /**\n * Groups everything pushed inside `body` into one entry.\n *\n * For a change an application makes as several calls and a person\n * made as one press: undoing runs the group's undos in reverse, and\n * redoing runs its redos in order. Unlike `coalesce`, a group keeps\n * every step, because a group is written down as a group rather than\n * discovered from a run of similar pushes.\n */\n transact<R>(label: string, body: () => R): R {\n if (this.running) {\n return body();\n }\n const outer = this.entries.length;\n const result = body();\n const collected = this.entries.splice(outer);\n if (collected.length > 0) {\n this.entries.push({\n label,\n undo: () => {\n for (let at = collected.length - 1; at >= 0; at--) {\n collected[at]!.undo();\n }\n },\n redo: () => {\n for (const step of collected) {\n step.redo();\n }\n }\n });\n this.sealed = true;\n this.publish();\n }\n return result;\n }\n\n /**\n * Ends the current run, so the next push starts its own entry.\n *\n * The twin of `EditableTextModel.endTypingRun`. A drag calls it when\n * the pointer comes up: without it, dragging a row, letting go, and\n * dragging the same row again would coalesce into one entry, and one\n * undo would put the row back where it was two gestures ago.\n */\n endRun(): void {\n this.sealed = true;\n }\n\n /** Undoes the last change. False when there was nothing to undo. */\n undo(): boolean {\n const entry = this.entries.pop();\n if (entry === undefined) {\n return false;\n }\n this.run(entry.undo);\n this.undone.push(entry);\n this.sealed = true;\n this.publish();\n return true;\n }\n\n /** Redoes the last undone change. False when there was nothing to redo. */\n redo(): boolean {\n const entry = this.undone.pop();\n if (entry === undefined) {\n return false;\n }\n this.run(entry.redo);\n this.entries.push(entry);\n this.sealed = true;\n this.publish();\n return true;\n }\n\n /**\n * Forgets everything, in both directions.\n *\n * What an application calls when the thing the entries refer to is\n * gone: a queue emptied, a document closed, a signed-out account's\n * library replaced. `EditableTextModel.setText` does the same for\n * the same reason, and the reason is that an inverse function whose\n * subject no longer exists is not an undo, it is a surprise.\n */\n clear(): void {\n this.entries.length = 0;\n this.undone.length = 0;\n this.sealed = false;\n this.publish();\n }\n\n private run(action: () => void): void {\n this.running = true;\n try {\n action();\n } finally {\n this.running = false;\n }\n }\n\n private publish(): void {\n const next: UndoLabels = {\n undo: this.entries[this.entries.length - 1]?.label ?? null,\n redo: this.undone[this.undone.length - 1]?.label ?? null\n };\n // Structural comparison rather than a write per push: coalescing a\n // drag rewrites the same label sixty times, and a menu bound to\n // these should redraw when the words change and not before.\n if (next.undo !== this.labels.value.undo || next.redo !== this.labels.value.redo) {\n this.labels.value = next;\n }\n }\n}\n","import type { Mutation } from '../mutate';\nimport type { UndoStack } from './UndoStack';\n\nexport interface UndoableOptions<A> {\n /** What the entry is called, from the argument that made it. */\n readonly label: string | ((argument: A) => string);\n /** The coalescing key, from the argument; see `UndoTransaction.coalesce`. */\n readonly coalesce?: string | ((argument: A) => string);\n}\n\n/**\n * Puts a `mutate` on an undo stack, by way of its own inverse.\n *\n * private readonly liked = internalState<readonly string[]>([]);\n * private readonly like = mutate(this.liked, toggled, id => api.favourite(id));\n * readonly toggleLike = undoable(undo, this.like, id => id, { label: () => 'Like' });\n *\n * The whole of it is that undoing an optimistic change is another\n * optimistic change. Nothing here writes the cell behind the\n * mutation's back, which is the thing that would go wrong if an undo\n * stack held values rather than operations: it would restore a value\n * the server has not been told about, and the next rollback would\n * fight it.\n *\n * `invert` answers the argument that undoes this one. For a toggle\n * that is the same argument again, which is why the example above\n * looks like it does nothing.\n *\n * **A refused write is not on the stack.** `mutate` already puts the\n * cell back when a commit rejects or resolves `false`, so the change\n * did not happen and there is nothing to undo; pushing it would give a\n * person an undo that undoes something they never saw.\n *\n * The undo and the redo call `mutation.run` rather than this wrapper,\n * so running them records nothing and the stack's reentrancy guard\n * never has to fire.\n */\nexport function undoable<A>(\n stack: UndoStack,\n mutation: Mutation<A>,\n invert: (argument: A) => A,\n options: UndoableOptions<A>\n): (argument: A) => Promise<boolean> {\n const { label, coalesce } = options;\n return async (argument: A): Promise<boolean> => {\n if (!(await mutation.run(argument))) {\n return false;\n }\n stack.push({\n label: typeof label === 'function' ? label(argument) : label,\n undo: () => void mutation.run(invert(argument)),\n redo: () => void mutation.run(argument),\n ...(coalesce === undefined ? {} : { coalesce: typeof coalesce === 'function' ? coalesce(argument) : coalesce })\n });\n return true;\n };\n}\n","import {\n classifyStorageError,\n storageReadFailure,\n storageReadValue,\n type StorageAdapter,\n type StorageOutcome,\n type StorageRead\n} from './StorageAdapter';\n\n/**\n * As much of the Origin Private File System as a store needs.\n *\n * Written out rather than taken from `lib.dom`, for two reasons. The\n * workspace's `lib` is `ES2023` and `DOM`, which has the handles but\n * not the async iteration `values()` needs, and a structural type is\n * what lets a spec hand this a directory of its own instead of\n * standing up a file system to test four methods.\n */\nexport interface OpfsDirectory {\n getFileHandle(name: string, options?: { create?: boolean }): Promise<OpfsFileHandle>;\n getDirectoryHandle(name: string, options?: { create?: boolean }): Promise<OpfsDirectory>;\n removeEntry(name: string): Promise<void>;\n keys(): AsyncIterable<string>;\n}\n\nexport interface OpfsFileHandle {\n getFile(): Promise<{ text(): Promise<string> }>;\n createWritable(): Promise<OpfsWritable>;\n}\n\nexport interface OpfsWritable {\n write(data: string): Promise<void>;\n close(): Promise<void>;\n}\n\nexport interface OpfsStorageOptions {\n /**\n * The folder under the origin's private root, so two stores in one\n * application do not share a namespace. Default `'gesso'`.\n */\n readonly directory?: string;\n /** The root, for a spec. Default `navigator.storage.getDirectory()`. */\n readonly root?: () => Promise<OpfsDirectory>;\n}\n\n/**\n * A store in the origin's private file system.\n *\n * The right default for an application's own state. It is reachable\n * from a worker, which `localStorage` is not, so the thread that owns\n * the state is the thread that writes it and nothing has to cross the\n * barrier to be remembered. It is asynchronous throughout, so nothing\n * it does blocks a frame. And it is per-origin and invisible to the\n * person, which is the right place for a queue or a draft and the\n * wrong place for anything they should be able to find and delete.\n *\n * One file per key, named by the key with the characters a file system\n * would refuse escaped, so a key is recoverable from a listing and a\n * key containing a slash cannot reach out of the folder.\n *\n * What happens on each failure:\n *\n * - **The platform has no OPFS**, or the browser refuses it (a private\n * window, a blocked origin): every method answers `denied` and the\n * error message says which. The store is not usable this session and\n * `persisted` stops writing to it after the first denial.\n * - **The quota is spent**: the write answers `full`. Nothing is\n * rolled back, because the value the application holds is the real\n * one and only the remembering failed.\n * - **There is no such record**: the read answers `ok` with `null`.\n * Not having been written yet is the ordinary first run, not a\n * failure.\n * - **Anything else**: `failed`, with the platform's message. A file\n * whose *contents* are not what this version writes is a different\n * thing and is `persisted`'s to judge, which it does by discarding\n * it, the same call `Tokens.ts` makes.\n */\nexport class OpfsStorage implements StorageAdapter {\n private readonly folder: string;\n private readonly rootOf: () => Promise<OpfsDirectory>;\n /** The folder, once opened. Reused, because opening it is a round trip. */\n private opening: Promise<OpfsDirectory> | null = null;\n\n constructor(options: OpfsStorageOptions = {}) {\n this.folder = options.directory ?? 'gesso';\n this.rootOf = options.root ?? defaultRoot;\n }\n\n async read(key: string): Promise<StorageRead> {\n try {\n const directory = await this.open();\n const handle = await directory.getFileHandle(fileFor(key));\n return storageReadValue(await (await handle.getFile()).text());\n } catch (error) {\n // A file that is not there is the ordinary answer to \"is\n // anything remembered\", and the platform says so by throwing\n // `NotFoundError`. It is not a failure and it is not `denied`.\n if (error instanceof Error && error.name === 'NotFoundError') {\n return storageReadValue(null);\n }\n return storageReadFailure(error);\n }\n }\n\n async write(key: string, value: string): Promise<StorageOutcome> {\n try {\n const directory = await this.open();\n const handle = await directory.getFileHandle(fileFor(key), { create: true });\n const writable = await handle.createWritable();\n // Written and closed in one go rather than kept open: a handle\n // held across frames is a handle a reload can leave locked.\n await writable.write(value);\n await writable.close();\n return 'ok';\n } catch (error) {\n return classifyStorageError(error);\n }\n }\n\n async remove(key: string): Promise<StorageOutcome> {\n try {\n await (await this.open()).removeEntry(fileFor(key));\n return 'ok';\n } catch (error) {\n if (error instanceof Error && error.name === 'NotFoundError') {\n return 'ok';\n }\n return classifyStorageError(error);\n }\n }\n\n async keys(): Promise<readonly string[]> {\n try {\n const found: string[] = [];\n for await (const name of (await this.open()).keys()) {\n found.push(keyFor(name));\n }\n return found;\n } catch {\n // A listing that cannot be taken is an empty one. The caller is\n // an application evicting its own records, and evicting nothing\n // is the safe answer to not knowing what is there.\n return [];\n }\n }\n\n /**\n * The folder, opened once and kept.\n *\n * A failed open is *not* kept. A rejected promise left in `opening`\n * would answer every later call with the same rejection, so one\n * refusal at start-up would be a store that never worked again even\n * after the person granted storage or made room. Kept when it\n * succeeds, dropped when it does not, which is one line and the\n * difference between a cache and a poison.\n */\n private open(): Promise<OpfsDirectory> {\n if (this.opening === null) {\n const opening = this.rootOf().then(root => root.getDirectoryHandle(this.folder, { create: true }));\n this.opening = opening;\n opening.catch(() => {\n if (this.opening === opening) {\n this.opening = null;\n }\n });\n }\n return this.opening;\n }\n}\n\nfunction defaultRoot(): Promise<OpfsDirectory> {\n const storage = (globalThis as { navigator?: { storage?: { getDirectory?: () => Promise<OpfsDirectory> } } })\n .navigator?.storage;\n if (storage?.getDirectory === undefined) {\n // A `TypeError`, which `classifyStorageError` reads as `denied`:\n // there is no store here and there will not be one.\n return Promise.reject(new TypeError('This environment has no Origin Private File System.'));\n }\n return storage.getDirectory();\n}\n\n/**\n * A key as a file name.\n *\n * `encodeURIComponent` and not a hash, so a listing of the folder in\n * devtools reads as the keys the application wrote. It escapes the\n * slash and the dot, which is what stops a key reaching a directory it\n * was not given.\n */\nfunction fileFor(key: string): string {\n return `${encodeURIComponent(key)}.json`;\n}\n\nfunction keyFor(name: string): string {\n return decodeURIComponent(name.replace(/\\.json$/, ''));\n}\n","import {\n classifyStorageError,\n storageReadFailure,\n storageReadValue,\n type StorageAdapter,\n type StorageOutcome,\n type StorageRead\n} from './StorageAdapter';\n\nexport interface IndexedDbStorageOptions {\n /** The database. Default `'gesso'`. */\n readonly database?: string;\n /** The object store inside it. Default `'records'`. */\n readonly store?: string;\n /** The factory, for a spec. Default `globalThis.indexedDB`. */\n readonly factory?: IDBFactory;\n}\n\n/**\n * A store in IndexedDB.\n *\n * Beside OPFS rather than instead of it, because the two fail in\n * different places and an application picks by which failure it\n * minds. IndexedDB is reachable from every thread, survives longer\n * under a browser's own eviction, and is what a Safari that has\n * disabled OPFS still has; OPFS is faster for one large record and\n * simpler to inspect. Neither is a default the framework picks: an\n * application names the one it wants.\n *\n * One object store of strings keyed by string, which is the shape\n * `StorageAdapter` describes and no more. Indexes, versions past the\n * first, and cursors over ranges are what an application builds when\n * it has outgrown a key-value store, and at that point it is writing\n * against IndexedDB rather than against this.\n *\n * What happens on each failure:\n *\n * - **No `indexedDB`, or an origin that may not open one** (a private\n * window in some browsers, a blocked third-party context): every\n * method answers `denied`, and the open is retried next time rather\n * than cached, because a `denied` can be lifted by a site setting\n * mid-session.\n * - **The quota is spent**: the write answers `full`. IndexedDB\n * reports this on the transaction rather than on the request, which\n * is why the write waits for `oncomplete` and not for\n * `onsuccess`: a put that succeeded into a transaction that then\n * aborted has not been written, and answering `ok` for it would be\n * the adapter assuming success.\n * - **A version change from another tab**: the connection is closed\n * and dropped, so the next call opens a fresh one. Answering\n * `failed` and holding a dead connection would make every later\n * call fail too.\n * - **Anything else**: `failed`, with the platform's message.\n */\nexport class IndexedDbStorage implements StorageAdapter {\n private readonly database: string;\n private readonly store: string;\n private readonly factory: IDBFactory | undefined;\n private connecting: Promise<IDBDatabase> | null = null;\n\n constructor(options: IndexedDbStorageOptions = {}) {\n this.database = options.database ?? 'gesso';\n this.store = options.store ?? 'records';\n this.factory = options.factory ?? (globalThis as { indexedDB?: IDBFactory }).indexedDB;\n }\n\n async read(key: string): Promise<StorageRead> {\n try {\n const value = await this.transact('readonly', store => store.get(key));\n return storageReadValue(typeof value === 'string' ? value : null);\n } catch (error) {\n return storageReadFailure(error);\n }\n }\n\n async write(key: string, value: string): Promise<StorageOutcome> {\n return this.outcomeOf(() => this.transact('readwrite', store => store.put(value, key)));\n }\n\n async remove(key: string): Promise<StorageOutcome> {\n return this.outcomeOf(() => this.transact('readwrite', store => store.delete(key)));\n }\n\n async keys(): Promise<readonly string[]> {\n try {\n const found = await this.transact('readonly', store => store.getAllKeys());\n return Array.isArray(found) ? found.filter((key): key is string => typeof key === 'string') : [];\n } catch {\n return [];\n }\n }\n\n /** Lets go of the connection, for a spec or an application shutting down. */\n close(): void {\n const connecting = this.connecting;\n this.connecting = null;\n void connecting?.then(\n database => database.close(),\n () => undefined\n );\n }\n\n private async outcomeOf(work: () => Promise<unknown>): Promise<StorageOutcome> {\n try {\n await work();\n return 'ok';\n } catch (error) {\n const outcome = classifyStorageError(error);\n if (outcome === 'denied') {\n this.connecting = null;\n }\n return outcome;\n }\n }\n\n /**\n * Runs one request inside one transaction and answers its result.\n *\n * A write resolves on the transaction completing rather than on the\n * request succeeding, because those are two different claims: the\n * second says the put was accepted, and only the first says it\n * reached the disk.\n */\n private async transact<T>(mode: IDBTransactionMode, run: (store: IDBObjectStore) => IDBRequest<T>): Promise<T> {\n const database = await this.connect();\n return new Promise<T>((resolve, reject) => {\n let answer: T;\n const transaction = database.transaction(this.store, mode);\n const request = run(transaction.objectStore(this.store));\n request.onsuccess = () => {\n answer = request.result;\n };\n request.onerror = () => reject(request.error ?? new Error('The request failed.'));\n transaction.oncomplete = () => resolve(answer);\n transaction.onabort = () => reject(transaction.error ?? new Error('The transaction was aborted.'));\n });\n }\n\n private connect(): Promise<IDBDatabase> {\n this.connecting ??= this.open();\n return this.connecting;\n }\n\n private open(): Promise<IDBDatabase> {\n const factory = this.factory;\n if (factory === undefined) {\n // A `TypeError`, which `classifyStorageError` reads as `denied`.\n return Promise.reject(new TypeError('This environment has no IndexedDB.'));\n }\n return new Promise<IDBDatabase>((resolve, reject) => {\n let request: IDBOpenDBRequest;\n try {\n request = factory.open(this.database, 1);\n } catch (error) {\n reject(error);\n return;\n }\n request.onupgradeneeded = () => {\n if (!request.result.objectStoreNames.contains(this.store)) {\n request.result.createObjectStore(this.store);\n }\n };\n request.onsuccess = () => {\n // Another tab asking for a newer version cannot proceed while\n // this connection is open, so it is given up rather than held.\n request.result.onversionchange = () => this.close();\n resolve(request.result);\n };\n request.onerror = () => reject(request.error ?? new Error('The database could not be opened.'));\n request.onblocked = () => reject(new Error('The database is open in another tab at a different version.'));\n });\n }\n}\n","import { of, skip, type Subscription } from 'rxjs';\n\nimport { debounced } from '../debounce';\nimport type { ReadableCell } from '../Input';\nimport { internalState, type InternalState } from '../InternalState';\nimport { resource, type Resource, type ResourceStatus } from '../resource';\nimport type { StorageAdapter } from './StorageAdapter';\n\nexport interface PersistedOptions<T> {\n /**\n * What the value is before anything has been read, and what it goes\n * back to when nothing was stored.\n *\n * Required, and that is the whole answer to the race between\n * hydration and the first frame: a screen reading this on the frame\n * it mounts gets the default, with `status` saying `loading`, and\n * gets the remembered value a moment later if there was one. There\n * is no fourth state to draw and no null to guard against.\n */\n readonly initial: T;\n /**\n * Reads a record back, refusing one this version does not\n * understand.\n *\n * Answer `null` and the record is treated as not being there, which\n * is what a record written by an older build should be: the cost is\n * one default, and the alternative is state holding `undefined`\n * where a field belongs. `Tokens.isStoredTokens` in Segue is the\n * same check written by hand.\n *\n * Without one the parsed JSON is trusted as-is, which is fine while\n * the shape has only ever had one version and is not fine\n * afterwards.\n */\n readonly revive?: (raw: unknown) => T | null;\n /**\n * How long a change waits for the next one before being written, in\n * milliseconds. Default 250.\n *\n * A `debounced` cell rather than a timer of its own, so this is the\n * same gate a search field uses. It is what makes a value changed\n * sixty times a second cost one write rather than sixty: see\n * `persisted.budget.spec.ts`, which asserts exactly that.\n */\n readonly settle?: number;\n /** What to call this in a devtools reading; optional. */\n readonly label?: string;\n}\n\nconst MESSAGES: Readonly<Record<string, string>> = {\n denied: 'This browser will not let the application store anything.',\n full: 'There is no room left to store this.',\n failed: 'The store could not be written to.'\n};\n\n/**\n * A value that survives the application being closed.\n *\n * Hydration is a `resource`, which is not a detail: reading from a\n * disk is a keyed request that can be slow, can answer \"there is\n * nothing\", and can fail, which is the same set of outcomes a request\n * over the network has. So the statuses here are `ResourceStatus`\n * itself rather than a fourth enum saying the same five things in\n * different words.\n *\n * readonly draft = persisted(new OpfsStorage(), 'draft', { initial: '' });\n *\n * // on a screen\n * <TextInput value={draft.value} onChange={text => draft.set(text)} />\n *\n * ## What a screen sees before hydration finishes\n *\n * The default, and `status` reading `loading`. Nothing waits, nothing\n * is null, and no screen has a shape it only has for the first eighty\n * milliseconds. When the read lands the value changes like any other\n * cell change, and a screen that wants to say \"restoring\" reads\n * `status`.\n *\n * The one race that needs a rule is a person changing the value before\n * the disk has answered, which is not rare: a queue is a press away\n * and OPFS is a round trip away. **What they did wins.** A hydration\n * answer is applied only if nothing has been `set` since, on the same\n * reasoning as `mutate`'s guarded rollback: an answer that was\n * overtaken is stale, and putting it on screen would undo something\n * the person just did.\n *\n * ## What happens when storing fails\n *\n * - `denied`: nothing is written this session and nothing is tried\n * again, because the answer will not change. `status` is `failed`\n * and `saveError` says so once rather than on every keystroke.\n * - `full`: the write failed and the value in memory is kept. Nothing\n * is rolled back: the change is real and only the remembering of it\n * failed. The next change is still attempted, because a quota can be\n * given back.\n * - `failed`: the same as `full`, and for the same reason.\n * - A record that parses but is not the shape `revive` accepts is\n * treated as `missing`: the application starts from its default\n * rather than showing an error about a file the person cannot see.\n *\n * ## It is a helper\n *\n * Nothing in the framework holds one, and nothing is reachable only\n * through it. A channel is served from plain Observables as it always\n * was, and an application that would rather read and write a store\n * itself is writing against the same `StorageAdapter` this is written\n * against. The thread model declined to own an application's data\n * architecture, and remembering a value is not the exception to that.\n */\nexport class PersistedState<T> {\n private readonly held: InternalState<T>;\n private readonly saves: InternalState<number>;\n private readonly failure: InternalState<string | null>;\n private readonly record: Resource<string, T>;\n private readonly writing: Subscription;\n /** True once the application has written a value of its own. */\n private touched = false;\n /** Set when the store said `denied`, which is permanent for the session. */\n private refused = false;\n /** The text last known to be on disk, so hydration does not write itself back. */\n private stored: string | null = null;\n /** What `forget` goes back to. */\n private readonly initial: T;\n\n /** Where the first read got to; the same five words `resource` uses. */\n readonly status: ReadableCell<ResourceStatus>;\n /** Why the read did not answer, as a message; null when it did. */\n readonly error: ReadableCell<string | null>;\n /** What is remembered: the default until the read lands. */\n readonly value: ReadableCell<T>;\n /** Writes in the air, for a saving indicator. A count, as `mutate.pending` is. */\n readonly saving: ReadableCell<number>;\n /** Why the last write did not happen, as a message; null when it did. */\n readonly saveError: ReadableCell<string | null>;\n /** Resolves when the first read has settled, whatever it found. */\n readonly hydrated: Promise<void>;\n\n constructor(\n private readonly adapter: StorageAdapter,\n private readonly key: string,\n options: PersistedOptions<T>\n ) {\n const label = options.label;\n this.initial = options.initial;\n this.held = internalState<T>(options.initial, label);\n this.saves = internalState(0, label === undefined ? undefined : `${label}.saving`);\n this.failure = internalState<string | null>(null, label === undefined ? undefined : `${label}.saveError`);\n this.value = this.held;\n this.saving = this.saves;\n this.saveError = this.failure;\n // A constant key, because the record being read is named once. The\n // resource is eager, so constructing this is what starts the read,\n // which is what \"hydration on start\" means: nothing has to\n // remember to call a `load()`.\n this.record = resource(\n of(key),\n () => this.load(options.revive),\n label === undefined ? {} : { label: `${label}.hydration` }\n );\n this.status = this.record.status;\n this.error = this.record.error;\n this.hydrated = this.record.settled.then(() => this.apply());\n this.writing = debounced(this.held, options.settle ?? 250)\n // The gate hands a new follower the value the cell already has,\n // which here is the default nobody asked to be written. Only\n // what comes after it is a change.\n .pipe(skip(1))\n .subscribe(value => void this.flush(value));\n }\n\n /** What is remembered right now, for code that is not subscribing. */\n get current(): T {\n return this.held.value;\n }\n\n /** Remembers a new value. The write follows once the changes stop. */\n set(value: T): void {\n this.touched = true;\n this.held.value = value;\n }\n\n /**\n * Writes what is held now, without waiting for the gate.\n *\n * For the moment an application knows it is about to lose the thread:\n * a `visibilitychange`, a route away from an editor, a sign-out.\n */\n save(): Promise<void> {\n return this.flush(this.held.value);\n }\n\n /**\n * Forgets the record and goes back to the default.\n *\n * Both halves, because a stored value removed while the cell still\n * holds it would be written straight back by the next change.\n */\n async forget(): Promise<void> {\n this.stored = null;\n this.touched = true;\n this.held.value = this.initial;\n await this.adapter.remove(this.key);\n }\n\n /** Gives back the write subscription and the resource's. */\n dispose(): void {\n this.writing.unsubscribe();\n this.record.dispose();\n }\n\n private async load(revive: ((raw: unknown) => T | null) | undefined): Promise<T | null> {\n const read = await this.adapter.read(this.key);\n if (read.outcome !== 'ok') {\n if (read.outcome === 'denied') {\n this.refused = true;\n }\n throw new Error(read.error ?? MESSAGES[read.outcome] ?? 'The store could not be read.');\n }\n if (read.value === null) {\n return null;\n }\n this.stored = read.value;\n let parsed: unknown;\n try {\n parsed = JSON.parse(read.value);\n } catch {\n // Half-written, or written by something else entirely. Treated\n // as nothing stored rather than as a failure: there is nothing a\n // person can do about it and a default is a working application.\n return null;\n }\n return revive === undefined ? (parsed as T) : revive(parsed);\n }\n\n private apply(): void {\n const found = this.record.value.value;\n if (found === null || this.touched) {\n // Nothing stored, or the person got there first. Theirs is the\n // newer truth and it stays.\n return;\n }\n this.held.value = found;\n }\n\n private async flush(value: T): Promise<void> {\n if (this.refused) {\n return;\n }\n let text: string;\n try {\n text = JSON.stringify(value);\n } catch (error) {\n this.failure.value = error instanceof Error ? error.message : String(error);\n return;\n }\n if (text === this.stored) {\n // Hydration writing what it just read, or a change that came\n // back round to where it started. Either way there is nothing\n // to say to the disk.\n return;\n }\n this.saves.value = this.saves.value + 1;\n try {\n const outcome = await this.adapter.write(this.key, text);\n if (outcome === 'ok') {\n this.stored = text;\n this.failure.value = null;\n return;\n }\n if (outcome === 'denied') {\n this.refused = true;\n }\n this.failure.value = MESSAGES[outcome] ?? 'The store could not be written to.';\n } finally {\n this.saves.value = Math.max(0, this.saves.value - 1);\n }\n }\n}\n\n/**\n * A value read from a store on start and written back as it changes.\n *\n * const settings = persisted(new IndexedDbStorage(), 'settings', {\n * initial: DEFAULTS,\n * revive: raw => (isSettings(raw) ? raw : null)\n * });\n *\n * See `PersistedState` for what a screen sees before the read lands\n * and what each kind of storage failure does.\n */\nexport function persisted<T>(adapter: StorageAdapter, key: string, options: PersistedOptions<T>): PersistedState<T> {\n return new PersistedState(adapter, key, options);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"persisted-IXHP0d-X.js","names":[],"sources":["../src/Input.ts","../src/InternalState.ts","../src/derive.ts","../src/computed.ts","../src/select.ts","../src/resource.ts","../src/mutate.ts","../src/debounce.ts","../src/channel/pick.ts","../src/storage/StorageAdapter.ts","../src/undo/UndoStack.ts","../src/undo/undoable.ts","../src/storage/OpfsStorage.ts","../src/storage/IndexedDbStorage.ts","../src/storage/persisted.ts"],"sourcesContent":["import { BehaviorSubject, type Observable, Subject } from 'rxjs';\n\n/**\n * Reactive cell holding a component input.\n *\n * Inputs must be cells because render() runs exactly once. A component\n * that read a plain input value during render would capture it for the\n * life of the instance, so a parent supplying new props under a\n * dynamic subtree would update the field while the rendered tree kept\n * showing the original value.\n *\n * The cell is written by the component host only: it accepts whatever\n * the parent passed, subscribing it first when the parent passed an\n * Observable. `value` is deliberately read-only, so `this.label.value =\n * x` inside a component is a compile error rather than a silent\n * violation of the one-way data flow.\n */\nexport class InputCell<T> extends BehaviorSubject<T> {\n /**\n * What to call this cell in a warning: `Component.prop`, or\n * `channel.key`. Set by whoever creates it; a cell without one is\n * reported as \"a cell\".\n */\n label: string | undefined;\n /** The component whose body read `.value`, for the stale-read warning below. */\n private snapshotBy: string | null = null;\n private warnedStale = false;\n /** Everything emitted through `emit`, created on the first `events` read. */\n private emitted: Subject<unknown> | null = null;\n\n constructor(initialValue: T) {\n super(initialValue);\n }\n\n /**\n * Fires the output this cell stands for.\n *\n * An output is a cell whose value is what the parent gave to be\n * called: a function, or a target made by `into(subject)`. `emit`\n * calls it with the arguments, and also pushes the first argument\n * through `events`, so the component can fire from three places\n * without passing the cell around and a parent that wants a stream\n * can have one. A parent that passed nothing is fine: the call\n * simply reaches nobody.\n */\n emit(...args: EmitArgs<T>): void {\n const handler = super.getValue() as unknown;\n if (typeof handler === 'function') {\n (handler as (...values: unknown[]) => void)(...args);\n }\n this.emitted?.next(args[0]);\n }\n\n /** What `emit` has fired, as a stream: the first argument of each call. */\n get events(): Observable<EmitValue<T>> {\n if (this.emitted === null) {\n this.emitted = new Subject<unknown>();\n }\n return this.emitted.asObservable() as Observable<EmitValue<T>>;\n }\n\n override get value(): T {\n trackRead(this);\n // A read a `computed` is collecting is not the body's snapshot: the\n // computed follows this cell from here on and carries the change,\n // and a body that read the computed itself is the computed's own\n // stale watch to report. Recording it here as well warned, wrongly,\n // for a screen row whose `computed` input had let go with the row.\n if (bodyOf !== null && tracking === null && this.snapshotBy === null) {\n this.snapshotBy = bodyOf;\n }\n return super.getValue();\n }\n\n /**\n * The one place the run-once model goes quietly wrong is a body that\n * reads `props.x.value`, uses the value to build the tree, and never\n * hears that it changed. Nothing crashes; the screen is simply stale.\n * So a cell remembers being read while a body ran, and if it later\n * changes with nobody subscribed to it, it says so once. A cell that\n * something is following is fine: the follower carries the change.\n */\n override next(value: T): void {\n if (this.snapshotBy !== null && !this.warnedStale && !this.observed && !Object.is(value, super.getValue())) {\n this.warnedStale = true;\n warnStaleRead(this.snapshotBy, this.label, super.getValue(), value);\n }\n super.next(value);\n }\n}\n\n/** The component whose function body is running, while one is. */\nlet bodyOf: string | null = null;\n\n/** The component whose body is running, for a cell that wants to remember being read there. */\nexport function currentBody(): string | null {\n return bodyOf;\n}\n\n/**\n * Anything with a current value that can be followed: an input, an\n * internal state, a channel view key, a computed. What `computed`\n * collects as it runs.\n */\nexport interface ReadableCell<T> extends Observable<T> {\n readonly value: T;\n}\n\n/** The set a running `computed` is collecting its reads into, while one is. */\nlet tracking: Set<ReadableCell<unknown>> | null = null;\n\n/** Records a `.value` read for whatever `computed` is running, if one is. */\nexport function trackRead(cell: ReadableCell<unknown>): void {\n tracking?.add(cell);\n}\n\n/** Runs `run` with every `.value` read on the way recorded into `into`. */\nexport function withTracking<T>(into: Set<ReadableCell<unknown>>, run: () => T): T {\n const previous = tracking;\n tracking = into;\n try {\n return run();\n } finally {\n tracking = previous;\n }\n}\n\n/**\n * Runs a component's body with its name on record, so a `.value` read\n * inside it can be told apart from one in an event handler later, which\n * is the ordinary way to read the current value and warns about nothing.\n */\nexport function withBodyOf<T>(tag: string, run: () => T): T {\n const previous = bodyOf;\n bodyOf = tag;\n try {\n return run();\n } finally {\n bodyOf = previous;\n }\n}\n\nfunction warnStaleRead(tag: string, label: string | undefined, from: unknown, to: unknown): void {\n const what = label === undefined ? 'a cell' : `\\`${label}\\``;\n const change = describeChange(from, to);\n console.warn(\n `Component '${tag}' read ${what} with .value while its body ran, and nothing is following that cell. ` +\n `It has since changed ${change}, and whatever was built from the first value still shows it. ` +\n `A component body runs once: bind the cell instead (pass it, or pipe it, into the prop it feeds), ` +\n `or give the component a key so a new value builds a new one.`\n );\n}\n\n/**\n * \"from X to Y\", or for two plain objects the first field that differs,\n * because two objects that print alike for sixty characters say nothing\n * about what actually moved.\n */\nfunction describeChange(from: unknown, to: unknown): string {\n if (isPlainObject(from) && isPlainObject(to)) {\n for (const key of new Set([...Object.keys(from), ...Object.keys(to)])) {\n const before = from[key];\n const after = to[key];\n if (!Object.is(before, after) && safeJson(before) !== safeJson(after)) {\n return `at .${key}, from ${describe(before)} to ${describe(after)}`;\n }\n }\n return 'to an equal-looking object';\n }\n return `from ${describe(from)} to ${describe(to)}`;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction safeJson(value: unknown): string {\n try {\n return JSON.stringify(value) ?? String(value);\n } catch {\n return String(value);\n }\n}\n\nfunction describe(value: unknown): string {\n let text: string;\n try {\n text = typeof value === 'function' ? 'a function' : (JSON.stringify(value) ?? String(value));\n } catch {\n text = String(value);\n }\n return text.length > 60 ? `${text.slice(0, 57)}...` : text;\n}\n\n/**\n * Creates a component input cell.\n *\n * Inside a class component the argument is the default the cell holds\n * until the parent supplies a value:\n *\n * @Input() label = input('Count');\n *\n * Inside a functional component the props are already cells; the\n * two-argument form derives a cell that replaces `undefined` with a\n * fallback, which is how an optional prop gets its default:\n *\n * function Counter(inputs: Inputs<{ label?: string }>) {\n * const label = input(inputs.label, 'Count'); // InputCell<string>\n * return Text({ text: label });\n * }\n *\n * The derived cell follows the source for the life of the component\n * and completes when the source does, so it needs no teardown.\n */\nexport function input<T>(initialValue: T): InputCell<T>;\nexport function input<T>(source: InputCell<T | undefined>, fallback: T): InputCell<T>;\nexport function input<T>(first: T | InputCell<T | undefined>, fallback?: T): InputCell<T> {\n if (arguments.length < 2 || !(first instanceof InputCell)) {\n return new InputCell(first as T);\n }\n const source = first as InputCell<T | undefined>;\n const withFallback = (value: T | undefined): T => (value === undefined ? (fallback as T) : value);\n const derived = new InputCell<T>(withFallback(source.value));\n derived.label = source.label;\n source.subscribe({\n next: value => derived.next(withFallback(value)),\n complete: () => derived.complete()\n });\n return derived;\n}\n\n/** The arguments `emit` takes for a cell holding a handler of type `T`. */\nexport type EmitArgs<T> = NonNullable<T> extends (...args: infer A) => void ? A : never;\n\n/** What `events` carries for a cell holding a handler of type `T`: the handler's first argument. */\nexport type EmitValue<T> = NonNullable<T> extends (first: infer V, ...rest: never[]) => void ? V : void;\n\n/**\n * A component's output: an input cell that holds whatever the parent\n * gave to be called, fired through `emit`. Every function-typed member\n * of a component's inputs is one, so on a component declared with\n * `onOpen: (id: string) => void`, `inputs.onOpen.emit(id)` is how the\n * component speaks, and a parent may pass a function or `into(subject)`.\n * The name is for a class field: `@Output() changed = output<[number]>()`.\n */\nexport type OutputCell<A extends unknown[]> = InputCell<((...args: A) => void) | undefined>;\n\n/**\n * An output a class component declares as a field:\n *\n * @Output() changed = output<[value: number]>();\n *\n * The host wires the parent's handler into it exactly as it wires an\n * input, and `this.changed.emit(next)` fires it.\n */\nexport function output<A extends unknown[]>(): OutputCell<A> {\n return new InputCell<((...args: A) => void) | undefined>(undefined);\n}\n\nconst OUTPUT_TARGET: unique symbol = Symbol('gesso:output-target');\n\n/**\n * A parent's way of receiving an output as a stream rather than a call:\n *\n * const opened = new Subject<string>();\n * <Card onOpen={into(opened)} />\n *\n * Wrapped rather than passed bare, because a bare `Subject` is an\n * Observable and would be read as an *input* the parent is feeding the\n * child, which is the opposite direction.\n */\nexport interface OutputTarget<V> {\n readonly [OUTPUT_TARGET]: { next(value: V): void };\n}\n\nexport function into<V>(target: { next(value: V): void }): OutputTarget<V> {\n return { [OUTPUT_TARGET]: target };\n}\n\nexport function isOutputTarget(value: unknown): value is OutputTarget<unknown> {\n return typeof value === 'object' && value !== null && OUTPUT_TARGET in value;\n}\n\n/** The receiver an `into()` target wraps. */\nexport function outputTargetOf<V>(target: OutputTarget<V>): { next(value: V): void } {\n return target[OUTPUT_TARGET];\n}\n","import { BehaviorSubject } from 'rxjs';\nimport { trackRead } from './Input';\n\n/**\n * A component's own state: originated here, and never crossing the\n * barrier.\n *\n * The writable counterpart to `InputCell`. The two are the same\n * `BehaviorSubject` and differ by one accessor — this one has a\n * `.value` setter — and that difference is the whole semantics:\n *\n * internalState() never crosses I write it\n * input() crosses inward someone else writes it\n *\n * Named on that axis deliberately. It used to be `state()`, which\n * described *what* a thing was while `input()` described *where it\n * came from*; two names on two axes made neither of them tell you\n * anything about the other. `internalState(products)` reads as a\n * mistake at the call site in a way `state(products)` never did.\n *\n * For values that originate on this thread and die with the component:\n * a tooltip's open flag, a caret, a scroll offset, the active tab.\n * Anything that survives a reload, or that another screen cares about,\n * is application state and belongs on a channel. Anything derived from\n * other cells is a `computed`.\n *\n * It is not only for components. The thread that owns a channel's data\n * writes cells too, and wrote them as a `BehaviorSubject` mirrored\n * into an `asObservable()` because this was reachable only through the\n * framework's main entry. `gesso-framework/worker` is the same cell\n * with none of the renderer behind it, so an application worker holds\n * one cell rather than a subject and a copy of it, and reads it with\n * `.value` in a `computed` rather than listing it in a\n * `combineLatest`. \"Internal\" still means what it says there: written\n * here, and crossing the barrier only as the plain data a view key\n * publishes.\n */\nexport class InternalState<T> extends BehaviorSubject<T> {\n /** What to call this cell in a warning or the inspector; optional. */\n label: string | undefined;\n constructor(initialValue: T) {\n super(initialValue);\n }\n\n override get value(): T {\n trackRead(this);\n return super.getValue();\n }\n\n override set value(next: T) {\n this.next(next);\n }\n}\n\n/**\n * Creates a reactive state cell.\n *\n * Usage inside a component:\n *\n * private readonly count = state(0);\n *\n * increment() {\n * this.count.value++;\n * }\n */\nexport function internalState<T>(initialValue: T, label?: string): InternalState<T> {\n const state = new InternalState(initialValue);\n if (label !== undefined) {\n state.label = label;\n }\n return state;\n}\n","import { combineLatest, distinctUntilChanged, map, type Observable } from 'rxjs';\n\nimport { structurallyEqual } from './channel/structuralEquals';\n\n/** How `derive` decides that a new value is not a change. */\nexport type Equality<T> = 'reference' | 'structural' | ((a: T, b: T) => boolean);\n\nexport interface DeriveOptions<T> {\n /**\n * `reference` (the default) is `Object.is`; `structural` compares\n * plain data by content, so a projection that builds a fresh object\n * of the same shape does not re-bind everything reading it; a\n * function is your own rule.\n */\n readonly equal?: Equality<T>;\n}\n\ntype Values<S extends readonly Observable<unknown>[]> = {\n [K in keyof S]: S[K] extends Observable<infer V> ? V : never;\n};\n\n/**\n * One value from several, kept equal to `project` of the latest of each\n * source, and emitted only when it changes.\n *\n * This is `combineLatest(...).pipe(map(...), distinctUntilChanged())`,\n * which is what nearly every derived binding in a component body wants\n * and what nearly every one had to write out. Sources are cells or any\n * Observables; the result is what a prop takes.\n *\n * const playing = derive([queue.view.playlistId, audio.state], (id, state) =>\n * id === card.id && state.status === 'playing'\n * );\n *\n * @deprecated Write it as a `computed`, which is the one derivation the\n * documentation teaches. A cell is read with\n * `.value` and anything else through the `read` the function is handed,\n * so the sources are the reads themselves and there is no list beside\n * the expression to keep in step with it:\n *\n * const playing = computed(read =>\n * queue.view.playlistId.value === card.id && read(audio.state).status === 'playing'\n * );\n *\n * It still works and nothing that uses it needs changing today.\n */\nexport function derive<S extends readonly Observable<unknown>[], T>(\n sources: readonly [...S],\n project: (...values: Values<S>) => T,\n options: DeriveOptions<T> = {}\n): Observable<T> {\n const equal = equalityOf(options.equal ?? 'reference');\n return combineLatest(sources as unknown as Observable<unknown>[]).pipe(\n map(values => project(...(values as Values<S>))),\n distinctUntilChanged(equal)\n );\n}\n\n/** The comparison an `Equality` names. Shared with `computed`. */\nexport function equalityOf<T>(equal: Equality<T>): (a: T, b: T) => boolean {\n if (equal === 'reference') {\n return Object.is;\n }\n if (equal === 'structural') {\n return (a, b) => structurallyEqual(a, b);\n }\n return equal;\n}\n","import { Observable, Subject, Subscription } from 'rxjs';\nimport { equalityOf, type Equality } from './derive';\nimport { currentBody, trackRead, withTracking, type ReadableCell } from './Input';\n\nexport interface ComputedOptions<T> {\n /** How a new result is judged unchanged; `reference` by default. */\n readonly equal?: Equality<T>;\n /** What to call this cell in a warning. */\n readonly label?: string;\n}\n\n/**\n * Reads a stream that is not a cell, inside a `computed`.\n *\n * A cell is read with `.value`; a plain Observable has no current value\n * to read, so the function is handed this instead:\n *\n * const playing = computed(read => read(audio.actions) === 'play');\n *\n * It answers with the stream's latest value and records the stream as a\n * source, so the computed follows it exactly as it follows a cell. A\n * cell passed to it is simply read, which means one call site works for\n * either and a service that later turns a stream into a cell breaks\n * nothing.\n */\nexport type ReadSource = <V>(source: Observable<V>) => V;\n\n/**\n * A cell whose value is a function of other cells.\n *\n * `computed(() => quantity.value * price.value)` reads like the value it\n * is. The cells its function reads through `.value` are its sources,\n * found by running the function and watching what it touches, so there\n * is no list to keep in step with the expression; a read the function\n * did not make this time is a source it no longer has.\n *\n * A stream that is not a cell is read through the `read` the function\n * is handed: `computed(read => read(stream).status)` follows the stream\n * as it follows a cell. That is what makes this the only derivation an\n * application needs, whether or not the thing it derives from happens\n * to have a current value of its own.\n *\n * It is a cell and nothing else. Bound to a prop it is an Observable\n * like every other cell, so a component written with it and one written\n * with `pipe` compose without translation. Read in a handler with\n * `.value` it is the current result, computed on the spot if nothing is\n * following it. RxJS is underneath and nothing here replaces it.\n *\n * Nothing runs until someone asks. With no subscriber the function runs\n * only when `.value` is read; with one, the cell follows its sources and\n * emits a result when it differs from the last by `equal`. When the last\n * subscriber leaves it lets go of its sources, so a computed made in a\n * component body dies with the component's bindings and needs no\n * disposal of its own.\n *\n * Reading it with `.value` while a component body runs is the same\n * snapshot an input read there is, and it warns the same way: once, if\n * it later changes with nobody following.\n */\nexport class ComputedCell<T> extends Observable<T> implements ReadableCell<T> {\n label: string | undefined;\n private readonly equal: (a: T, b: T) => boolean;\n private readonly changes = new Subject<T>();\n private sources = new Set<ReadableCell<unknown>>();\n private cached!: T;\n private hasValue = false;\n private upstream: Subscription | null = null;\n private subscribers = 0;\n private attaching = false;\n private snapshotBy: string | null = null;\n private warnedStale = false;\n /** Follows the sources after a body read, only to notice the change the body will not. */\n private staleWatch: Subscription | null = null;\n\n constructor(\n private readonly compute: (read: ReadSource) => T,\n options: ComputedOptions<T> = {}\n ) {\n super(subscriber => {\n this.subscribers++;\n // A follower makes the body-read watch moot: the change reaches the screen.\n this.staleWatch?.unsubscribe();\n this.staleWatch = null;\n if (this.upstream === null) {\n this.attach();\n }\n subscriber.next(this.cached);\n const following = this.changes.subscribe(subscriber);\n return () => {\n following.unsubscribe();\n this.subscribers--;\n if (this.subscribers === 0) {\n this.detach();\n }\n };\n });\n this.equal = equalityOf(options.equal ?? 'reference');\n this.label = options.label;\n }\n\n /** The current result: kept by the sources while followed, computed now when not. */\n get value(): T {\n trackRead(this);\n if (this.upstream === null) {\n this.recompute();\n }\n const body = currentBody();\n if (body !== null && this.snapshotBy === null) {\n this.snapshotBy = body;\n this.watchForStaleRead();\n }\n return this.cached;\n }\n\n /**\n * A body read the value once; if a source now changes with nothing\n * following this cell, the screen built from that read is stale and\n * nobody would know. So the sources are watched for exactly that, and\n * the watch ends with the warning or with a real subscriber arriving.\n */\n private watchForStaleRead(): void {\n this.staleWatch = new Subscription();\n let settling = true;\n for (const source of this.sources) {\n this.staleWatch.add(\n source.subscribe(() => {\n if (settling || this.observed) {\n return;\n }\n if (this.recompute()) {\n this.warnStale();\n this.staleWatch?.unsubscribe();\n this.staleWatch = null;\n }\n })\n );\n }\n settling = false;\n }\n\n private warnStale(): void {\n if (this.snapshotBy === null || this.warnedStale) {\n return;\n }\n this.warnedStale = true;\n console.warn(\n `Component '${this.snapshotBy}' read ${this.label === undefined ? 'a computed cell' : `\\`${this.label}\\``} with .value while its body ran, ` +\n `and nothing is following that cell. It has since changed, and whatever was built from the first value still shows it. ` +\n `A component body runs once: bind the cell instead.`\n );\n }\n\n /** Whether anything is following this cell; the stale-read warning's question. */\n get observed(): boolean {\n return this.subscribers > 0;\n }\n\n /** Runs the function, watching what it reads; true when the result changed. */\n private recompute(): boolean {\n const touched = new Set<ReadableCell<unknown>>();\n const next = withTracking(touched, () => this.compute(readSource));\n this.sources = touched;\n const changed = !this.hasValue || !this.equal(this.cached, next);\n this.hasValue = true;\n this.cached = next;\n return changed;\n }\n\n /** Follows the current sources, re-running on any change to them. */\n private attach(): void {\n this.attaching = true;\n this.recompute();\n this.upstream = new Subscription();\n for (const source of this.sources) {\n this.upstream.add(source.subscribe(() => this.onSourceChanged()));\n }\n this.attaching = false;\n }\n\n private detach(): void {\n this.upstream?.unsubscribe();\n this.upstream = null;\n }\n\n private onSourceChanged(): void {\n if (this.attaching) {\n return; // a cell replays its current value on subscribe; that is not a change\n }\n const before = new Set(this.sources);\n const changed = this.recompute();\n if (!sameSet(before, this.sources)) {\n // The function read different cells this time: follow those.\n this.detach();\n this.attaching = true;\n this.upstream = new Subscription();\n for (const source of this.sources) {\n this.upstream.add(source.subscribe(() => this.onSourceChanged()));\n }\n this.attaching = false;\n }\n if (changed) {\n this.changes.next(this.cached);\n }\n }\n}\n\n/**\n * A cell computed from what its function reads. See `ComputedCell`.\n *\n * const total = computed(() => quantity.value * PRICE * RATES[currency.value]);\n * <text text={computed(() => String(count.value))} />\n *\n * Cells are read with `.value`; anything else is read through the\n * `read` the function is given, which follows it the same way:\n *\n * const late = computed(read => read(clock) > deadline);\n */\nexport function computed<T>(compute: (read: ReadSource) => T, options: ComputedOptions<T> = {}): ComputedCell<T> {\n return new ComputedCell(compute, options);\n}\n\n/**\n * The cell standing for a stream, one per stream.\n *\n * Anything that already has a current value is its own cell, so a\n * `read` of an input, an internal state or another computed costs a\n * property access and nothing more. Everything else gets a `StreamCell`\n * held against it here, so several computeds reading one stream share a\n * single subscription to it rather than opening one each.\n *\n * Weak on purpose: the entry is reachable only while the stream is, so\n * a stream made in a component body is collected with the component.\n */\nconst streamCells = new WeakMap<Observable<unknown>, StreamCell<unknown>>();\n\n/**\n * Whether a source's `.value` announces itself to the running computed,\n * decided once per source and remembered.\n *\n * Having a `value` is not enough. A framework cell records its reads\n * through `trackRead`, which is what lets a computed learn what it\n * depends on; a plain `BehaviorSubject` has a `value` too and records\n * nothing, so a computed that trusted the property would read it once\n * and never hear it change. That is exactly what happened to three\n * data-layer specs that fed a raw subject where the application feeds\n * a channel view. The probe reads `.value` once under a tracking set\n * of its own and asks whether the source turned up in it.\n */\nconst tracksReads = new WeakMap<Observable<unknown>, boolean>();\n\nfunction announcesItsReads(source: ReadableCell<unknown>): boolean {\n let known = tracksReads.get(source);\n if (known === undefined) {\n const seen = new Set<ReadableCell<unknown>>();\n withTracking(seen, () => void source.value);\n known = seen.has(source);\n tracksReads.set(source, known);\n }\n return known;\n}\n\nfunction cellFor<T>(source: Observable<T>): ReadableCell<T> {\n if ('value' in source && announcesItsReads(source as ReadableCell<T>)) {\n return source as ReadableCell<T>;\n }\n let cell = streamCells.get(source as Observable<unknown>);\n if (cell === undefined) {\n cell = new StreamCell(source as Observable<unknown>);\n streamCells.set(source as Observable<unknown>, cell);\n }\n return cell as ReadableCell<T>;\n}\n\n/** The `read` every computed's function is handed. */\nconst readSource: ReadSource = <V>(source: Observable<V>): V => cellFor(source).value;\n\n/**\n * A plain stream, seen as a cell.\n *\n * It holds the last value it saw and hands it to whoever asks, which is\n * the one thing a cell has and an Observable does not. While something\n * follows it, it follows the stream; when the last follower leaves it\n * lets go, so it costs nothing between uses and needs no disposal, on\n * the same terms as `ComputedCell`.\n *\n * A `.value` read with nothing following takes one synchronous\n * subscription and drops it again, which is how a `BehaviorSubject`\n * behind an `asObservable()`, or a `combineLatest` over such subjects,\n * answers with what it already holds. A stream that has nothing to say\n * synchronously answers `undefined` until its first emission arrives,\n * which is the honest answer: there is no value yet.\n */\nclass StreamCell<T> extends Observable<T> implements ReadableCell<T> {\n private last!: T;\n private followers = 0;\n private upstream: Subscription | null = null;\n private readonly changes = new Subject<T>();\n\n constructor(private readonly stream: Observable<T>) {\n super(subscriber => {\n this.followers++;\n if (this.upstream === null) {\n this.attach();\n }\n subscriber.next(this.last);\n const following = this.changes.subscribe(subscriber);\n return () => {\n following.unsubscribe();\n this.followers--;\n if (this.followers === 0) {\n this.upstream?.unsubscribe();\n this.upstream = null;\n }\n };\n });\n }\n\n get value(): T {\n trackRead(this);\n if (this.upstream === null) {\n const asking = this.stream.subscribe(value => {\n this.last = value;\n });\n asking.unsubscribe();\n }\n return this.last;\n }\n\n private attach(): void {\n this.upstream = this.stream.subscribe(value => {\n this.last = value;\n this.changes.next(value);\n });\n }\n}\n\nfunction sameSet<T>(a: ReadonlySet<T>, b: ReadonlySet<T>): boolean {\n if (a.size !== b.size) {\n return false;\n }\n for (const item of a) {\n if (!b.has(item)) {\n return false;\n }\n }\n return true;\n}\n","import type { Observable } from 'rxjs';\n\nimport { computed, type ComputedCell } from './computed';\nimport type { Equality } from './derive';\n\nexport interface SelectOptions<T> {\n /**\n * How a projected value is judged unchanged; `structural` by default,\n * because a projection nearly always builds a value rather than\n * passing one through, and a rebuilt equal one is not a change.\n */\n readonly equal?: Equality<T>;\n /** What to call the cell in a warning. */\n readonly label?: string;\n}\n\n/**\n * One field of a cell, or one projection of it, as a cell.\n *\n * const title = select(inputs.track, 'title');\n * const names = select(inputs.track, entry => entry?.tags ?? []);\n *\n * Reading four fields of one input costs four of these rather than four\n * `pipe(map(...), distinctUntilChanged(...))`, which is what the two\n * applications wrote by hand often enough to invent their own `field()`\n * and `text()` helpers for it.\n *\n * The source may be any Observable, not only a cell: `select` is a\n * `computed` over one source, and reads it the way a computed reads\n * anything. The comparison is structural by default, since the reason\n * for nearly every hand-written comparator is exactly this: a\n * projection that rebuilds an equal array or object each time should\n * not re-bind everything reading it.\n *\n * There is no `inputs.track.title` proxy. A cell is an Observable, so\n * its own members (`value`, `pipe`, `subscribe`, `source`) would shadow\n * the fields of anything projected through it, and a data type that\n * happens to have a field called `value` would read as the cell's\n * current value instead. `select` names the field explicitly and cannot\n * collide.\n */\nexport function select<T extends object, K extends keyof T>(\n source: Observable<T>,\n key: K,\n options?: SelectOptions<T[K]>\n): ComputedCell<T[K]>;\nexport function select<T, R>(\n source: Observable<T>,\n project: (value: T) => R,\n options?: SelectOptions<R>\n): ComputedCell<R>;\nexport function select(\n source: Observable<unknown>,\n keyOrProject: PropertyKey | ((value: unknown) => unknown),\n options: SelectOptions<unknown> = {}\n): ComputedCell<unknown> {\n const project =\n typeof keyOrProject === 'function'\n ? keyOrProject\n : // A stream that has not spoken yet has no value to read a field\n // off, and neither has a source that is legitimately null. Both\n // answer `undefined` rather than throwing on the first frame.\n (value: unknown) =>\n value === null || value === undefined ? undefined : (value as Record<PropertyKey, unknown>)[keyOrProject];\n return computed(read => project(read(source)), {\n equal: options.equal ?? 'structural',\n ...(options.label === undefined ? {} : { label: options.label })\n });\n}\n","import { Subscription, type Observable } from 'rxjs';\n\nimport type { ComputedCell } from './computed';\nimport type { ReadableCell } from './Input';\nimport { internalState, type InternalState } from './InternalState';\nimport { select } from './select';\n\n/**\n * What a request has come to, in the five words every screen needs.\n *\n * Chosen once so screens stop inventing them. The tree had three\n * enums saying nearly the same thing in different words\n * (`'loading' | 'ready' | 'empty' | 'failed'`,\n * `'idle' | 'loading' | 'ready' | 'missing' | 'failed'`,\n * `'idle' | 'searching' | 'done' | 'failed'`), which meant a screen\n * reading two of them had two vocabularies for one idea.\n *\n * - `idle`: nothing has been asked for. The key is `null`.\n * - `loading`: a request is out and there is nothing to show yet.\n * - `ready`: there is a value. A refresh may still be in the air, and\n * that is deliberately not a separate status: what is on screen is\n * real, and saying \"loading\" over it would be a lie.\n * - `missing`: the answer was that there is no such thing. A fetch\n * says so by resolving `null`.\n * - `failed`: the request could not be answered at all.\n *\n * `missing` and `failed` are reported only when there is nothing to\n * fall back on. A refresh that fails over a value already held leaves\n * it on screen and stays `ready`, because a stale answer is stale and\n * not wrong, and an empty page would be worse. The error is still\n * there to read.\n *\n * What is *not* here is `empty`. A list that loaded and has no rows is\n * `ready` with an empty array, which is a judgement about the value\n * rather than about the request, and the screen that cares makes it.\n */\nexport type ResourceStatus = 'idle' | 'loading' | 'ready' | 'missing' | 'failed';\n\n/**\n * A resource as one record, so it can be a channel view key.\n *\n * Plain data throughout: the error is its message rather than the\n * `Error`, because a message is what a screen shows and what survives\n * the barrier, and an `Error` is neither.\n */\nexport interface ResourceState<T> {\n readonly status: ResourceStatus;\n readonly value: T | null;\n readonly error: string | null;\n}\n\nexport interface ResourceOptions<K, T> {\n /**\n * What is already known for this key, shown at once while the\n * request runs behind it.\n *\n * This is the store-first rule in one function: a hit is on screen\n * synchronously and marked `ready` even though a request is on its\n * way, so a page opened twice is instant the second time and correct\n * a moment later. Answering `null` or `undefined` means nothing is\n * held, and the resource says `loading`.\n */\n readonly peek?: (key: K) => T | null | undefined;\n /** What to call this resource in a warning; optional. */\n readonly label?: string;\n}\n\n/**\n * A request, keyed, so a stale answer cannot win.\n *\n * The key says what to fetch, and every value the key source emits is\n * a request. An answer is published only if its request is still the\n * current one, which is the generation counter every screen that loads\n * anything was writing by hand, and the reason opening a page, going\n * back and opening another before the first answers does not end with\n * the first answer on screen.\n *\n * private readonly ref = internalState<PageRef | null>(null);\n * readonly page = resource(this.ref, ref => api.trackPage(ref));\n *\n * show(ref: PageRef | null): Promise<void> {\n * this.ref.value = ref;\n * return this.page.settled;\n * }\n *\n * A `null` key is \"nothing is being asked for\": the status is `idle`,\n * the value is `null`, and no fetch runs. That is what a screen showing\n * nothing yet actually means, and it saves every caller a branch.\n *\n * Unlike `computed`, a resource is eager: it follows its key from the\n * moment it is made, because a request is an effect and an effect that\n * waits for a subscriber is a request that never happens. It takes one\n * subscription to the key source for its whole life, however many\n * requests run through it, and `dispose()` gives that back.\n *\n * It is a helper and not a data layer. Nothing in the framework\n * requires one, a channel is reached exactly as it was, and an\n * application that would rather write its own is writing against the\n * same barrier this is written against.\n */\nexport class Resource<K, T> {\n /** The one cell everything else here is a projection of. */\n private readonly cell: InternalState<ResourceState<T>>;\n /** Bumped per request; an answer from an older one is dropped. */\n private requests = 0;\n private asked: K | null = null;\n private settling: Promise<void> = Promise.resolve();\n private readonly following: Subscription;\n\n /** Status, value and error as one record: what a channel view key takes. */\n readonly state: ReadableCell<ResourceState<T>>;\n readonly status: ComputedCell<ResourceStatus>;\n /** What is loaded, or `null` while there is nothing to show. */\n readonly value: ComputedCell<T | null>;\n /** Why the last request failed, as its message; `null` when it did not. */\n readonly error: ComputedCell<string | null>;\n\n constructor(\n key: Observable<K | null | undefined>,\n private readonly fetch: (key: K) => Promise<T | null>,\n private readonly options: ResourceOptions<K, T> = {}\n ) {\n this.cell = internalState<ResourceState<T>>({ status: 'idle', value: null, error: null }, options.label);\n this.state = this.cell;\n this.status = select(this.cell, 'status', named(options.label, 'status'));\n // Compared by identity rather than structurally: a fetch builds a\n // new value every time, and walking a page-sized object to\n // discover that is exactly the cost the differ already pays once.\n this.value = select(this.cell, 'value', { ...named(options.label, 'value'), equal: 'reference' });\n this.error = select(this.cell, 'error', named(options.label, 'error'));\n this.following = key.subscribe(next => this.request(next ?? null));\n }\n\n /** What is being asked for, for a caller that needs to guard on it. */\n get requested(): K | null {\n return this.asked;\n }\n\n /**\n * The request in the air, as a promise that resolves when it settles.\n *\n * Already resolved when nothing is in flight, so a caller that sets\n * the key and returns this reads as an ordinary async method. Each\n * request keeps its own promise, so a dropped one still resolves for\n * whoever is awaiting it; it simply changes nothing on the way.\n */\n get settled(): Promise<void> {\n return this.settling;\n }\n\n /**\n * Asks again for the same key, keeping what is on screen.\n *\n * This is the retry button. It does not clear the value the way a\n * new key does, because a person pressing retry is asking for the\n * thing they can already see to be brought up to date, and blanking\n * it first would be a worse answer than the stale one.\n */\n retry(): Promise<void> {\n if (this.asked === null) {\n return this.settling;\n }\n const generation = ++this.requests;\n const held = this.cell.value;\n if (held.value === null && held.status !== 'loading') {\n this.write({ status: 'loading', value: null, error: null });\n }\n this.settling = this.run(this.asked, generation);\n return this.settling;\n }\n\n /**\n * Replaces what is loaded, for a change made here rather than\n * fetched.\n *\n * A page of comments appended to the answer already held, an\n * optimistic edit: the resource holds the value, so something has to\n * be able to write it. It does not touch the request in flight, so a\n * refresh that lands afterwards still wins, which is what it should\n * do: it is the newer truth.\n */\n set(value: T): void {\n this.write({ status: 'ready', value, error: null });\n }\n\n /** Gives back the subscription to the key source. */\n dispose(): void {\n this.following.unsubscribe();\n }\n\n private request(key: K | null): void {\n this.asked = key;\n const generation = ++this.requests;\n if (key === null) {\n this.write({ status: 'idle', value: null, error: null });\n this.settling = Promise.resolve();\n return;\n }\n const held = this.options.peek?.(key) ?? null;\n this.write({ status: held === null ? 'loading' : 'ready', value: held, error: null });\n this.settling = this.run(key, generation);\n }\n\n private run(key: K, generation: number): Promise<void> {\n return this.fetch(key).then(\n answer => this.answered(generation, answer),\n (error: unknown) => this.refused(generation, error)\n );\n }\n\n private answered(generation: number, answer: T | null): void {\n if (generation !== this.requests) {\n return;\n }\n if (answer === null) {\n const held = this.cell.value.value;\n this.write({ status: held === null ? 'missing' : 'ready', value: held, error: null });\n return;\n }\n this.write({ status: 'ready', value: answer, error: null });\n }\n\n private refused(generation: number, error: unknown): void {\n if (generation !== this.requests) {\n return;\n }\n const held = this.cell.value.value;\n this.write({ status: held === null ? 'failed' : 'ready', value: held, error: messageOf(error) });\n }\n\n private write(next: ResourceState<T>): void {\n this.cell.value = next;\n }\n}\n\n/**\n * A keyed request with a status, a value, an error and a retry.\n *\n * const page = resource(ref, key => api.page(key), { peek: key => store.get(key) });\n * <Show when={computed(() => page.status.value === 'loading')}>{() => <Spinner />}</Show>\n *\n * See `Resource` for what each status means and when a stale value is\n * kept. The key is an Observable so that setting a cell is what asks\n * for something: `computed` and `internalState` both work, and so does\n * a channel view key or a router match.\n */\nexport function resource<K, T>(\n key: Observable<K | null | undefined>,\n fetch: (key: K) => Promise<T | null>,\n options: ResourceOptions<K, T> = {}\n): Resource<K, T> {\n return new Resource(key, fetch, options);\n}\n\nfunction named(base: string | undefined, part: string): { label?: string } {\n return base === undefined ? {} : { label: `${base}.${part}` };\n}\n\nfunction messageOf(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n","import { equalityOf, type Equality } from './derive';\nimport type { ReadableCell } from './Input';\nimport { internalState, type InternalState } from './InternalState';\n\nexport interface MutateOptions<T> {\n /**\n * How the rollback decides the cell still holds what was applied;\n * `structural` by default, because an optimistic change nearly\n * always builds a new list or a new record rather than passing one\n * through, so identity would never match and nothing would ever roll\n * back.\n */\n readonly equal?: Equality<T>;\n /** What to call the in-flight count in a warning; optional. */\n readonly label?: string;\n}\n\n/** A change that is made locally at once and confirmed afterwards. */\nexport interface Mutation<A> {\n /**\n * How many writes are in the air, for a saving indicator.\n *\n * A count rather than a flag, because two presses in quick\n * succession are two writes and a flag cleared by the first would\n * say the second had finished.\n */\n readonly pending: ReadableCell<number>;\n /**\n * Applies the change, commits it, and puts it back if the commit\n * refuses. Answers whether it stuck.\n */\n run(argument: A): Promise<boolean>;\n}\n\n/**\n * An optimistic change to a cell, with a rollback that does not fight\n * the person.\n *\n * private readonly favourites = internalState<readonly string[]>([]);\n * private readonly like = mutate(this.favourites, toggled, id => api.favourite(id));\n *\n * toggle(id: string): void {\n * void this.like.run(id);\n * }\n *\n * Three things happen and the order is the whole point. `apply` runs\n * first and writes the cell, so the screen changes on the press rather\n * than a round trip later. `commit` then does the real write. If it\n * rejects, or resolves `false`, the cell goes back to what it held\n * before.\n *\n * **The rollback is guarded.** It happens only while the cell still\n * holds exactly what `apply` wrote. Without that, a slow rejection\n * would fight a fast second press and the cell would end up saying the\n * opposite of the last thing anyone did, which is the guard every\n * optimistic screen writes by hand and half of them get wrong.\n *\n * The cell is named first, and not because it was first written\n * `mutate(apply, commit)`: the cell is what makes the guard possible.\n * A mutation handed only two functions can undo its own change but\n * cannot tell whether undoing it is still the right thing to do.\n *\n * A helper and not a data layer: nothing in the framework requires\n * one, and an application that would rather write the four lines out\n * is writing the same four lines this does.\n */\nexport function mutate<T, A = void>(\n cell: InternalState<T>,\n apply: (current: T, argument: A) => T,\n commit: (argument: A, applied: T) => Promise<unknown>,\n options: MutateOptions<T> = {}\n): Mutation<A> {\n const equal = equalityOf<T>(options.equal ?? 'structural');\n const inFlight = internalState(0, options.label);\n\n const revert = (before: T, applied: T): void => {\n if (!equal(cell.value, applied)) {\n // Changed again since, by whoever is using the screen. Their\n // change is newer than this answer and wins.\n return;\n }\n cell.value = before;\n };\n\n return {\n pending: inFlight,\n async run(argument: A): Promise<boolean> {\n const before = cell.value;\n const applied = apply(before, argument);\n cell.value = applied;\n inFlight.value = inFlight.value + 1;\n try {\n // `false` and a rejection mean the same thing: the write did\n // not happen. Anything else, `undefined` included, is a\n // commit that went through.\n if ((await commit(argument, applied)) === false) {\n revert(before, applied);\n return false;\n }\n return true;\n } catch {\n revert(before, applied);\n return false;\n } finally {\n inFlight.value = Math.max(0, inFlight.value - 1);\n }\n }\n };\n}\n","import {\n debounceTime,\n Observable,\n Subject,\n Subscription,\n tap,\n throttleTime,\n type MonoTypeOperatorFunction\n} from 'rxjs';\n\nimport { trackRead, type ReadableCell } from './Input';\n\n/**\n * A cell that lets its source through on a timer.\n *\n * The two operators below are the same machinery with a different\n * gate, and both are cells rather than plain streams on purpose: a\n * `computed` reads a cell with `.value` and follows it, so a debounced\n * search term composes with everything else in the dialect instead of\n * being the one value in a screen that has to be piped.\n *\n * It holds the last value the gate let through, which is what `.value`\n * answers while something is following it. With nothing following\n * there is no timer running to hold anything back, so `.value` reads\n * the source directly, which is the honest answer rather than a value\n * frozen at whatever moment the last follower left.\n *\n * A value equal to the one it already holds is not a change and is not\n * emitted, the same rule `computed` follows. Without it the first pass\n * of the gate after a subscription would repeat the value the\n * subscriber had just been handed.\n *\n * One subscription upstream however many followers it has, given back\n * when the last of them leaves, on the same terms as `ComputedCell`.\n */\nclass TimedCell<T> extends Observable<T> implements ReadableCell<T> {\n private current!: T;\n private hasCurrent = false;\n private followers = 0;\n private seeding = false;\n private upstream: Subscription | null = null;\n private readonly changes = new Subject<T>();\n\n constructor(\n private readonly stream: Observable<T>,\n private readonly gate: MonoTypeOperatorFunction<T>\n ) {\n super(subscriber => {\n this.followers++;\n if (this.upstream === null) {\n this.attach();\n }\n subscriber.next(this.current);\n const following = this.changes.subscribe(subscriber);\n return () => {\n following.unsubscribe();\n this.followers--;\n if (this.followers === 0) {\n this.upstream?.unsubscribe();\n this.upstream = null;\n }\n };\n });\n }\n\n get value(): T {\n trackRead(this);\n if (this.upstream === null) {\n const asking = this.stream.subscribe(value => {\n this.current = value;\n this.hasCurrent = true;\n });\n asking.unsubscribe();\n }\n return this.current;\n }\n\n private attach(): void {\n // Seeds the current value from whatever the source already holds,\n // so a cell behind a gate has a value on the first frame rather\n // than after the first delay. Only during the subscribe itself: a\n // value arriving later is a change, and absorbing it here would\n // let it past the gate without telling anybody.\n this.seeding = true;\n this.upstream = this.stream\n .pipe(\n tap(value => {\n if (this.seeding && !this.hasCurrent) {\n this.current = value;\n this.hasCurrent = true;\n }\n }),\n this.gate\n )\n .subscribe(value => {\n if (this.hasCurrent && Object.is(this.current, value)) {\n return;\n }\n this.current = value;\n this.hasCurrent = true;\n this.changes.next(value);\n });\n this.seeding = false;\n }\n}\n\n/**\n * A cell that follows its source once it has stopped moving.\n *\n * const query = internalState('');\n * const term = debounced(query, 200);\n * const results = computed(() => index.search(term.value));\n *\n * Nothing is emitted while values keep arriving; `ms` after the last\n * one, the last one is. This is what a search field wants and what\n * every search field in the tree was reaching the router without: a\n * keystroke is not a question, and a pause is.\n *\n * The value the source already held is there immediately, so a screen\n * built from this draws on the first frame rather than `ms` later.\n */\nexport function debounced<T>(source: Observable<T>, ms: number): ReadableCell<T> {\n return new TimedCell(source, debounceTime<T>(ms));\n}\n\n/**\n * A cell that follows its source at most once every `ms`.\n *\n * const position = throttled(scrollOffset, 100);\n *\n * The first value goes straight through and the last of a burst\n * follows at the end of the window, so a value that arrives while the\n * window is open is late rather than lost. That pairing is what makes\n * this usable for a position or a progress reading, where the\n * beginning and the end of a movement both matter and the middle does\n * not.\n *\n * Use this for something that is continuously true, and `debounced`\n * for something a person has finished saying.\n */\nexport function throttled<T>(source: Observable<T>, ms: number): ReadableCell<T> {\n return new TimedCell(source, throttleTime<T>(ms, undefined, { leading: true, trailing: true }));\n}\n","import { distinctUntilChanged, map, type Observable } from 'rxjs';\n\n/**\n * One key of a view model, as its own Observable, emitting only when\n * that key's value changes.\n *\n * `provide` and `serveChannels` want one Observable per view key, so\n * the differ can patch each key on its own, while a view model is most\n * naturally one Observable of one object. This is the seam between the\n * two, and every application worker was about to write it.\n */\nexport function pick<T, K extends keyof T>(source: Observable<T>, key: K): Observable<T[K]> {\n return source.pipe(\n map(value => value[key]),\n distinctUntilChanged()\n );\n}\n\n/**\n * Every key of a view model as its own Observable: the `view` a channel\n * source wants, from the one Observable a view model has.\n *\n * serveChannels([{ token: Queue, source: { view: pickKeys(queue.view, QUEUE_KEYS), commands } }])\n */\nexport function pickKeys<T extends object, K extends keyof T>(\n source: Observable<T>,\n keys: readonly K[]\n): { readonly [P in K]: Observable<T[P]> } {\n const out: Partial<{ [P in K]: Observable<T[P]> }> = {};\n for (const key of keys) {\n out[key] = pick(source, key);\n }\n return out as { readonly [P in K]: Observable<T[P]> };\n}\n","/**\n * What a store did with a request.\n *\n * Four words rather than an exception, because three of them are\n * ordinary outcomes an application routes around rather than faults it\n * reports. A browser that refuses storage in a private window, and a\n * quota that has run out, are both things a running application has to\n * carry on through, and making every caller wrap a `try` around a read\n * to discover which one happened is how storage code ends up assuming\n * success.\n *\n * - `ok`: the store answered. For a read that includes \"there is no\n * such record\", which is `value: null` and not a failure.\n * - `denied`: the platform will not let this origin store anything.\n * No OPFS in this browser, a private window that refuses IndexedDB,\n * `localStorage` blocked by a site setting. Permanent for the\n * session: nothing an application does will change the answer, so\n * asking again is wasted work.\n * - `full`: the quota is spent. Temporary, and worth trying again\n * after something has been given back, which is why it is not\n * `denied`.\n * - `failed`: anything else. A half-written file, a corrupt database,\n * a transaction that aborted for a reason the platform did not\n * explain.\n */\nexport type StorageOutcome = 'ok' | 'denied' | 'full' | 'failed';\n\n/** What came back from a read. */\nexport interface StorageRead {\n readonly outcome: StorageOutcome;\n /** The record, or null when there is none or the read did not answer. */\n readonly value: string | null;\n /** Why it did not answer, as a message; null when it did. */\n readonly error: string | null;\n}\n\n/**\n * Somewhere an application's state survives being closed.\n *\n * Four methods over text keyed by a string, which is the shape all\n * three implementations can actually keep. Bytes are deliberately not\n * in it: `localStorage` cannot hold them, so an interface that\n * promised them would be one the shell route could not implement, and\n * an application storing pictures wants a store of its own with an\n * eviction policy rather than this. the\n * artwork cache is that other thing.\n *\n * Nothing rejects. Every method answers with an outcome, and the\n * per-failure behaviour is the implementation's to document; the three\n * in this package all follow `classifyStorageError`.\n *\n * It is a shape and not a service. Nothing in the framework holds one,\n * `persisted` takes whichever one it is given, and an application with\n * its own store implements four methods rather than adopting anything.\n */\nexport interface StorageAdapter {\n /** Reads one record. */\n read(key: string): Promise<StorageRead>;\n /** Writes one record, replacing whatever was there. */\n write(key: string, value: string): Promise<StorageOutcome>;\n /** Removes one record. Removing what is not there is `ok`. */\n remove(key: string): Promise<StorageOutcome>;\n /** Every key this store holds, for an application that evicts its own. */\n keys(): Promise<readonly string[]>;\n}\n\n/**\n * Which of the four outcomes a thrown platform error is.\n *\n * The names are the ones the storage APIs actually throw.\n * `QuotaExceededError` is the DOM's word for full, and every browser\n * uses it for OPFS, IndexedDB and `localStorage` alike. `SecurityError`\n * and `NotAllowedError` are what a blocked origin gets. A missing API\n * (no `navigator.storage`, no `indexedDB`) is a `TypeError` here and\n * is `denied` for the same reason: nothing the application does will\n * produce a store.\n */\nexport function classifyStorageError(error: unknown): StorageOutcome {\n const name = error instanceof Error ? error.name : '';\n if (name === 'QuotaExceededError' || name === 'NS_ERROR_DOM_QUOTA_REACHED') {\n return 'full';\n }\n if (name === 'SecurityError' || name === 'NotAllowedError' || name === 'TypeError') {\n return 'denied';\n }\n return 'failed';\n}\n\n/** A thrown value as the message a screen could show. */\nexport function storageErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\n/** A read that could not answer, as one record. */\nexport function storageReadFailure(error: unknown): StorageRead {\n return { outcome: classifyStorageError(error), value: null, error: storageErrorMessage(error) };\n}\n\n/** A read that answered, whether or not it found anything. */\nexport function storageReadValue(value: string | null): StorageRead {\n return { outcome: 'ok', value, error: null };\n}\n\n/**\n * The same contract in memory, for specs and for a platform with no\n * store at all.\n *\n * Not a fallback anything installs on its own. An application that\n * would rather run with an unremembered session than fail says so by\n * passing one of these; one that would rather tell the person its\n * settings will not be kept reads the `denied` outcome and says so.\n * Choosing between those two on an application's behalf is exactly the\n * kind of decision the thread model keeps out of the framework.\n */\nexport class MemoryStorage implements StorageAdapter {\n private readonly records = new Map<string, string>();\n /** Written by a spec that wants to see what a full store does. */\n full = false;\n\n read(key: string): Promise<StorageRead> {\n return Promise.resolve(storageReadValue(this.records.get(key) ?? null));\n }\n\n write(key: string, value: string): Promise<StorageOutcome> {\n if (this.full) {\n return Promise.resolve('full');\n }\n this.records.set(key, value);\n return Promise.resolve('ok');\n }\n\n remove(key: string): Promise<StorageOutcome> {\n this.records.delete(key);\n return Promise.resolve('ok');\n }\n\n keys(): Promise<readonly string[]> {\n return Promise.resolve([...this.records.keys()]);\n }\n}\n","import { computed, type ComputedCell } from '../computed';\nimport { internalState, type InternalState } from '../InternalState';\nimport { select } from '../select';\n\n/** One undoable change, as the two functions that make and unmake it. */\nexport interface UndoTransaction {\n /**\n * What a menu item or a toast calls this change: \"Move Sunset\",\n * \"Remove Sunset from the queue\".\n *\n * A phrase rather than a sentence, because the words around it are\n * the caller's: an application writes \"Undo \" in front of it.\n */\n readonly label: string;\n /** Puts the world back the way it was before `redo` ran. */\n readonly undo: () => void;\n /** Makes the change again, from wherever `undo` left things. */\n readonly redo: () => void;\n /**\n * Merges this into the entry before it when the two keys match.\n *\n * A drag reports a crossing per row and a typed name reports a\n * keystroke, and neither is a change a person means to undo one step\n * at a time. Entries pushed one after another under the same key\n * become one entry: **the first one's `undo` and the last one's\n * `redo`**, with everything between them dropped.\n *\n * That merge rule is what makes the two functions above worth\n * writing as absolute moves rather than as deltas. `undo` should say\n * where the thing goes, not how far back it goes, because after\n * three more coalesced steps a delta is wrong and a position is\n * still right. See `endRun` for where a run stops.\n */\n readonly coalesce?: string;\n}\n\nexport interface UndoStackOptions {\n /**\n * How many entries are kept, oldest dropped first. Default 100.\n *\n * A bound rather than a growing list, for the reason\n * `EditableTextModel` has one: an application that is used for an\n * afternoon would otherwise hold every intermediate state of that\n * afternoon, and nobody undoes a hundred steps.\n */\n readonly limit?: number;\n /** What to call the stack in a devtools reading; optional. */\n readonly label?: string;\n}\n\n/** The two labels, as one record so a menu reads them in one go. */\ninterface UndoLabels {\n readonly undo: string | null;\n readonly redo: string | null;\n}\n\nconst NOTHING: UndoLabels = { undo: null, redo: null };\n\n/**\n * The application's undo, as a stack of named transactions.\n *\n * A component registers what it did and how to unmake it, and a menu,\n * a button or a keyboard shortcut drives the stack. Nothing here\n * listens to anything or knows what an application's state is: the two\n * functions in a transaction are the whole of the coupling, which is\n * why this can sit on either thread and why it does not become a data\n * layer.\n *\n * const undo = ctx.inject(UndoStack);\n *\n * queue.send.remove(at);\n * undo.push({\n * label: `Remove ${track.title}`,\n * undo: () => queue.send.addToQueue(track.id),\n * redo: () => queue.send.remove(at)\n * });\n *\n * ## How this relates to the undo inside a text field\n *\n * `EditableTextModel` has had its own undo since text became editable,\n * and the two are deliberately separate. A field's undo is a stack of\n * **snapshots of one string**, private to the field, and it has to be:\n * the model is the only thing that knows where the caret was, which\n * run of typing coalesces with which, and what an IME composition is\n * doing. This one is a stack of **inverse operations** over whatever\n * an application's state happens to be, and it cannot see inside a\n * field at all.\n *\n * They meet at one key press, and the rule there is that the focused\n * field wins: `registerUndoShortcuts` skips Mod+Z while something is\n * being typed into, so undo in a field undoes typing and undo\n * everywhere else undoes the application's last change. Merging the\n * two stacks would mean a keystroke and a queue reorder sharing one\n * history, which is not what either of them means.\n *\n * ## Reentrancy\n *\n * A push while an undo or a redo is running is dropped. Without it an\n * application whose edit path records itself would record the undo as\n * a new edit and the stack would never empty. Write the inverse\n * functions to call the state directly rather than through the same\n * path that records, and the guard never fires.\n */\nexport class UndoStack {\n private readonly entries: UndoTransaction[] = [];\n private readonly undone: UndoTransaction[] = [];\n private readonly limit: number;\n /** True while `undo()` or `redo()` is running one of the functions. */\n private running = false;\n /** Set by `endRun`, so the next push starts a new entry whatever its key. */\n private sealed = false;\n\n private readonly labels: InternalState<UndoLabels>;\n\n /** What undoing would undo, or null when there is nothing to undo. */\n readonly undoLabel: ComputedCell<string | null>;\n /** What redoing would redo, or null when there is nothing to redo. */\n readonly redoLabel: ComputedCell<string | null>;\n /** Whether there is anything to undo, for a menu item's `disabled`. */\n readonly canUndo: ComputedCell<boolean>;\n readonly canRedo: ComputedCell<boolean>;\n\n constructor(options: UndoStackOptions = {}) {\n this.limit = Math.max(1, options.limit ?? 100);\n this.labels = internalState<UndoLabels>(NOTHING, options.label);\n this.undoLabel = select(this.labels, 'undo');\n this.redoLabel = select(this.labels, 'redo');\n this.canUndo = computed(() => this.undoLabel.value !== null);\n this.canRedo = computed(() => this.redoLabel.value !== null);\n }\n\n /** How many entries are held, for a spec or a budget. */\n get size(): number {\n return this.entries.length;\n }\n\n /** How many redos are waiting, for a spec or a budget. */\n get redoSize(): number {\n return this.undone.length;\n }\n\n /**\n * Records a change that has already been made.\n *\n * Pushing is what discards the redo branch: making a change after\n * undoing two is the person choosing the other future, and keeping\n * the abandoned one would mean redoing into a state that no longer\n * follows from what is on screen.\n */\n push(transaction: UndoTransaction): void {\n if (this.running) {\n return;\n }\n this.undone.length = 0;\n const previous = this.entries[this.entries.length - 1];\n const sealed = this.sealed;\n this.sealed = false;\n if (\n !sealed &&\n previous !== undefined &&\n transaction.coalesce !== undefined &&\n previous.coalesce === transaction.coalesce\n ) {\n // The run's own undo, kept from its first step, and its newest\n // redo. Every step in between is released here, which is the\n // whole of what stops a drag across sixty rows retaining sixty\n // closures and the sixty values they close over.\n this.entries[this.entries.length - 1] = {\n label: transaction.label,\n undo: previous.undo,\n redo: transaction.redo,\n coalesce: transaction.coalesce\n };\n this.publish();\n return;\n }\n this.entries.push(transaction);\n while (this.entries.length > this.limit) {\n this.entries.shift();\n }\n this.publish();\n }\n\n /**\n * Groups everything pushed inside `body` into one entry.\n *\n * For a change an application makes as several calls and a person\n * made as one press: undoing runs the group's undos in reverse, and\n * redoing runs its redos in order. Unlike `coalesce`, a group keeps\n * every step, because a group is written down as a group rather than\n * discovered from a run of similar pushes.\n */\n transact<R>(label: string, body: () => R): R {\n if (this.running) {\n return body();\n }\n const outer = this.entries.length;\n const result = body();\n const collected = this.entries.splice(outer);\n if (collected.length > 0) {\n this.entries.push({\n label,\n undo: () => {\n for (let at = collected.length - 1; at >= 0; at--) {\n collected[at]!.undo();\n }\n },\n redo: () => {\n for (const step of collected) {\n step.redo();\n }\n }\n });\n this.sealed = true;\n this.publish();\n }\n return result;\n }\n\n /**\n * Ends the current run, so the next push starts its own entry.\n *\n * The twin of `EditableTextModel.endTypingRun`. A drag calls it when\n * the pointer comes up: without it, dragging a row, letting go, and\n * dragging the same row again would coalesce into one entry, and one\n * undo would put the row back where it was two gestures ago.\n */\n endRun(): void {\n this.sealed = true;\n }\n\n /** Undoes the last change. False when there was nothing to undo. */\n undo(): boolean {\n const entry = this.entries.pop();\n if (entry === undefined) {\n return false;\n }\n this.run(entry.undo);\n this.undone.push(entry);\n this.sealed = true;\n this.publish();\n return true;\n }\n\n /** Redoes the last undone change. False when there was nothing to redo. */\n redo(): boolean {\n const entry = this.undone.pop();\n if (entry === undefined) {\n return false;\n }\n this.run(entry.redo);\n this.entries.push(entry);\n this.sealed = true;\n this.publish();\n return true;\n }\n\n /**\n * Forgets everything, in both directions.\n *\n * What an application calls when the thing the entries refer to is\n * gone: a queue emptied, a document closed, a signed-out account's\n * library replaced. `EditableTextModel.setText` does the same for\n * the same reason, and the reason is that an inverse function whose\n * subject no longer exists is not an undo, it is a surprise.\n */\n clear(): void {\n this.entries.length = 0;\n this.undone.length = 0;\n this.sealed = false;\n this.publish();\n }\n\n private run(action: () => void): void {\n this.running = true;\n try {\n action();\n } finally {\n this.running = false;\n }\n }\n\n private publish(): void {\n const next: UndoLabels = {\n undo: this.entries[this.entries.length - 1]?.label ?? null,\n redo: this.undone[this.undone.length - 1]?.label ?? null\n };\n // Structural comparison rather than a write per push: coalescing a\n // drag rewrites the same label sixty times, and a menu bound to\n // these should redraw when the words change and not before.\n if (next.undo !== this.labels.value.undo || next.redo !== this.labels.value.redo) {\n this.labels.value = next;\n }\n }\n}\n","import type { Mutation } from '../mutate';\nimport type { UndoStack } from './UndoStack';\n\nexport interface UndoableOptions<A> {\n /** What the entry is called, from the argument that made it. */\n readonly label: string | ((argument: A) => string);\n /** The coalescing key, from the argument; see `UndoTransaction.coalesce`. */\n readonly coalesce?: string | ((argument: A) => string);\n}\n\n/**\n * Puts a `mutate` on an undo stack, by way of its own inverse.\n *\n * private readonly liked = internalState<readonly string[]>([]);\n * private readonly like = mutate(this.liked, toggled, id => api.favourite(id));\n * readonly toggleLike = undoable(undo, this.like, id => id, { label: () => 'Like' });\n *\n * The whole of it is that undoing an optimistic change is another\n * optimistic change. Nothing here writes the cell behind the\n * mutation's back, which is the thing that would go wrong if an undo\n * stack held values rather than operations: it would restore a value\n * the server has not been told about, and the next rollback would\n * fight it.\n *\n * `invert` answers the argument that undoes this one. For a toggle\n * that is the same argument again, which is why the example above\n * looks like it does nothing.\n *\n * **A refused write is not on the stack.** `mutate` already puts the\n * cell back when a commit rejects or resolves `false`, so the change\n * did not happen and there is nothing to undo; pushing it would give a\n * person an undo that undoes something they never saw.\n *\n * The undo and the redo call `mutation.run` rather than this wrapper,\n * so running them records nothing and the stack's reentrancy guard\n * never has to fire.\n */\nexport function undoable<A>(\n stack: UndoStack,\n mutation: Mutation<A>,\n invert: (argument: A) => A,\n options: UndoableOptions<A>\n): (argument: A) => Promise<boolean> {\n const { label, coalesce } = options;\n return async (argument: A): Promise<boolean> => {\n if (!(await mutation.run(argument))) {\n return false;\n }\n stack.push({\n label: typeof label === 'function' ? label(argument) : label,\n undo: () => void mutation.run(invert(argument)),\n redo: () => void mutation.run(argument),\n ...(coalesce === undefined ? {} : { coalesce: typeof coalesce === 'function' ? coalesce(argument) : coalesce })\n });\n return true;\n };\n}\n","import {\n classifyStorageError,\n storageReadFailure,\n storageReadValue,\n type StorageAdapter,\n type StorageOutcome,\n type StorageRead\n} from './StorageAdapter';\n\n/**\n * As much of the Origin Private File System as a store needs.\n *\n * Written out rather than taken from `lib.dom`, for two reasons. The\n * workspace's `lib` is `ES2023` and `DOM`, which has the handles but\n * not the async iteration `values()` needs, and a structural type is\n * what lets a spec hand this a directory of its own instead of\n * standing up a file system to test four methods.\n */\nexport interface OpfsDirectory {\n getFileHandle(name: string, options?: { create?: boolean }): Promise<OpfsFileHandle>;\n getDirectoryHandle(name: string, options?: { create?: boolean }): Promise<OpfsDirectory>;\n removeEntry(name: string): Promise<void>;\n keys(): AsyncIterable<string>;\n}\n\nexport interface OpfsFileHandle {\n getFile(): Promise<{ text(): Promise<string> }>;\n createWritable(): Promise<OpfsWritable>;\n}\n\nexport interface OpfsWritable {\n write(data: string): Promise<void>;\n close(): Promise<void>;\n}\n\nexport interface OpfsStorageOptions {\n /**\n * The folder under the origin's private root, so two stores in one\n * application do not share a namespace. Default `'gesso'`.\n */\n readonly directory?: string;\n /** The root, for a spec. Default `navigator.storage.getDirectory()`. */\n readonly root?: () => Promise<OpfsDirectory>;\n}\n\n/**\n * A store in the origin's private file system.\n *\n * The right default for an application's own state. It is reachable\n * from a worker, which `localStorage` is not, so the thread that owns\n * the state is the thread that writes it and nothing has to cross the\n * barrier to be remembered. It is asynchronous throughout, so nothing\n * it does blocks a frame. And it is per-origin and invisible to the\n * person, which is the right place for a queue or a draft and the\n * wrong place for anything they should be able to find and delete.\n *\n * One file per key, named by the key with the characters a file system\n * would refuse escaped, so a key is recoverable from a listing and a\n * key containing a slash cannot reach out of the folder.\n *\n * What happens on each failure:\n *\n * - **The platform has no OPFS**, or the browser refuses it (a private\n * window, a blocked origin): every method answers `denied` and the\n * error message says which. The store is not usable this session and\n * `persisted` stops writing to it after the first denial.\n * - **The quota is spent**: the write answers `full`. Nothing is\n * rolled back, because the value the application holds is the real\n * one and only the remembering failed.\n * - **There is no such record**: the read answers `ok` with `null`.\n * Not having been written yet is the ordinary first run, not a\n * failure.\n * - **Anything else**: `failed`, with the platform's message. A file\n * whose *contents* are not what this version writes is a different\n * thing and is `persisted`'s to judge, which it does by discarding\n * it, the same call `Tokens.ts` makes.\n */\nexport class OpfsStorage implements StorageAdapter {\n private readonly folder: string;\n private readonly rootOf: () => Promise<OpfsDirectory>;\n /** The folder, once opened. Reused, because opening it is a round trip. */\n private opening: Promise<OpfsDirectory> | null = null;\n\n constructor(options: OpfsStorageOptions = {}) {\n this.folder = options.directory ?? 'gesso';\n this.rootOf = options.root ?? defaultRoot;\n }\n\n async read(key: string): Promise<StorageRead> {\n try {\n const directory = await this.open();\n const handle = await directory.getFileHandle(fileFor(key));\n return storageReadValue(await (await handle.getFile()).text());\n } catch (error) {\n // A file that is not there is the ordinary answer to \"is\n // anything remembered\", and the platform says so by throwing\n // `NotFoundError`. It is not a failure and it is not `denied`.\n if (error instanceof Error && error.name === 'NotFoundError') {\n return storageReadValue(null);\n }\n return storageReadFailure(error);\n }\n }\n\n async write(key: string, value: string): Promise<StorageOutcome> {\n try {\n const directory = await this.open();\n const handle = await directory.getFileHandle(fileFor(key), { create: true });\n const writable = await handle.createWritable();\n // Written and closed in one go rather than kept open: a handle\n // held across frames is a handle a reload can leave locked.\n await writable.write(value);\n await writable.close();\n return 'ok';\n } catch (error) {\n return classifyStorageError(error);\n }\n }\n\n async remove(key: string): Promise<StorageOutcome> {\n try {\n await (await this.open()).removeEntry(fileFor(key));\n return 'ok';\n } catch (error) {\n if (error instanceof Error && error.name === 'NotFoundError') {\n return 'ok';\n }\n return classifyStorageError(error);\n }\n }\n\n async keys(): Promise<readonly string[]> {\n try {\n const found: string[] = [];\n for await (const name of (await this.open()).keys()) {\n found.push(keyFor(name));\n }\n return found;\n } catch {\n // A listing that cannot be taken is an empty one. The caller is\n // an application evicting its own records, and evicting nothing\n // is the safe answer to not knowing what is there.\n return [];\n }\n }\n\n /**\n * The folder, opened once and kept.\n *\n * A failed open is *not* kept. A rejected promise left in `opening`\n * would answer every later call with the same rejection, so one\n * refusal at start-up would be a store that never worked again even\n * after the person granted storage or made room. Kept when it\n * succeeds, dropped when it does not, which is one line and the\n * difference between a cache and a poison.\n */\n private open(): Promise<OpfsDirectory> {\n if (this.opening === null) {\n const opening = this.rootOf().then(root => root.getDirectoryHandle(this.folder, { create: true }));\n this.opening = opening;\n opening.catch(() => {\n if (this.opening === opening) {\n this.opening = null;\n }\n });\n }\n return this.opening;\n }\n}\n\nfunction defaultRoot(): Promise<OpfsDirectory> {\n const storage = (globalThis as { navigator?: { storage?: { getDirectory?: () => Promise<OpfsDirectory> } } })\n .navigator?.storage;\n if (storage?.getDirectory === undefined) {\n // A `TypeError`, which `classifyStorageError` reads as `denied`:\n // there is no store here and there will not be one.\n return Promise.reject(new TypeError('This environment has no Origin Private File System.'));\n }\n return storage.getDirectory();\n}\n\n/**\n * A key as a file name.\n *\n * `encodeURIComponent` and not a hash, so a listing of the folder in\n * devtools reads as the keys the application wrote. It escapes the\n * slash and the dot, which is what stops a key reaching a directory it\n * was not given.\n */\nfunction fileFor(key: string): string {\n return `${encodeURIComponent(key)}.json`;\n}\n\nfunction keyFor(name: string): string {\n return decodeURIComponent(name.replace(/\\.json$/, ''));\n}\n","import {\n classifyStorageError,\n storageReadFailure,\n storageReadValue,\n type StorageAdapter,\n type StorageOutcome,\n type StorageRead\n} from './StorageAdapter';\n\nexport interface IndexedDbStorageOptions {\n /** The database. Default `'gesso'`. */\n readonly database?: string;\n /** The object store inside it. Default `'records'`. */\n readonly store?: string;\n /** The factory, for a spec. Default `globalThis.indexedDB`. */\n readonly factory?: IDBFactory;\n}\n\n/**\n * A store in IndexedDB.\n *\n * Beside OPFS rather than instead of it, because the two fail in\n * different places and an application picks by which failure it\n * minds. IndexedDB is reachable from every thread, survives longer\n * under a browser's own eviction, and is what a Safari that has\n * disabled OPFS still has; OPFS is faster for one large record and\n * simpler to inspect. Neither is a default the framework picks: an\n * application names the one it wants.\n *\n * One object store of strings keyed by string, which is the shape\n * `StorageAdapter` describes and no more. Indexes, versions past the\n * first, and cursors over ranges are what an application builds when\n * it has outgrown a key-value store, and at that point it is writing\n * against IndexedDB rather than against this.\n *\n * What happens on each failure:\n *\n * - **No `indexedDB`, or an origin that may not open one** (a private\n * window in some browsers, a blocked third-party context): every\n * method answers `denied`, and the open is retried next time rather\n * than cached, because a `denied` can be lifted by a site setting\n * mid-session.\n * - **The quota is spent**: the write answers `full`. IndexedDB\n * reports this on the transaction rather than on the request, which\n * is why the write waits for `oncomplete` and not for\n * `onsuccess`: a put that succeeded into a transaction that then\n * aborted has not been written, and answering `ok` for it would be\n * the adapter assuming success.\n * - **A version change from another tab**: the connection is closed\n * and dropped, so the next call opens a fresh one. Answering\n * `failed` and holding a dead connection would make every later\n * call fail too.\n * - **Anything else**: `failed`, with the platform's message.\n */\nexport class IndexedDbStorage implements StorageAdapter {\n private readonly database: string;\n private readonly store: string;\n private readonly factory: IDBFactory | undefined;\n private connecting: Promise<IDBDatabase> | null = null;\n\n constructor(options: IndexedDbStorageOptions = {}) {\n this.database = options.database ?? 'gesso';\n this.store = options.store ?? 'records';\n this.factory = options.factory ?? (globalThis as { indexedDB?: IDBFactory }).indexedDB;\n }\n\n async read(key: string): Promise<StorageRead> {\n try {\n const value = await this.transact('readonly', store => store.get(key));\n return storageReadValue(typeof value === 'string' ? value : null);\n } catch (error) {\n return storageReadFailure(error);\n }\n }\n\n async write(key: string, value: string): Promise<StorageOutcome> {\n return this.outcomeOf(() => this.transact('readwrite', store => store.put(value, key)));\n }\n\n async remove(key: string): Promise<StorageOutcome> {\n return this.outcomeOf(() => this.transact('readwrite', store => store.delete(key)));\n }\n\n async keys(): Promise<readonly string[]> {\n try {\n const found = await this.transact('readonly', store => store.getAllKeys());\n return Array.isArray(found) ? found.filter((key): key is string => typeof key === 'string') : [];\n } catch {\n return [];\n }\n }\n\n /** Lets go of the connection, for a spec or an application shutting down. */\n close(): void {\n const connecting = this.connecting;\n this.connecting = null;\n void connecting?.then(\n database => database.close(),\n () => undefined\n );\n }\n\n private async outcomeOf(work: () => Promise<unknown>): Promise<StorageOutcome> {\n try {\n await work();\n return 'ok';\n } catch (error) {\n const outcome = classifyStorageError(error);\n if (outcome === 'denied') {\n this.connecting = null;\n }\n return outcome;\n }\n }\n\n /**\n * Runs one request inside one transaction and answers its result.\n *\n * A write resolves on the transaction completing rather than on the\n * request succeeding, because those are two different claims: the\n * second says the put was accepted, and only the first says it\n * reached the disk.\n */\n private async transact<T>(mode: IDBTransactionMode, run: (store: IDBObjectStore) => IDBRequest<T>): Promise<T> {\n const database = await this.connect();\n return new Promise<T>((resolve, reject) => {\n let answer: T;\n const transaction = database.transaction(this.store, mode);\n const request = run(transaction.objectStore(this.store));\n request.onsuccess = () => {\n answer = request.result;\n };\n request.onerror = () => reject(request.error ?? new Error('The request failed.'));\n transaction.oncomplete = () => resolve(answer);\n transaction.onabort = () => reject(transaction.error ?? new Error('The transaction was aborted.'));\n });\n }\n\n private connect(): Promise<IDBDatabase> {\n this.connecting ??= this.open();\n return this.connecting;\n }\n\n private open(): Promise<IDBDatabase> {\n const factory = this.factory;\n if (factory === undefined) {\n // A `TypeError`, which `classifyStorageError` reads as `denied`.\n return Promise.reject(new TypeError('This environment has no IndexedDB.'));\n }\n return new Promise<IDBDatabase>((resolve, reject) => {\n let request: IDBOpenDBRequest;\n try {\n request = factory.open(this.database, 1);\n } catch (error) {\n reject(error);\n return;\n }\n request.onupgradeneeded = () => {\n if (!request.result.objectStoreNames.contains(this.store)) {\n request.result.createObjectStore(this.store);\n }\n };\n request.onsuccess = () => {\n // Another tab asking for a newer version cannot proceed while\n // this connection is open, so it is given up rather than held.\n request.result.onversionchange = () => this.close();\n resolve(request.result);\n };\n request.onerror = () => reject(request.error ?? new Error('The database could not be opened.'));\n request.onblocked = () => reject(new Error('The database is open in another tab at a different version.'));\n });\n }\n}\n","import { of, skip, type Subscription } from 'rxjs';\n\nimport { debounced } from '../debounce';\nimport type { ReadableCell } from '../Input';\nimport { internalState, type InternalState } from '../InternalState';\nimport { resource, type Resource, type ResourceStatus } from '../resource';\nimport type { StorageAdapter } from './StorageAdapter';\n\nexport interface PersistedOptions<T> {\n /**\n * What the value is before anything has been read, and what it goes\n * back to when nothing was stored.\n *\n * Required, and that is the whole answer to the race between\n * hydration and the first frame: a screen reading this on the frame\n * it mounts gets the default, with `status` saying `loading`, and\n * gets the remembered value a moment later if there was one. There\n * is no fourth state to draw and no null to guard against.\n */\n readonly initial: T;\n /**\n * Reads a record back, refusing one this version does not\n * understand.\n *\n * Answer `null` and the record is treated as not being there, which\n * is what a record written by an older build should be: the cost is\n * one default, and the alternative is state holding `undefined`\n * where a field belongs. `Tokens.isStoredTokens` in Segue is the\n * same check written by hand.\n *\n * Without one the parsed JSON is trusted as-is, which is fine while\n * the shape has only ever had one version and is not fine\n * afterwards.\n */\n readonly revive?: (raw: unknown) => T | null;\n /**\n * How long a change waits for the next one before being written, in\n * milliseconds. Default 250.\n *\n * A `debounced` cell rather than a timer of its own, so this is the\n * same gate a search field uses. It is what makes a value changed\n * sixty times a second cost one write rather than sixty: see\n * `persisted.budget.spec.ts`, which asserts exactly that.\n */\n readonly settle?: number;\n /** What to call this in a devtools reading; optional. */\n readonly label?: string;\n}\n\nconst MESSAGES: Readonly<Record<string, string>> = {\n denied: 'This browser will not let the application store anything.',\n full: 'There is no room left to store this.',\n failed: 'The store could not be written to.'\n};\n\n/**\n * A value that survives the application being closed.\n *\n * Hydration is a `resource`, which is not a detail: reading from a\n * disk is a keyed request that can be slow, can answer \"there is\n * nothing\", and can fail, which is the same set of outcomes a request\n * over the network has. So the statuses here are `ResourceStatus`\n * itself rather than a fourth enum saying the same five things in\n * different words.\n *\n * readonly draft = persisted(new OpfsStorage(), 'draft', { initial: '' });\n *\n * // on a screen\n * <TextInput value={draft.value} onChange={text => draft.set(text)} />\n *\n * ## What a screen sees before hydration finishes\n *\n * The default, and `status` reading `loading`. Nothing waits, nothing\n * is null, and no screen has a shape it only has for the first eighty\n * milliseconds. When the read lands the value changes like any other\n * cell change, and a screen that wants to say \"restoring\" reads\n * `status`.\n *\n * The one race that needs a rule is a person changing the value before\n * the disk has answered, which is not rare: a queue is a press away\n * and OPFS is a round trip away. **What they did wins.** A hydration\n * answer is applied only if nothing has been `set` since, on the same\n * reasoning as `mutate`'s guarded rollback: an answer that was\n * overtaken is stale, and putting it on screen would undo something\n * the person just did.\n *\n * ## What happens when storing fails\n *\n * - `denied`: nothing is written this session and nothing is tried\n * again, because the answer will not change. `status` is `failed`\n * and `saveError` says so once rather than on every keystroke.\n * - `full`: the write failed and the value in memory is kept. Nothing\n * is rolled back: the change is real and only the remembering of it\n * failed. The next change is still attempted, because a quota can be\n * given back.\n * - `failed`: the same as `full`, and for the same reason.\n * - A record that parses but is not the shape `revive` accepts is\n * treated as `missing`: the application starts from its default\n * rather than showing an error about a file the person cannot see.\n *\n * ## It is a helper\n *\n * Nothing in the framework holds one, and nothing is reachable only\n * through it. A channel is served from plain Observables as it always\n * was, and an application that would rather read and write a store\n * itself is writing against the same `StorageAdapter` this is written\n * against. The thread model declined to own an application's data\n * architecture, and remembering a value is not the exception to that.\n */\nexport class PersistedState<T> {\n private readonly held: InternalState<T>;\n private readonly saves: InternalState<number>;\n private readonly failure: InternalState<string | null>;\n private readonly record: Resource<string, T>;\n private readonly writing: Subscription;\n /** True once the application has written a value of its own. */\n private touched = false;\n /** Set when the store said `denied`, which is permanent for the session. */\n private refused = false;\n /** The text last known to be on disk, so hydration does not write itself back. */\n private stored: string | null = null;\n /** What `forget` goes back to. */\n private readonly initial: T;\n\n /** Where the first read got to; the same five words `resource` uses. */\n readonly status: ReadableCell<ResourceStatus>;\n /** Why the read did not answer, as a message; null when it did. */\n readonly error: ReadableCell<string | null>;\n /** What is remembered: the default until the read lands. */\n readonly value: ReadableCell<T>;\n /** Writes in the air, for a saving indicator. A count, as `mutate.pending` is. */\n readonly saving: ReadableCell<number>;\n /** Why the last write did not happen, as a message; null when it did. */\n readonly saveError: ReadableCell<string | null>;\n /** Resolves when the first read has settled, whatever it found. */\n readonly hydrated: Promise<void>;\n\n constructor(\n private readonly adapter: StorageAdapter,\n private readonly key: string,\n options: PersistedOptions<T>\n ) {\n const label = options.label;\n this.initial = options.initial;\n this.held = internalState<T>(options.initial, label);\n this.saves = internalState(0, label === undefined ? undefined : `${label}.saving`);\n this.failure = internalState<string | null>(null, label === undefined ? undefined : `${label}.saveError`);\n this.value = this.held;\n this.saving = this.saves;\n this.saveError = this.failure;\n // A constant key, because the record being read is named once. The\n // resource is eager, so constructing this is what starts the read,\n // which is what \"hydration on start\" means: nothing has to\n // remember to call a `load()`.\n this.record = resource(\n of(key),\n () => this.load(options.revive),\n label === undefined ? {} : { label: `${label}.hydration` }\n );\n this.status = this.record.status;\n this.error = this.record.error;\n this.hydrated = this.record.settled.then(() => this.apply());\n this.writing = debounced(this.held, options.settle ?? 250)\n // The gate hands a new follower the value the cell already has,\n // which here is the default nobody asked to be written. Only\n // what comes after it is a change.\n .pipe(skip(1))\n .subscribe(value => void this.flush(value));\n }\n\n /** What is remembered right now, for code that is not subscribing. */\n get current(): T {\n return this.held.value;\n }\n\n /** Remembers a new value. The write follows once the changes stop. */\n set(value: T): void {\n this.touched = true;\n this.held.value = value;\n }\n\n /**\n * Writes what is held now, without waiting for the gate.\n *\n * For the moment an application knows it is about to lose the thread:\n * a `visibilitychange`, a route away from an editor, a sign-out.\n */\n save(): Promise<void> {\n return this.flush(this.held.value);\n }\n\n /**\n * Forgets the record and goes back to the default.\n *\n * Both halves, because a stored value removed while the cell still\n * holds it would be written straight back by the next change.\n */\n async forget(): Promise<void> {\n this.stored = null;\n this.touched = true;\n this.held.value = this.initial;\n await this.adapter.remove(this.key);\n }\n\n /** Gives back the write subscription and the resource's. */\n dispose(): void {\n this.writing.unsubscribe();\n this.record.dispose();\n }\n\n private async load(revive: ((raw: unknown) => T | null) | undefined): Promise<T | null> {\n const read = await this.adapter.read(this.key);\n if (read.outcome !== 'ok') {\n if (read.outcome === 'denied') {\n this.refused = true;\n }\n throw new Error(read.error ?? MESSAGES[read.outcome] ?? 'The store could not be read.');\n }\n if (read.value === null) {\n return null;\n }\n this.stored = read.value;\n let parsed: unknown;\n try {\n parsed = JSON.parse(read.value);\n } catch {\n // Half-written, or written by something else entirely. Treated\n // as nothing stored rather than as a failure: there is nothing a\n // person can do about it and a default is a working application.\n return null;\n }\n return revive === undefined ? (parsed as T) : revive(parsed);\n }\n\n private apply(): void {\n const found = this.record.value.value;\n if (found === null || this.touched) {\n // Nothing stored, or the person got there first. Theirs is the\n // newer truth and it stays.\n return;\n }\n this.held.value = found;\n }\n\n private async flush(value: T): Promise<void> {\n if (this.refused) {\n return;\n }\n let text: string;\n try {\n text = JSON.stringify(value);\n } catch (error) {\n this.failure.value = error instanceof Error ? error.message : String(error);\n return;\n }\n if (text === this.stored) {\n // Hydration writing what it just read, or a change that came\n // back round to where it started. Either way there is nothing\n // to say to the disk.\n return;\n }\n this.saves.value = this.saves.value + 1;\n try {\n const outcome = await this.adapter.write(this.key, text);\n if (outcome === 'ok') {\n this.stored = text;\n this.failure.value = null;\n return;\n }\n if (outcome === 'denied') {\n this.refused = true;\n }\n this.failure.value = MESSAGES[outcome] ?? 'The store could not be written to.';\n } finally {\n this.saves.value = Math.max(0, this.saves.value - 1);\n }\n }\n}\n\n/**\n * A value read from a store on start and written back as it changes.\n *\n * const settings = persisted(new IndexedDbStorage(), 'settings', {\n * initial: DEFAULTS,\n * revive: raw => (isSettings(raw) ? raw : null)\n * });\n *\n * See `PersistedState` for what a screen sees before the read lands\n * and what each kind of storage failure does.\n */\nexport function persisted<T>(adapter: StorageAdapter, key: string, options: PersistedOptions<T>): PersistedState<T> {\n return new PersistedState(adapter, key, options);\n}\n"],"mappings":"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"}
|
package/dist/remote/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { g as ServedChannel } from "../ui-
|
|
2
|
-
import { Ct as AppLogicEndpoint } from "../index-
|
|
1
|
+
import { g as ServedChannel } from "../ui-e4RHm8Mt.js";
|
|
2
|
+
import { Ct as AppLogicEndpoint } from "../index-oHQAJMqR.js";
|
|
3
3
|
//#region src/remote/frames.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* The whole wire format, which is deliberately smaller than the
|
package/dist/remote/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as isHubMessage, n as serveChannels, o as isPortHandshake } from "../serveChannels-
|
|
1
|
+
import { i as isHubMessage, n as serveChannels, o as isPortHandshake } from "../serveChannels-CFzq0Hz3.js";
|
|
2
2
|
//#region src/remote/frames.ts
|
|
3
3
|
/**
|
|
4
4
|
* The whole wire format, which is deliberately smaller than the
|