gesso-framework 0.7.0 → 0.8.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"computed-DyW9SPpq.js","names":[],"sources":["../src/Input.ts","../src/InternalState.ts","../src/derive.ts","../src/computed.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 * The value it already holds is not a change, and tells nobody: values\n * that cross into a component are treated as immutable, so the same\n * reference is the same value.\n */\n override next(value: T): void {\n // `getValue` throws for a cell that has errored; `next` there is a no-op either way.\n if (!this.hasError && Object.is(value, super.getValue())) {\n return;\n }\n if (this.snapshotBy !== null && !this.warnedStale && !this.observed) {\n this.warnedStale = true;\n warnStaleRead(this.snapshotBy, this.label, super.getValue(), value);\n }\n super.next(value);\n }\n}\n\n/**\n * Hands a cell a value even when it holds it already, which `next`\n * drops: an input a design tool's preview starts or ends is told again,\n * so a control that owns what it was started at starts again from it.\n * For the host, not for components.\n */\nexport function forceInput<T>(cell: InputCell<T>, value: T): void {\n if (Object.is(value, cell.getValue())) {\n BehaviorSubject.prototype.next.call(cell, value);\n } else {\n cell.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 /**\n * The set the next run collects its reads into. It trades places with\n * `sources` after the run, so following a cell allocates nothing per\n * change; the two sets are simply reused.\n */\n private collecting = new Set<ReadableCell<unknown>>();\n /** Whether the last run read different cells from the run before it. */\n private sourcesChanged = false;\n private recomputing = false;\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 // A run started from inside a run (a read that synchronously changes\n // a source) gets a set of its own, so it cannot clear the one the\n // outer run is still filling.\n const nested = this.recomputing;\n const touched = nested ? new Set<ReadableCell<unknown>>() : this.collecting;\n touched.clear();\n this.recomputing = true;\n let next: T;\n try {\n next = withTracking(touched, () => this.compute(readSource));\n } finally {\n this.recomputing = nested;\n }\n this.sourcesChanged = !sameSet(touched, this.sources);\n if (!nested) {\n this.collecting = this.sources;\n }\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 changed = this.recompute();\n if (this.sourcesChanged) {\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"],"mappings":"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"}