gesso-framework 0.6.29 → 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.
- package/CHANGELOG.md +68 -0
- package/dist/{FunctionComponent-CHtojpp7.d.ts → FunctionComponent-Czkex8fp.d.ts} +358 -114
- package/dist/{serveChannels-BlEag7WU.js → WorkerPorts-m_hLRoPj.js} +8 -240
- package/dist/WorkerPorts-m_hLRoPj.js.map +1 -0
- package/dist/agent/index.d.ts +53 -2
- package/dist/agent/index.js +5 -4
- package/dist/agent/index.js.map +1 -1
- package/dist/app-De3z58x7.js +638 -0
- package/dist/app-De3z58x7.js.map +1 -0
- package/dist/computed-_cYcdczA.js +577 -0
- package/dist/computed-_cYcdczA.js.map +1 -0
- package/dist/createComponent-DJads88q.js +47 -0
- package/dist/createComponent-DJads88q.js.map +1 -0
- package/dist/{index-DZbSb8IN.d.ts → index-BENndtaH.d.ts} +222 -96
- package/dist/index.d.ts +4 -5
- package/dist/index.js +440 -6645
- package/dist/index.js.map +1 -1
- package/dist/jsx/jsx-runtime.d.ts +8 -5
- package/dist/jsx/jsx-runtime.js +15 -6
- package/dist/jsx/jsx-runtime.js.map +1 -1
- package/dist/{persisted-fUzIU8FZ.js → persisted-8meJmsOh.js} +4 -576
- package/dist/persisted-8meJmsOh.js.map +1 -0
- package/dist/remote/index.d.ts +2 -2
- package/dist/remote/index.js +2 -1
- package/dist/remote/index.js.map +1 -1
- package/dist/{remote-BoB8zh7z.js → remote-B4bjdTzg.js} +19 -4
- package/dist/remote-B4bjdTzg.js.map +1 -0
- package/dist/render-cydQ10Nd.js +6795 -0
- package/dist/render-cydQ10Nd.js.map +1 -0
- package/dist/serveChannels-DReCmRxO.js +241 -0
- package/dist/serveChannels-DReCmRxO.js.map +1 -0
- package/dist/{createComponent-3n1ZSql8.js → show-D0MevHaV.js} +2 -46
- package/dist/show-D0MevHaV.js.map +1 -0
- package/dist/{ui-CHtNccR8.d.ts → tools-BFBwuinE.d.ts} +351 -8
- package/dist/{webmcp-CdDq2nzS.js → webmcp-D7IlA0fu.js} +3 -3
- package/dist/webmcp-D7IlA0fu.js.map +1 -0
- package/dist/worker/index.d.ts +3 -4
- package/dist/worker/index.js +5 -3
- package/dist/worker/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/ChannelProtocol-ByNoHujM.d.ts +0 -247
- package/dist/app-Yig59Ygf.js +0 -326
- package/dist/app-Yig59Ygf.js.map +0 -1
- package/dist/createComponent-3n1ZSql8.js.map +0 -1
- package/dist/persisted-fUzIU8FZ.js.map +0 -1
- package/dist/remote-BoB8zh7z.js.map +0 -1
- package/dist/serveChannels-BlEag7WU.js.map +0 -1
- package/dist/webmcp-CdDq2nzS.js.map +0 -1
|
@@ -1,577 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
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
|
|
1
|
+
import { i as equalityOf, n as computed, o as internalState, p as trackRead } from "./computed-_cYcdczA.js";
|
|
2
|
+
import { Observable, Subject, debounceTime, distinctUntilChanged, map, of, skip, tap, throttleTime } from "rxjs";
|
|
575
3
|
//#region src/select.ts
|
|
576
4
|
function select(source, keyOrProject, options = {}) {
|
|
577
5
|
const project = typeof keyOrProject === "function" ? keyOrProject : (value) => value === null || value === void 0 ? void 0 : value[keyOrProject];
|
|
@@ -1751,6 +1179,6 @@ function persisted(adapter, key, options) {
|
|
|
1751
1179
|
return new PersistedState(adapter, key, options);
|
|
1752
1180
|
}
|
|
1753
1181
|
//#endregion
|
|
1754
|
-
export {
|
|
1182
|
+
export { Resource as _, undoable as a, classifyStorageError as c, storageReadValue as d, pick as f, mutate as g, throttled as h, OpfsStorage as i, storageErrorMessage as l, debounced as m, persisted as n, UndoStack as o, pickKeys as p, IndexedDbStorage as r, MemoryStorage as s, PersistedState as t, storageReadFailure as u, resource as v, select as y };
|
|
1755
1183
|
|
|
1756
|
-
//# sourceMappingURL=persisted-
|
|
1184
|
+
//# sourceMappingURL=persisted-8meJmsOh.js.map
|