gesso-framework 0.6.30 → 0.7.0

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.
Files changed (48) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/dist/{FunctionComponent-CHtojpp7.d.ts → FunctionComponent-Czkex8fp.d.ts} +358 -114
  3. package/dist/{serveChannels-BlEag7WU.js → WorkerPorts-m_hLRoPj.js} +8 -240
  4. package/dist/WorkerPorts-m_hLRoPj.js.map +1 -0
  5. package/dist/agent/index.d.ts +53 -2
  6. package/dist/agent/index.js +5 -4
  7. package/dist/agent/index.js.map +1 -1
  8. package/dist/app-De3z58x7.js +638 -0
  9. package/dist/app-De3z58x7.js.map +1 -0
  10. package/dist/computed-_cYcdczA.js +577 -0
  11. package/dist/computed-_cYcdczA.js.map +1 -0
  12. package/dist/createComponent-DJads88q.js +47 -0
  13. package/dist/createComponent-DJads88q.js.map +1 -0
  14. package/dist/{index-DZbSb8IN.d.ts → index-BENndtaH.d.ts} +222 -96
  15. package/dist/index.d.ts +4 -5
  16. package/dist/index.js +440 -6645
  17. package/dist/index.js.map +1 -1
  18. package/dist/jsx/jsx-runtime.d.ts +8 -5
  19. package/dist/jsx/jsx-runtime.js +15 -6
  20. package/dist/jsx/jsx-runtime.js.map +1 -1
  21. package/dist/{persisted-fUzIU8FZ.js → persisted-8meJmsOh.js} +4 -576
  22. package/dist/persisted-8meJmsOh.js.map +1 -0
  23. package/dist/remote/index.d.ts +2 -2
  24. package/dist/remote/index.js +2 -1
  25. package/dist/remote/index.js.map +1 -1
  26. package/dist/{remote-BoB8zh7z.js → remote-B4bjdTzg.js} +19 -4
  27. package/dist/remote-B4bjdTzg.js.map +1 -0
  28. package/dist/render-cydQ10Nd.js +6795 -0
  29. package/dist/render-cydQ10Nd.js.map +1 -0
  30. package/dist/serveChannels-DReCmRxO.js +241 -0
  31. package/dist/serveChannels-DReCmRxO.js.map +1 -0
  32. package/dist/{createComponent-3n1ZSql8.js → show-D0MevHaV.js} +2 -46
  33. package/dist/show-D0MevHaV.js.map +1 -0
  34. package/dist/{ui-CHtNccR8.d.ts → tools-BFBwuinE.d.ts} +351 -8
  35. package/dist/{webmcp-CdDq2nzS.js → webmcp-D7IlA0fu.js} +3 -3
  36. package/dist/webmcp-D7IlA0fu.js.map +1 -0
  37. package/dist/worker/index.d.ts +3 -4
  38. package/dist/worker/index.js +5 -3
  39. package/dist/worker/index.js.map +1 -1
  40. package/package.json +2 -2
  41. package/dist/ChannelProtocol-ByNoHujM.d.ts +0 -247
  42. package/dist/app-Yig59Ygf.js +0 -326
  43. package/dist/app-Yig59Ygf.js.map +0 -1
  44. package/dist/createComponent-3n1ZSql8.js.map +0 -1
  45. package/dist/persisted-fUzIU8FZ.js.map +0 -1
  46. package/dist/remote-BoB8zh7z.js.map +0 -1
  47. package/dist/serveChannels-BlEag7WU.js.map +0 -1
  48. package/dist/webmcp-CdDq2nzS.js.map +0 -1
@@ -0,0 +1,577 @@
1
+ import { T as structurallyEqual } from "./WorkerPorts-m_hLRoPj.js";
2
+ import { BehaviorSubject, Observable, Subject, Subscription, combineLatest, distinctUntilChanged, map } from "rxjs";
3
+ //#region src/Input.ts
4
+ /**
5
+ * Reactive cell holding a component input.
6
+ *
7
+ * Inputs must be cells because render() runs exactly once. A component
8
+ * that read a plain input value during render would capture it for the
9
+ * life of the instance, so a parent supplying new props under a
10
+ * dynamic subtree would update the field while the rendered tree kept
11
+ * showing the original value.
12
+ *
13
+ * The cell is written by the component host only: it accepts whatever
14
+ * the parent passed, subscribing it first when the parent passed an
15
+ * Observable. `value` is deliberately read-only, so `this.label.value =
16
+ * x` inside a component is a compile error rather than a silent
17
+ * violation of the one-way data flow.
18
+ */
19
+ var InputCell = class extends BehaviorSubject {
20
+ /**
21
+ * What to call this cell in a warning: `Component.prop`, or
22
+ * `channel.key`. Set by whoever creates it; a cell without one is
23
+ * reported as "a cell".
24
+ */
25
+ label;
26
+ /** The component whose body read `.value`, for the stale-read warning below. */
27
+ snapshotBy = null;
28
+ warnedStale = false;
29
+ /** Everything emitted through `emit`, created on the first `events` read. */
30
+ emitted = null;
31
+ constructor(initialValue) {
32
+ super(initialValue);
33
+ }
34
+ /**
35
+ * Fires the output this cell stands for.
36
+ *
37
+ * An output is a cell whose value is what the parent gave to be
38
+ * called: a function, or a target made by `into(subject)`. `emit`
39
+ * calls it with the arguments, and also pushes the first argument
40
+ * through `events`, so the component can fire from three places
41
+ * without passing the cell around and a parent that wants a stream
42
+ * can have one. A parent that passed nothing is fine: the call
43
+ * simply reaches nobody.
44
+ */
45
+ emit(...args) {
46
+ const handler = super.getValue();
47
+ if (typeof handler === "function") handler(...args);
48
+ this.emitted?.next(args[0]);
49
+ }
50
+ /** What `emit` has fired, as a stream: the first argument of each call. */
51
+ get events() {
52
+ if (this.emitted === null) this.emitted = new Subject();
53
+ return this.emitted.asObservable();
54
+ }
55
+ get value() {
56
+ trackRead(this);
57
+ if (bodyOf !== null && tracking === null && this.snapshotBy === null) this.snapshotBy = bodyOf;
58
+ return super.getValue();
59
+ }
60
+ /**
61
+ * The one place the run-once model goes quietly wrong is a body that
62
+ * reads `props.x.value`, uses the value to build the tree, and never
63
+ * hears that it changed. Nothing crashes; the screen is simply stale.
64
+ * So a cell remembers being read while a body ran, and if it later
65
+ * changes with nobody subscribed to it, it says so once. A cell that
66
+ * something is following is fine: the follower carries the change.
67
+ *
68
+ * The value it already holds is not a change, and tells nobody: values
69
+ * that cross into a component are treated as immutable, so the same
70
+ * reference is the same value.
71
+ */
72
+ next(value) {
73
+ if (!this.hasError && Object.is(value, super.getValue())) return;
74
+ if (this.snapshotBy !== null && !this.warnedStale && !this.observed) {
75
+ this.warnedStale = true;
76
+ warnStaleRead(this.snapshotBy, this.label, super.getValue(), value);
77
+ }
78
+ super.next(value);
79
+ }
80
+ };
81
+ /** The component whose function body is running, while one is. */
82
+ let bodyOf = null;
83
+ /** The component whose body is running, for a cell that wants to remember being read there. */
84
+ function currentBody() {
85
+ return bodyOf;
86
+ }
87
+ /** The set a running `computed` is collecting its reads into, while one is. */
88
+ let tracking = null;
89
+ /** Records a `.value` read for whatever `computed` is running, if one is. */
90
+ function trackRead(cell) {
91
+ tracking?.add(cell);
92
+ }
93
+ /** Runs `run` with every `.value` read on the way recorded into `into`. */
94
+ function withTracking(into, run) {
95
+ const previous = tracking;
96
+ tracking = into;
97
+ try {
98
+ return run();
99
+ } finally {
100
+ tracking = previous;
101
+ }
102
+ }
103
+ /**
104
+ * Runs a component's body with its name on record, so a `.value` read
105
+ * inside it can be told apart from one in an event handler later, which
106
+ * is the ordinary way to read the current value and warns about nothing.
107
+ */
108
+ function withBodyOf(tag, run) {
109
+ const previous = bodyOf;
110
+ bodyOf = tag;
111
+ try {
112
+ return run();
113
+ } finally {
114
+ bodyOf = previous;
115
+ }
116
+ }
117
+ function warnStaleRead(tag, label, from, to) {
118
+ const what = label === void 0 ? "a cell" : `\`${label}\``;
119
+ const change = describeChange(from, to);
120
+ console.warn(`Component '${tag}' read ${what} with .value while its body ran, and nothing is following that cell. It has since changed ${change}, and whatever was built from the first value still shows it. A component body runs once: bind the cell instead (pass it, or pipe it, into the prop it feeds), or give the component a key so a new value builds a new one.`);
121
+ }
122
+ /**
123
+ * "from X to Y", or for two plain objects the first field that differs,
124
+ * because two objects that print alike for sixty characters say nothing
125
+ * about what actually moved.
126
+ */
127
+ function describeChange(from, to) {
128
+ if (isPlainObject(from) && isPlainObject(to)) {
129
+ for (const key of /* @__PURE__ */ new Set([...Object.keys(from), ...Object.keys(to)])) {
130
+ const before = from[key];
131
+ const after = to[key];
132
+ if (!Object.is(before, after) && safeJson(before) !== safeJson(after)) return `at .${key}, from ${describe(before)} to ${describe(after)}`;
133
+ }
134
+ return "to an equal-looking object";
135
+ }
136
+ return `from ${describe(from)} to ${describe(to)}`;
137
+ }
138
+ function isPlainObject(value) {
139
+ return typeof value === "object" && value !== null && !Array.isArray(value);
140
+ }
141
+ function safeJson(value) {
142
+ try {
143
+ return JSON.stringify(value) ?? String(value);
144
+ } catch {
145
+ return String(value);
146
+ }
147
+ }
148
+ function describe(value) {
149
+ let text;
150
+ try {
151
+ text = typeof value === "function" ? "a function" : JSON.stringify(value) ?? String(value);
152
+ } catch {
153
+ text = String(value);
154
+ }
155
+ return text.length > 60 ? `${text.slice(0, 57)}...` : text;
156
+ }
157
+ function input(first, fallback) {
158
+ if (arguments.length < 2 || !(first instanceof InputCell)) return new InputCell(first);
159
+ const source = first;
160
+ const withFallback = (value) => value === void 0 ? fallback : value;
161
+ const derived = new InputCell(withFallback(source.value));
162
+ derived.label = source.label;
163
+ source.subscribe({
164
+ next: (value) => derived.next(withFallback(value)),
165
+ complete: () => derived.complete()
166
+ });
167
+ return derived;
168
+ }
169
+ /**
170
+ * An output a class component declares as a field:
171
+ *
172
+ * @Output() changed = output<[value: number]>();
173
+ *
174
+ * The host wires the parent's handler into it exactly as it wires an
175
+ * input, and `this.changed.emit(next)` fires it.
176
+ */
177
+ function output() {
178
+ return new InputCell(void 0);
179
+ }
180
+ const OUTPUT_TARGET = Symbol("gesso:output-target");
181
+ function into(target) {
182
+ return { [OUTPUT_TARGET]: target };
183
+ }
184
+ function isOutputTarget(value) {
185
+ return typeof value === "object" && value !== null && OUTPUT_TARGET in value;
186
+ }
187
+ /** The receiver an `into()` target wraps. */
188
+ function outputTargetOf(target) {
189
+ return target[OUTPUT_TARGET];
190
+ }
191
+ //#endregion
192
+ //#region src/InternalState.ts
193
+ /**
194
+ * A component's own state: originated here, and never crossing the
195
+ * barrier.
196
+ *
197
+ * The writable counterpart to `InputCell`. The two are the same
198
+ * `BehaviorSubject` and differ by one accessor — this one has a
199
+ * `.value` setter — and that difference is the whole semantics:
200
+ *
201
+ * internalState() never crosses I write it
202
+ * input() crosses inward someone else writes it
203
+ *
204
+ * Named on that axis deliberately. It used to be `state()`, which
205
+ * described *what* a thing was while `input()` described *where it
206
+ * came from*; two names on two axes made neither of them tell you
207
+ * anything about the other. `internalState(products)` reads as a
208
+ * mistake at the call site in a way `state(products)` never did.
209
+ *
210
+ * For values that originate on this thread and die with the component:
211
+ * a tooltip's open flag, a caret, a scroll offset, the active tab.
212
+ * Anything that survives a reload, or that another screen cares about,
213
+ * is application state and belongs on a channel. Anything derived from
214
+ * other cells is a `computed`.
215
+ *
216
+ * It is not only for components. The thread that owns a channel's data
217
+ * writes cells too, and wrote them as a `BehaviorSubject` mirrored
218
+ * into an `asObservable()` because this was reachable only through the
219
+ * framework's main entry. `gesso-framework/worker` is the same cell
220
+ * with none of the renderer behind it, so an application worker holds
221
+ * one cell rather than a subject and a copy of it, and reads it with
222
+ * `.value` in a `computed` rather than listing it in a
223
+ * `combineLatest`. "Internal" still means what it says there: written
224
+ * here, and crossing the barrier only as the plain data a view key
225
+ * publishes.
226
+ */
227
+ var InternalState = class extends BehaviorSubject {
228
+ /** What to call this cell in a warning or the inspector; optional. */
229
+ label;
230
+ constructor(initialValue) {
231
+ super(initialValue);
232
+ }
233
+ get value() {
234
+ trackRead(this);
235
+ return super.getValue();
236
+ }
237
+ set value(next) {
238
+ this.next(next);
239
+ }
240
+ };
241
+ /**
242
+ * Creates a reactive state cell.
243
+ *
244
+ * Usage inside a component:
245
+ *
246
+ * private readonly count = state(0);
247
+ *
248
+ * increment() {
249
+ * this.count.value++;
250
+ * }
251
+ */
252
+ function internalState(initialValue, label) {
253
+ const state = new InternalState(initialValue);
254
+ if (label !== void 0) state.label = label;
255
+ return state;
256
+ }
257
+ //#endregion
258
+ //#region src/derive.ts
259
+ /**
260
+ * One value from several, kept equal to `project` of the latest of each
261
+ * source, and emitted only when it changes.
262
+ *
263
+ * This is `combineLatest(...).pipe(map(...), distinctUntilChanged())`,
264
+ * which is what nearly every derived binding in a component body wants
265
+ * and what nearly every one had to write out. Sources are cells or any
266
+ * Observables; the result is what a prop takes.
267
+ *
268
+ * const playing = derive([queue.view.playlistId, audio.state], (id, state) =>
269
+ * id === card.id && state.status === 'playing'
270
+ * );
271
+ *
272
+ * @deprecated Write it as a `computed`, which is the one derivation the
273
+ * documentation teaches. A cell is read with
274
+ * `.value` and anything else through the `read` the function is handed,
275
+ * so the sources are the reads themselves and there is no list beside
276
+ * the expression to keep in step with it:
277
+ *
278
+ * const playing = computed(read =>
279
+ * queue.view.playlistId.value === card.id && read(audio.state).status === 'playing'
280
+ * );
281
+ *
282
+ * It still works and nothing that uses it needs changing today.
283
+ */
284
+ function derive(sources, project, options = {}) {
285
+ const equal = equalityOf(options.equal ?? "reference");
286
+ return combineLatest(sources).pipe(map((values) => project(...values)), distinctUntilChanged(equal));
287
+ }
288
+ /** The comparison an `Equality` names. Shared with `computed`. */
289
+ function equalityOf(equal) {
290
+ if (equal === "reference") return Object.is;
291
+ if (equal === "structural") return (a, b) => structurallyEqual(a, b);
292
+ return equal;
293
+ }
294
+ //#endregion
295
+ //#region src/computed.ts
296
+ /**
297
+ * A cell whose value is a function of other cells.
298
+ *
299
+ * `computed(() => quantity.value * price.value)` reads like the value it
300
+ * is. The cells its function reads through `.value` are its sources,
301
+ * found by running the function and watching what it touches, so there
302
+ * is no list to keep in step with the expression; a read the function
303
+ * did not make this time is a source it no longer has.
304
+ *
305
+ * A stream that is not a cell is read through the `read` the function
306
+ * is handed: `computed(read => read(stream).status)` follows the stream
307
+ * as it follows a cell. That is what makes this the only derivation an
308
+ * application needs, whether or not the thing it derives from happens
309
+ * to have a current value of its own.
310
+ *
311
+ * It is a cell and nothing else. Bound to a prop it is an Observable
312
+ * like every other cell, so a component written with it and one written
313
+ * with `pipe` compose without translation. Read in a handler with
314
+ * `.value` it is the current result, computed on the spot if nothing is
315
+ * following it. RxJS is underneath and nothing here replaces it.
316
+ *
317
+ * Nothing runs until someone asks. With no subscriber the function runs
318
+ * only when `.value` is read; with one, the cell follows its sources and
319
+ * emits a result when it differs from the last by `equal`. When the last
320
+ * subscriber leaves it lets go of its sources, so a computed made in a
321
+ * component body dies with the component's bindings and needs no
322
+ * disposal of its own.
323
+ *
324
+ * Reading it with `.value` while a component body runs is the same
325
+ * snapshot an input read there is, and it warns the same way: once, if
326
+ * it later changes with nobody following.
327
+ */
328
+ var ComputedCell = class extends Observable {
329
+ compute;
330
+ label;
331
+ equal;
332
+ changes = new Subject();
333
+ sources = /* @__PURE__ */ new Set();
334
+ /**
335
+ * The set the next run collects its reads into. It trades places with
336
+ * `sources` after the run, so following a cell allocates nothing per
337
+ * change; the two sets are simply reused.
338
+ */
339
+ collecting = /* @__PURE__ */ new Set();
340
+ /** Whether the last run read different cells from the run before it. */
341
+ sourcesChanged = false;
342
+ recomputing = false;
343
+ cached;
344
+ hasValue = false;
345
+ upstream = null;
346
+ subscribers = 0;
347
+ attaching = false;
348
+ snapshotBy = null;
349
+ warnedStale = false;
350
+ /** Follows the sources after a body read, only to notice the change the body will not. */
351
+ staleWatch = null;
352
+ constructor(compute, options = {}) {
353
+ super((subscriber) => {
354
+ this.subscribers++;
355
+ this.staleWatch?.unsubscribe();
356
+ this.staleWatch = null;
357
+ if (this.upstream === null) this.attach();
358
+ subscriber.next(this.cached);
359
+ const following = this.changes.subscribe(subscriber);
360
+ return () => {
361
+ following.unsubscribe();
362
+ this.subscribers--;
363
+ if (this.subscribers === 0) this.detach();
364
+ };
365
+ });
366
+ this.compute = compute;
367
+ this.equal = equalityOf(options.equal ?? "reference");
368
+ this.label = options.label;
369
+ }
370
+ /** The current result: kept by the sources while followed, computed now when not. */
371
+ get value() {
372
+ trackRead(this);
373
+ if (this.upstream === null) this.recompute();
374
+ const body = currentBody();
375
+ if (body !== null && this.snapshotBy === null) {
376
+ this.snapshotBy = body;
377
+ this.watchForStaleRead();
378
+ }
379
+ return this.cached;
380
+ }
381
+ /**
382
+ * A body read the value once; if a source now changes with nothing
383
+ * following this cell, the screen built from that read is stale and
384
+ * nobody would know. So the sources are watched for exactly that, and
385
+ * the watch ends with the warning or with a real subscriber arriving.
386
+ */
387
+ watchForStaleRead() {
388
+ this.staleWatch = new Subscription();
389
+ let settling = true;
390
+ for (const source of this.sources) this.staleWatch.add(source.subscribe(() => {
391
+ if (settling || this.observed) return;
392
+ if (this.recompute()) {
393
+ this.warnStale();
394
+ this.staleWatch?.unsubscribe();
395
+ this.staleWatch = null;
396
+ }
397
+ }));
398
+ settling = false;
399
+ }
400
+ warnStale() {
401
+ if (this.snapshotBy === null || this.warnedStale) return;
402
+ this.warnedStale = true;
403
+ console.warn(`Component '${this.snapshotBy}' read ${this.label === void 0 ? "a computed cell" : `\`${this.label}\``} with .value while its body ran, and nothing is following that cell. It has since changed, and whatever was built from the first value still shows it. A component body runs once: bind the cell instead.`);
404
+ }
405
+ /** Whether anything is following this cell; the stale-read warning's question. */
406
+ get observed() {
407
+ return this.subscribers > 0;
408
+ }
409
+ /** Runs the function, watching what it reads; true when the result changed. */
410
+ recompute() {
411
+ const nested = this.recomputing;
412
+ const touched = nested ? /* @__PURE__ */ new Set() : this.collecting;
413
+ touched.clear();
414
+ this.recomputing = true;
415
+ let next;
416
+ try {
417
+ next = withTracking(touched, () => this.compute(readSource));
418
+ } finally {
419
+ this.recomputing = nested;
420
+ }
421
+ this.sourcesChanged = !sameSet(touched, this.sources);
422
+ if (!nested) this.collecting = this.sources;
423
+ this.sources = touched;
424
+ const changed = !this.hasValue || !this.equal(this.cached, next);
425
+ this.hasValue = true;
426
+ this.cached = next;
427
+ return changed;
428
+ }
429
+ /** Follows the current sources, re-running on any change to them. */
430
+ attach() {
431
+ this.attaching = true;
432
+ this.recompute();
433
+ this.upstream = new Subscription();
434
+ for (const source of this.sources) this.upstream.add(source.subscribe(() => this.onSourceChanged()));
435
+ this.attaching = false;
436
+ }
437
+ detach() {
438
+ this.upstream?.unsubscribe();
439
+ this.upstream = null;
440
+ }
441
+ onSourceChanged() {
442
+ if (this.attaching) return;
443
+ const changed = this.recompute();
444
+ if (this.sourcesChanged) {
445
+ this.detach();
446
+ this.attaching = true;
447
+ this.upstream = new Subscription();
448
+ for (const source of this.sources) this.upstream.add(source.subscribe(() => this.onSourceChanged()));
449
+ this.attaching = false;
450
+ }
451
+ if (changed) this.changes.next(this.cached);
452
+ }
453
+ };
454
+ /**
455
+ * A cell computed from what its function reads. See `ComputedCell`.
456
+ *
457
+ * const total = computed(() => quantity.value * PRICE * RATES[currency.value]);
458
+ * <text text={computed(() => String(count.value))} />
459
+ *
460
+ * Cells are read with `.value`; anything else is read through the
461
+ * `read` the function is given, which follows it the same way:
462
+ *
463
+ * const late = computed(read => read(clock) > deadline);
464
+ */
465
+ function computed(compute, options = {}) {
466
+ return new ComputedCell(compute, options);
467
+ }
468
+ /**
469
+ * The cell standing for a stream, one per stream.
470
+ *
471
+ * Anything that already has a current value is its own cell, so a
472
+ * `read` of an input, an internal state or another computed costs a
473
+ * property access and nothing more. Everything else gets a `StreamCell`
474
+ * held against it here, so several computeds reading one stream share a
475
+ * single subscription to it rather than opening one each.
476
+ *
477
+ * Weak on purpose: the entry is reachable only while the stream is, so
478
+ * a stream made in a component body is collected with the component.
479
+ */
480
+ const streamCells = /* @__PURE__ */ new WeakMap();
481
+ /**
482
+ * Whether a source's `.value` announces itself to the running computed,
483
+ * decided once per source and remembered.
484
+ *
485
+ * Having a `value` is not enough. A framework cell records its reads
486
+ * through `trackRead`, which is what lets a computed learn what it
487
+ * depends on; a plain `BehaviorSubject` has a `value` too and records
488
+ * nothing, so a computed that trusted the property would read it once
489
+ * and never hear it change. That is exactly what happened to three
490
+ * data-layer specs that fed a raw subject where the application feeds
491
+ * a channel view. The probe reads `.value` once under a tracking set
492
+ * of its own and asks whether the source turned up in it.
493
+ */
494
+ const tracksReads = /* @__PURE__ */ new WeakMap();
495
+ function announcesItsReads(source) {
496
+ let known = tracksReads.get(source);
497
+ if (known === void 0) {
498
+ const seen = /* @__PURE__ */ new Set();
499
+ withTracking(seen, () => void source.value);
500
+ known = seen.has(source);
501
+ tracksReads.set(source, known);
502
+ }
503
+ return known;
504
+ }
505
+ function cellFor(source) {
506
+ if ("value" in source && announcesItsReads(source)) return source;
507
+ let cell = streamCells.get(source);
508
+ if (cell === void 0) {
509
+ cell = new StreamCell(source);
510
+ streamCells.set(source, cell);
511
+ }
512
+ return cell;
513
+ }
514
+ /** The `read` every computed's function is handed. */
515
+ const readSource = (source) => cellFor(source).value;
516
+ /**
517
+ * A plain stream, seen as a cell.
518
+ *
519
+ * It holds the last value it saw and hands it to whoever asks, which is
520
+ * the one thing a cell has and an Observable does not. While something
521
+ * follows it, it follows the stream; when the last follower leaves it
522
+ * lets go, so it costs nothing between uses and needs no disposal, on
523
+ * the same terms as `ComputedCell`.
524
+ *
525
+ * A `.value` read with nothing following takes one synchronous
526
+ * subscription and drops it again, which is how a `BehaviorSubject`
527
+ * behind an `asObservable()`, or a `combineLatest` over such subjects,
528
+ * answers with what it already holds. A stream that has nothing to say
529
+ * synchronously answers `undefined` until its first emission arrives,
530
+ * which is the honest answer: there is no value yet.
531
+ */
532
+ var StreamCell = class extends Observable {
533
+ stream;
534
+ last;
535
+ followers = 0;
536
+ upstream = null;
537
+ changes = new Subject();
538
+ constructor(stream) {
539
+ super((subscriber) => {
540
+ this.followers++;
541
+ if (this.upstream === null) this.attach();
542
+ subscriber.next(this.last);
543
+ const following = this.changes.subscribe(subscriber);
544
+ return () => {
545
+ following.unsubscribe();
546
+ this.followers--;
547
+ if (this.followers === 0) {
548
+ this.upstream?.unsubscribe();
549
+ this.upstream = null;
550
+ }
551
+ };
552
+ });
553
+ this.stream = stream;
554
+ }
555
+ get value() {
556
+ trackRead(this);
557
+ if (this.upstream === null) this.stream.subscribe((value) => {
558
+ this.last = value;
559
+ }).unsubscribe();
560
+ return this.last;
561
+ }
562
+ attach() {
563
+ this.upstream = this.stream.subscribe((value) => {
564
+ this.last = value;
565
+ this.changes.next(value);
566
+ });
567
+ }
568
+ };
569
+ function sameSet(a, b) {
570
+ if (a.size !== b.size) return false;
571
+ for (const item of a) if (!b.has(item)) return false;
572
+ return true;
573
+ }
574
+ //#endregion
575
+ export { InternalState as a, input as c, output as d, outputTargetOf as f, equalityOf as i, into as l, withBodyOf as m, computed as n, internalState as o, trackRead as p, derive as r, InputCell as s, ComputedCell as t, isOutputTarget as u };
576
+
577
+ //# sourceMappingURL=computed-_cYcdczA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"computed-_cYcdczA.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/** 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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"}