@esportsplus/reactivity 0.31.3 → 0.33.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/README.md +43 -6
- package/bench/cellx.bench.ts +61 -0
- package/bench/dynamic.bench.ts +103 -0
- package/bench/kairo.bench.ts +368 -0
- package/bench/lib/reactive-adapter.ts +51 -0
- package/bench/molbench.bench.ts +75 -0
- package/{tests/bench/system.ts → bench/system.bench.ts} +3 -3
- package/build/compiler/array.d.ts +1 -1
- package/build/compiler/constants.d.ts +7 -6
- package/build/compiler/constants.js +6 -7
- package/build/compiler/object.d.ts +1 -1
- package/build/compiler/object.js +1 -2
- package/build/compiler/primitives.d.ts +1 -1
- package/build/constants.d.ts +3 -1
- package/build/constants.js +3 -1
- package/build/reactive/array.d.ts +7 -3
- package/build/reactive/array.js +32 -24
- package/build/reactive/index.d.ts +2 -2
- package/build/reactive/index.js +1 -1
- package/build/reactive/object.d.ts +4 -4
- package/build/reactive/object.js +8 -23
- package/build/system.d.ts +15 -6
- package/build/system.js +432 -69
- package/build/types.d.ts +22 -4
- package/package.json +10 -7
- package/src/compiler/array.ts +1 -1
- package/src/compiler/constants.ts +8 -6
- package/src/compiler/index.ts +2 -1
- package/src/compiler/object.ts +2 -2
- package/src/compiler/plugins/vite.ts +1 -0
- package/src/compiler/primitives.ts +1 -1
- package/src/constants.ts +6 -2
- package/src/reactive/array.ts +50 -35
- package/src/reactive/index.ts +6 -6
- package/src/reactive/object.ts +16 -41
- package/src/system.ts +635 -80
- package/src/types.ts +38 -9
- package/test/async-computed.test.ts +409 -0
- package/test/async-errors.test.ts +146 -0
- package/test/async-hardening.test.ts +73 -0
- package/test/async-iterable.test.ts +221 -0
- package/test/async-nested.test.ts +19 -0
- package/test/deep-graphs.test.ts +215 -0
- package/{tests/effects.ts → test/effects.test.ts} +122 -0
- package/test/equals.test.ts +142 -0
- package/test/errors.test.ts +201 -0
- package/test/flush.test.ts +185 -0
- package/test/glitch-freedom.test.ts +115 -0
- package/test/invalidate.test.ts +147 -0
- package/test/lib/wait-for.ts +28 -0
- package/test/pending-writes.test.ts +139 -0
- package/{tests/reactive.ts → test/reactive/reactive.test.ts} +20 -19
- package/test/read-dedup.test.ts +120 -0
- package/test/signal-selector.test.ts +187 -0
- package/{tests/system.ts → test/system.test.ts} +210 -19
- package/test/tsconfig.json +16 -0
- package/test/untrack.test.ts +146 -0
- package/vitest.config.ts +2 -2
- package/tests/async-computed.ts +0 -239
- /package/{tests/bench/array.ts → bench/reactive/array.bench.ts} +0 -0
- /package/{tests/bench/reactive-object.ts → bench/reactive/reactive-object.bench.ts} +0 -0
- /package/{tests → bench}/tsconfig.json +0 -0
- /package/{tests/compiler.ts → test/compiler/compiler.test.ts} +0 -0
- /package/{tests/primitives.ts → test/primitives.test.ts} +0 -0
- /package/{tests/array.ts → test/reactive/array.test.ts} +0 -0
- /package/{tests/nested.ts → test/reactive/nested.test.ts} +0 -0
- /package/{tests/objects.ts → test/reactive/objects.test.ts} +0 -0
package/build/system.js
CHANGED
|
@@ -1,20 +1,52 @@
|
|
|
1
|
-
import { SIGNAL, STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED, STATE_CHECK, STATE_COMPUTED, STATE_DIRTY, STATE_IN_HEAP, STATE_NOTIFY_MASK, STATE_RECOMPUTING } from './constants.js';
|
|
2
|
-
import { isObject } from '@esportsplus/utilities';
|
|
3
|
-
let depth = 0, heap = new Array(64), heap_i = 0, heap_n = 0, linkPoolHead = null, microtask = queueMicrotask, notified = false, observer = null, scope = null, stabilizer = STABILIZER_IDLE, version = 0;
|
|
1
|
+
import { PACKAGE_NAME, SIGNAL, STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED, STATE_CHECK, STATE_COMPUTED, STATE_DIRTY, STATE_EFFECT, STATE_ERROR, STATE_IN_HEAP, STATE_NOTIFY_MASK, STATE_RECOMPUTING } from './constants.js';
|
|
2
|
+
import { isObject, isPromise } from '@esportsplus/utilities';
|
|
3
|
+
let asyncMeta = new WeakMap(), depth = 0, disposeHead = null, draining = false, heap = new Array(64), heap_i = 0, heap_n = 0, linkPoolHead = null, microtask = queueMicrotask, notified = false, observer = null, pendingHead = null, scope = null, stabilizer = STABILIZER_IDLE, version = 0, walkPoolHead = null, writes = 0;
|
|
4
|
+
function walkPop(walk) {
|
|
5
|
+
let prev = walk.prev;
|
|
6
|
+
walk.computed = null;
|
|
7
|
+
walk.link = null;
|
|
8
|
+
walk.prev = walkPoolHead;
|
|
9
|
+
walkPoolHead = walk;
|
|
10
|
+
return prev;
|
|
11
|
+
}
|
|
12
|
+
function walkPush(computed, link, prev) {
|
|
13
|
+
let walk = walkPoolHead;
|
|
14
|
+
if (walk !== null) {
|
|
15
|
+
walkPoolHead = walk.prev;
|
|
16
|
+
walk.computed = computed;
|
|
17
|
+
walk.link = link;
|
|
18
|
+
walk.prev = prev;
|
|
19
|
+
return walk;
|
|
20
|
+
}
|
|
21
|
+
return { computed, link, prev };
|
|
22
|
+
}
|
|
4
23
|
function cleanup(computed) {
|
|
5
24
|
if (!computed.cleanup) {
|
|
6
25
|
return;
|
|
7
26
|
}
|
|
8
|
-
let value = computed.cleanup;
|
|
27
|
+
let errors = [], value = computed.cleanup;
|
|
28
|
+
computed.cleanup = null;
|
|
9
29
|
if (typeof value === 'function') {
|
|
10
|
-
|
|
30
|
+
try {
|
|
31
|
+
value();
|
|
32
|
+
}
|
|
33
|
+
catch (e) {
|
|
34
|
+
errors.push(e);
|
|
35
|
+
}
|
|
11
36
|
}
|
|
12
37
|
else {
|
|
13
38
|
for (let i = 0, n = value.length; i < n; i++) {
|
|
14
|
-
|
|
39
|
+
try {
|
|
40
|
+
value[i]();
|
|
41
|
+
}
|
|
42
|
+
catch (e) {
|
|
43
|
+
errors.push(e);
|
|
44
|
+
}
|
|
15
45
|
}
|
|
16
46
|
}
|
|
17
|
-
|
|
47
|
+
if (errors.length) {
|
|
48
|
+
throw errors.length === 1 ? errors[0] : new AggregateError(errors, `${PACKAGE_NAME}: cleanup produced multiple errors`);
|
|
49
|
+
}
|
|
18
50
|
}
|
|
19
51
|
function deleteFromHeap(computed) {
|
|
20
52
|
let state = computed.state;
|
|
@@ -39,6 +71,18 @@ function deleteFromHeap(computed) {
|
|
|
39
71
|
computed.nextHeap = undefined;
|
|
40
72
|
computed.prevHeap = computed;
|
|
41
73
|
}
|
|
74
|
+
function drainPending() {
|
|
75
|
+
let node = pendingHead;
|
|
76
|
+
pendingHead = null;
|
|
77
|
+
while (node !== null) {
|
|
78
|
+
let next = node.nextPending === node ? null : node.nextPending;
|
|
79
|
+
node.nextPending = null;
|
|
80
|
+
for (let link = node.subs; link; link = link.nextSub) {
|
|
81
|
+
insertIntoHeap(link.sub);
|
|
82
|
+
}
|
|
83
|
+
node = next;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
42
86
|
function insertIntoHeap(computed) {
|
|
43
87
|
let state = computed.state;
|
|
44
88
|
if (state & STATE_IN_HEAP) {
|
|
@@ -63,14 +107,19 @@ function insertIntoHeap(computed) {
|
|
|
63
107
|
}
|
|
64
108
|
}
|
|
65
109
|
function link(dep, sub) {
|
|
110
|
+
if (dep.rv === version) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
66
113
|
let prevDep = sub.depsTail;
|
|
67
114
|
if (prevDep && prevDep.dep === dep) {
|
|
115
|
+
dep.rv = version;
|
|
68
116
|
return;
|
|
69
117
|
}
|
|
70
118
|
let nextDep = null;
|
|
71
119
|
if (sub.state & STATE_RECOMPUTING) {
|
|
72
120
|
nextDep = prevDep ? prevDep.nextDep : sub.deps;
|
|
73
121
|
if (nextDep && nextDep.dep === dep) {
|
|
122
|
+
dep.rv = version;
|
|
74
123
|
nextDep.version = version;
|
|
75
124
|
sub.depsTail = nextDep;
|
|
76
125
|
return;
|
|
@@ -80,8 +129,10 @@ function link(dep, sub) {
|
|
|
80
129
|
if (prevSub &&
|
|
81
130
|
prevSub.version === version &&
|
|
82
131
|
prevSub.sub === sub) {
|
|
132
|
+
dep.rv = version;
|
|
83
133
|
return;
|
|
84
134
|
}
|
|
135
|
+
dep.rv = version;
|
|
85
136
|
let pooled = linkPoolHead, newLink = sub.depsTail =
|
|
86
137
|
dep.subsTail = pooled
|
|
87
138
|
? (linkPoolHead = pooled.nextDep,
|
|
@@ -119,9 +170,52 @@ function notify(computed, newState) {
|
|
|
119
170
|
return;
|
|
120
171
|
}
|
|
121
172
|
computed.state = state | newState;
|
|
122
|
-
|
|
123
|
-
|
|
173
|
+
let link = computed.subs, stack = null;
|
|
174
|
+
for (;;) {
|
|
175
|
+
while (link !== null) {
|
|
176
|
+
let sub = link.sub, subState = sub.state;
|
|
177
|
+
if ((subState & STATE_NOTIFY_MASK) < STATE_CHECK) {
|
|
178
|
+
sub.state = subState | STATE_CHECK;
|
|
179
|
+
if (sub.subs !== null) {
|
|
180
|
+
if (link.nextSub !== null) {
|
|
181
|
+
stack = walkPush(null, link.nextSub, stack);
|
|
182
|
+
}
|
|
183
|
+
link = sub.subs;
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
link = link.nextSub;
|
|
188
|
+
}
|
|
189
|
+
if (stack === null) {
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
link = stack.link;
|
|
193
|
+
stack = walkPop(stack);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function pull(node) {
|
|
197
|
+
if (!notified) {
|
|
198
|
+
notified = true;
|
|
199
|
+
for (let i = 0; i <= heap_n; i++) {
|
|
200
|
+
for (let computed = heap[i]; computed !== undefined; computed = computed.nextHeap) {
|
|
201
|
+
notify(computed, STATE_DIRTY);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
124
204
|
}
|
|
205
|
+
let o = observer;
|
|
206
|
+
observer = null;
|
|
207
|
+
update(node);
|
|
208
|
+
observer = o;
|
|
209
|
+
}
|
|
210
|
+
function propagate(computed) {
|
|
211
|
+
for (let c = computed.subs; c; c = c.nextSub) {
|
|
212
|
+
let s = c.sub, state = s.state;
|
|
213
|
+
if (state & STATE_CHECK) {
|
|
214
|
+
s.state = state | STATE_DIRTY;
|
|
215
|
+
}
|
|
216
|
+
insertIntoHeap(s);
|
|
217
|
+
}
|
|
218
|
+
schedule();
|
|
125
219
|
}
|
|
126
220
|
function recompute(computed, del) {
|
|
127
221
|
if (del) {
|
|
@@ -132,12 +226,19 @@ function recompute(computed, del) {
|
|
|
132
226
|
computed.prevHeap = computed;
|
|
133
227
|
}
|
|
134
228
|
if (computed.cleanup) {
|
|
135
|
-
|
|
229
|
+
try {
|
|
230
|
+
cleanup(computed);
|
|
231
|
+
}
|
|
232
|
+
catch (e) {
|
|
233
|
+
microtask(() => {
|
|
234
|
+
throw e;
|
|
235
|
+
});
|
|
236
|
+
}
|
|
136
237
|
}
|
|
137
|
-
let o = observer, ok = true, value;
|
|
238
|
+
let err, flags = computed.state & STATE_EFFECT, hadError = computed.state & STATE_ERROR, o = observer, ok = true, value, w = writes;
|
|
138
239
|
observer = computed;
|
|
139
240
|
computed.depsTail = null;
|
|
140
|
-
computed.state = STATE_COMPUTED | STATE_RECOMPUTING;
|
|
241
|
+
computed.state = STATE_COMPUTED | STATE_RECOMPUTING | flags;
|
|
141
242
|
depth++;
|
|
142
243
|
version++;
|
|
143
244
|
try {
|
|
@@ -145,10 +246,13 @@ function recompute(computed, del) {
|
|
|
145
246
|
}
|
|
146
247
|
catch (e) {
|
|
147
248
|
ok = false;
|
|
249
|
+
err = e;
|
|
148
250
|
}
|
|
149
251
|
depth--;
|
|
252
|
+
version++;
|
|
150
253
|
observer = o;
|
|
151
|
-
computed.state = STATE_COMPUTED;
|
|
254
|
+
computed.state = STATE_COMPUTED | flags;
|
|
255
|
+
computed.gv = w;
|
|
152
256
|
let depsTail = computed.depsTail, remove = depsTail ? depsTail.nextDep : computed.deps;
|
|
153
257
|
if (remove) {
|
|
154
258
|
do {
|
|
@@ -161,16 +265,27 @@ function recompute(computed, del) {
|
|
|
161
265
|
computed.deps = null;
|
|
162
266
|
}
|
|
163
267
|
}
|
|
164
|
-
if (ok
|
|
268
|
+
if (ok) {
|
|
269
|
+
computed.error = null;
|
|
270
|
+
let changed = computed.equals === null || computed.value === undefined
|
|
271
|
+
? value !== computed.value
|
|
272
|
+
: !computed.equals(computed.value, value);
|
|
165
273
|
computed.value = value;
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
274
|
+
if (changed || hadError) {
|
|
275
|
+
propagate(computed);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
computed.error = err;
|
|
280
|
+
computed.state |= STATE_ERROR;
|
|
281
|
+
if (flags) {
|
|
282
|
+
microtask(() => {
|
|
283
|
+
throw err;
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
propagate(computed);
|
|
172
288
|
}
|
|
173
|
-
schedule();
|
|
174
289
|
}
|
|
175
290
|
}
|
|
176
291
|
function schedule() {
|
|
@@ -190,6 +305,9 @@ function stabilize() {
|
|
|
190
305
|
observer = null;
|
|
191
306
|
stabilizer = STABILIZER_RUNNING;
|
|
192
307
|
for (heap_i = 0; heap_i <= heap_n; heap_i++) {
|
|
308
|
+
if (pendingHead !== null) {
|
|
309
|
+
drainPending();
|
|
310
|
+
}
|
|
193
311
|
let computed = heap[heap_i];
|
|
194
312
|
heap[heap_i] = undefined;
|
|
195
313
|
while (computed !== undefined) {
|
|
@@ -221,8 +339,17 @@ function unlink(link) {
|
|
|
221
339
|
if (prevSub) {
|
|
222
340
|
prevSub.nextSub = nextSub;
|
|
223
341
|
}
|
|
224
|
-
else if ((dep.subs = nextSub) === null
|
|
225
|
-
|
|
342
|
+
else if ((dep.subs = nextSub) === null) {
|
|
343
|
+
if (dep.state & STATE_COMPUTED) {
|
|
344
|
+
dispose(dep);
|
|
345
|
+
}
|
|
346
|
+
else if (dep.parent !== undefined) {
|
|
347
|
+
let parent = dep.parent;
|
|
348
|
+
parent.keys.delete(dep.key);
|
|
349
|
+
if (parent.keys.size === 0) {
|
|
350
|
+
parent.keys = null;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
226
353
|
}
|
|
227
354
|
link.dep = link.sub = null;
|
|
228
355
|
link.nextSub = link.prevSub = null;
|
|
@@ -230,44 +357,137 @@ function unlink(link) {
|
|
|
230
357
|
linkPoolHead = link;
|
|
231
358
|
return nextDep;
|
|
232
359
|
}
|
|
233
|
-
function update(
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
if (
|
|
238
|
-
|
|
239
|
-
|
|
360
|
+
function update(root) {
|
|
361
|
+
let link = null, node = root, resuming = false, stack = null, w = writes;
|
|
362
|
+
for (;;) {
|
|
363
|
+
if (!resuming) {
|
|
364
|
+
if (node.gv === w) {
|
|
365
|
+
node.state &= ~STATE_NOTIFY_MASK;
|
|
366
|
+
}
|
|
367
|
+
else if (node.state & STATE_CHECK) {
|
|
368
|
+
link = node.deps;
|
|
369
|
+
resuming = true;
|
|
370
|
+
}
|
|
371
|
+
else if (node.state & STATE_DIRTY) {
|
|
372
|
+
recompute(node, true);
|
|
373
|
+
node.state &= ~STATE_NOTIFY_MASK;
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
node.gv = w;
|
|
377
|
+
node.state &= ~STATE_NOTIFY_MASK;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (resuming) {
|
|
381
|
+
let descended = false;
|
|
382
|
+
while (link !== null && !(node.state & STATE_DIRTY)) {
|
|
383
|
+
if (link.dep.state & STATE_COMPUTED) {
|
|
384
|
+
stack = walkPush(node, link.nextDep, stack);
|
|
385
|
+
node = link.dep;
|
|
386
|
+
link = null;
|
|
387
|
+
resuming = false;
|
|
388
|
+
descended = true;
|
|
240
389
|
break;
|
|
241
390
|
}
|
|
391
|
+
link = link.nextDep;
|
|
392
|
+
}
|
|
393
|
+
if (descended) {
|
|
394
|
+
continue;
|
|
242
395
|
}
|
|
396
|
+
if (node.state & STATE_DIRTY) {
|
|
397
|
+
recompute(node, true);
|
|
398
|
+
}
|
|
399
|
+
else {
|
|
400
|
+
node.gv = w;
|
|
401
|
+
}
|
|
402
|
+
node.state &= ~STATE_NOTIFY_MASK;
|
|
403
|
+
resuming = false;
|
|
243
404
|
}
|
|
405
|
+
if (stack === null) {
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
node = stack.computed;
|
|
409
|
+
link = stack.link;
|
|
410
|
+
stack = walkPop(stack);
|
|
411
|
+
resuming = true;
|
|
244
412
|
}
|
|
245
|
-
if (computed.state & STATE_DIRTY) {
|
|
246
|
-
recompute(computed, true);
|
|
247
|
-
}
|
|
248
|
-
computed.state = STATE_COMPUTED;
|
|
249
413
|
}
|
|
250
|
-
|
|
251
|
-
let
|
|
252
|
-
|
|
253
|
-
let
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
write(
|
|
414
|
+
function makeAsyncComputed(factory) {
|
|
415
|
+
let error = signal(undefined), node = signal(undefined), pending = signal(false), v = 0;
|
|
416
|
+
let stop = effect(() => {
|
|
417
|
+
let fail = (e) => {
|
|
418
|
+
if (id === v && !(factory.state & (STATE_IN_HEAP | STATE_NOTIFY_MASK))) {
|
|
419
|
+
write(error, e === undefined ? new Error('reactivity: async computed rejected with undefined') : e);
|
|
420
|
+
write(pending, false);
|
|
257
421
|
}
|
|
258
|
-
},
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
422
|
+
}, id = ++v, result = read(factory);
|
|
423
|
+
if (isPromise(result)) {
|
|
424
|
+
if (id === v && !(factory.state & (STATE_IN_HEAP | STATE_NOTIFY_MASK))) {
|
|
425
|
+
write(pending, true);
|
|
426
|
+
}
|
|
427
|
+
result.then((value) => {
|
|
428
|
+
if (id === v && !(factory.state & (STATE_IN_HEAP | STATE_NOTIFY_MASK))) {
|
|
429
|
+
write(error, undefined);
|
|
430
|
+
write(node, value);
|
|
431
|
+
write(pending, false);
|
|
432
|
+
}
|
|
433
|
+
}, fail);
|
|
434
|
+
}
|
|
435
|
+
else if (result != null && typeof result[Symbol.asyncIterator] === 'function') {
|
|
436
|
+
let it = result[Symbol.asyncIterator]();
|
|
437
|
+
onCleanup(() => {
|
|
438
|
+
it.return?.();
|
|
439
|
+
});
|
|
440
|
+
let step = (r) => {
|
|
441
|
+
if (id !== v || (factory.state & (STATE_IN_HEAP | STATE_NOTIFY_MASK))) {
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
if (!r.done) {
|
|
445
|
+
write(error, undefined);
|
|
446
|
+
write(node, r.value);
|
|
447
|
+
write(pending, false);
|
|
448
|
+
it.next().then(step, fail);
|
|
449
|
+
}
|
|
450
|
+
else {
|
|
451
|
+
write(pending, false);
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
if (id === v && !(factory.state & (STATE_IN_HEAP | STATE_NOTIFY_MASK))) {
|
|
455
|
+
write(pending, true);
|
|
456
|
+
}
|
|
457
|
+
untrack(() => it.next()).then(step, fail);
|
|
458
|
+
}
|
|
459
|
+
else {
|
|
460
|
+
write(error, undefined);
|
|
461
|
+
write(node, result);
|
|
462
|
+
write(pending, false);
|
|
463
|
+
}
|
|
464
|
+
});
|
|
465
|
+
let wrapper = makeComputed(() => {
|
|
466
|
+
let e = read(error);
|
|
467
|
+
if (e !== undefined) {
|
|
468
|
+
throw e;
|
|
469
|
+
}
|
|
470
|
+
return read(node);
|
|
471
|
+
});
|
|
472
|
+
wrapper.pending = pending;
|
|
473
|
+
asyncMeta.set(wrapper, { factory: factory });
|
|
474
|
+
wrapper.disposal = stop;
|
|
475
|
+
return wrapper;
|
|
476
|
+
}
|
|
477
|
+
function makeComputed(fn, eager = false) {
|
|
263
478
|
let self = {
|
|
264
479
|
cleanup: null,
|
|
265
480
|
deps: null,
|
|
266
481
|
depsTail: null,
|
|
482
|
+
disposal: null,
|
|
483
|
+
equals: null,
|
|
484
|
+
error: null,
|
|
267
485
|
fn: fn,
|
|
486
|
+
gv: 0,
|
|
268
487
|
height: 0,
|
|
269
488
|
nextHeap: undefined,
|
|
270
489
|
prevHeap: null,
|
|
490
|
+
rv: 0,
|
|
271
491
|
state: STATE_COMPUTED,
|
|
272
492
|
subs: null,
|
|
273
493
|
subsTail: null,
|
|
@@ -279,6 +499,10 @@ const computed = (fn) => {
|
|
|
279
499
|
self.height = observer.height;
|
|
280
500
|
recompute(self, false);
|
|
281
501
|
}
|
|
502
|
+
else if (eager) {
|
|
503
|
+
self.height = observer.height + 1;
|
|
504
|
+
recompute(self, false);
|
|
505
|
+
}
|
|
282
506
|
else {
|
|
283
507
|
self.height = observer.height + 1;
|
|
284
508
|
insertIntoHeap(self);
|
|
@@ -295,24 +519,101 @@ const computed = (fn) => {
|
|
|
295
519
|
}
|
|
296
520
|
}
|
|
297
521
|
return self;
|
|
522
|
+
}
|
|
523
|
+
const batch = (fn) => {
|
|
524
|
+
depth++;
|
|
525
|
+
try {
|
|
526
|
+
return fn();
|
|
527
|
+
}
|
|
528
|
+
finally {
|
|
529
|
+
depth--;
|
|
530
|
+
if (!depth) {
|
|
531
|
+
schedule();
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
const computed = (fn, equals = null) => {
|
|
536
|
+
let o = observer, self = makeComputed(fn, true), value = self.value;
|
|
537
|
+
if (isPromise(value) || (value != null && typeof value[Symbol.asyncIterator] === 'function')) {
|
|
538
|
+
self = untrack(() => makeAsyncComputed(self));
|
|
539
|
+
if (o) {
|
|
540
|
+
onCleanup(() => dispose(self));
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
self.equals = equals;
|
|
544
|
+
return self;
|
|
545
|
+
};
|
|
546
|
+
computed.invalidate = (c) => {
|
|
547
|
+
let meta = asyncMeta.get(c);
|
|
548
|
+
if (meta) {
|
|
549
|
+
computed.invalidate(meta.factory);
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
writes++;
|
|
553
|
+
c.state |= STATE_DIRTY;
|
|
554
|
+
insertIntoHeap(c);
|
|
555
|
+
schedule();
|
|
298
556
|
};
|
|
299
557
|
const dispose = (computed) => {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
dep = unlink(dep);
|
|
558
|
+
if (draining) {
|
|
559
|
+
disposeHead = walkPush(computed, null, disposeHead);
|
|
560
|
+
return;
|
|
304
561
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
562
|
+
draining = true;
|
|
563
|
+
let node = computed;
|
|
564
|
+
try {
|
|
565
|
+
for (;;) {
|
|
566
|
+
deleteFromHeap(node);
|
|
567
|
+
let dep = node.deps;
|
|
568
|
+
while (dep) {
|
|
569
|
+
dep = unlink(dep);
|
|
570
|
+
}
|
|
571
|
+
node.deps = null;
|
|
572
|
+
if (node.cleanup) {
|
|
573
|
+
cleanup(node);
|
|
574
|
+
}
|
|
575
|
+
if (node.disposal) {
|
|
576
|
+
let d = node.disposal;
|
|
577
|
+
node.disposal = null;
|
|
578
|
+
d();
|
|
579
|
+
}
|
|
580
|
+
if (disposeHead === null) {
|
|
581
|
+
break;
|
|
582
|
+
}
|
|
583
|
+
node = disposeHead.computed;
|
|
584
|
+
disposeHead = walkPop(disposeHead);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
finally {
|
|
588
|
+
draining = false;
|
|
308
589
|
}
|
|
309
590
|
};
|
|
310
|
-
const effect = (fn) => {
|
|
311
|
-
let
|
|
591
|
+
const effect = (fn, apply) => {
|
|
592
|
+
let prev;
|
|
593
|
+
let c = makeComputed(apply
|
|
594
|
+
? (o) => {
|
|
595
|
+
let v = fn(o);
|
|
596
|
+
untrack(() => apply(v, prev));
|
|
597
|
+
prev = v;
|
|
598
|
+
return v;
|
|
599
|
+
}
|
|
600
|
+
: fn);
|
|
601
|
+
c.state |= STATE_EFFECT;
|
|
602
|
+
if (c.state & STATE_ERROR) {
|
|
603
|
+
let err = c.error;
|
|
604
|
+
microtask(() => {
|
|
605
|
+
throw err;
|
|
606
|
+
});
|
|
607
|
+
}
|
|
312
608
|
return () => {
|
|
313
609
|
dispose(c);
|
|
314
610
|
};
|
|
315
611
|
};
|
|
612
|
+
const flush = () => {
|
|
613
|
+
while (stabilizer === STABILIZER_SCHEDULED) {
|
|
614
|
+
stabilize();
|
|
615
|
+
}
|
|
616
|
+
};
|
|
316
617
|
const isComputed = (value) => {
|
|
317
618
|
return isObject(value) && !!(value.state & STATE_COMPUTED);
|
|
318
619
|
};
|
|
@@ -336,27 +637,37 @@ const onCleanup = (fn) => {
|
|
|
336
637
|
}
|
|
337
638
|
return fn;
|
|
338
639
|
};
|
|
640
|
+
const peek = (node) => {
|
|
641
|
+
if (node.state & STATE_COMPUTED) {
|
|
642
|
+
if (pendingHead !== null) {
|
|
643
|
+
drainPending();
|
|
644
|
+
}
|
|
645
|
+
pull(node);
|
|
646
|
+
}
|
|
647
|
+
if (node.state & STATE_ERROR) {
|
|
648
|
+
throw node.error;
|
|
649
|
+
}
|
|
650
|
+
return node.value;
|
|
651
|
+
};
|
|
339
652
|
const read = (node) => {
|
|
340
653
|
if (observer) {
|
|
341
654
|
link(node, observer);
|
|
342
655
|
if (node.state & STATE_COMPUTED) {
|
|
656
|
+
if (pendingHead !== null) {
|
|
657
|
+
drainPending();
|
|
658
|
+
}
|
|
343
659
|
let height = node.height;
|
|
344
660
|
if (height >= observer.height) {
|
|
345
661
|
observer.height = height + 1;
|
|
346
662
|
}
|
|
347
663
|
if (height >= heap_i || node.state & STATE_NOTIFY_MASK) {
|
|
348
|
-
|
|
349
|
-
notified = true;
|
|
350
|
-
for (let i = 0; i <= heap_n; i++) {
|
|
351
|
-
for (let computed = heap[i]; computed !== undefined; computed = computed.nextHeap) {
|
|
352
|
-
notify(computed, STATE_DIRTY);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
update(node);
|
|
664
|
+
pull(node);
|
|
357
665
|
}
|
|
358
666
|
}
|
|
359
667
|
}
|
|
668
|
+
if (node.state & STATE_ERROR) {
|
|
669
|
+
throw node.error;
|
|
670
|
+
}
|
|
360
671
|
return node.value;
|
|
361
672
|
};
|
|
362
673
|
const root = (fn) => {
|
|
@@ -380,26 +691,78 @@ const root = (fn) => {
|
|
|
380
691
|
return value;
|
|
381
692
|
};
|
|
382
693
|
root.disposables = 0;
|
|
383
|
-
const signal = (value) => {
|
|
694
|
+
const signal = (value, equals = null) => {
|
|
384
695
|
return {
|
|
696
|
+
equals: equals,
|
|
697
|
+
key: undefined,
|
|
698
|
+
keys: null,
|
|
699
|
+
nextPending: null,
|
|
700
|
+
parent: undefined,
|
|
701
|
+
rv: 0,
|
|
702
|
+
state: 0,
|
|
385
703
|
subs: null,
|
|
386
704
|
subsTail: null,
|
|
387
705
|
type: SIGNAL,
|
|
388
706
|
value,
|
|
389
707
|
};
|
|
390
708
|
};
|
|
709
|
+
signal.selector = (node, key) => {
|
|
710
|
+
if (!observer) {
|
|
711
|
+
return node.value === key;
|
|
712
|
+
}
|
|
713
|
+
let keys = (node.keys ??= new Map()), entry = keys.get(key);
|
|
714
|
+
if (entry === undefined) {
|
|
715
|
+
keys.set(key, entry = {
|
|
716
|
+
equals: null,
|
|
717
|
+
key,
|
|
718
|
+
keys: null,
|
|
719
|
+
nextPending: null,
|
|
720
|
+
parent: node,
|
|
721
|
+
rv: 0,
|
|
722
|
+
state: 0,
|
|
723
|
+
subs: null,
|
|
724
|
+
subsTail: null,
|
|
725
|
+
type: SIGNAL,
|
|
726
|
+
value: node.value === key
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
return read(entry);
|
|
730
|
+
};
|
|
731
|
+
const untrack = (fn) => {
|
|
732
|
+
let o = observer;
|
|
733
|
+
observer = null;
|
|
734
|
+
try {
|
|
735
|
+
return fn();
|
|
736
|
+
}
|
|
737
|
+
finally {
|
|
738
|
+
observer = o;
|
|
739
|
+
}
|
|
740
|
+
};
|
|
391
741
|
const write = (signal, value) => {
|
|
392
|
-
|
|
742
|
+
let prev = signal.value;
|
|
743
|
+
if (signal.equals === null ? prev === value : signal.equals(prev, value)) {
|
|
393
744
|
return;
|
|
394
745
|
}
|
|
395
746
|
signal.value = value;
|
|
747
|
+
writes++;
|
|
748
|
+
if (signal.keys !== null) {
|
|
749
|
+
let entry = signal.keys.get(prev);
|
|
750
|
+
if (entry !== undefined) {
|
|
751
|
+
write(entry, false);
|
|
752
|
+
}
|
|
753
|
+
entry = signal.keys.get(value);
|
|
754
|
+
if (entry !== undefined) {
|
|
755
|
+
write(entry, true);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
396
758
|
if (signal.subs === null) {
|
|
397
759
|
return;
|
|
398
760
|
}
|
|
399
761
|
notified = false;
|
|
400
|
-
|
|
401
|
-
|
|
762
|
+
if (signal.nextPending === null) {
|
|
763
|
+
signal.nextPending = pendingHead ?? signal;
|
|
764
|
+
pendingHead = signal;
|
|
402
765
|
}
|
|
403
766
|
schedule();
|
|
404
767
|
};
|
|
405
|
-
export {
|
|
768
|
+
export { batch, computed, dispose, effect, flush, isComputed, isSignal, onCleanup, peek, read, root, signal, untrack, write };
|