@lazily-hub/lazily-js 0.19.0 → 0.20.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/BENCHMARKS.md +11 -11
- package/package.json +1 -1
- package/src/reactive.js +133 -92
package/BENCHMARKS.md
CHANGED
|
@@ -19,7 +19,7 @@ like-for-like single-process counterpart here and are intentionally omitted.
|
|
|
19
19
|
|
|
20
20
|
<!-- benchmark-results:start -->
|
|
21
21
|
|
|
22
|
-
Generated for package `@lazily-hub/lazily-js` version `0.
|
|
22
|
+
Generated for package `@lazily-hub/lazily-js` version `0.19.0`.
|
|
23
23
|
|
|
24
24
|
Environment: Node.js `26.4.0` on `linux x64`.
|
|
25
25
|
|
|
@@ -33,16 +33,16 @@ Mean wall-clock time per iteration; 95% CI half-width from the standard error.
|
|
|
33
33
|
|
|
34
34
|
| Group | Case | Mean | 95% CI | p75 | p99 | Samples |
|
|
35
35
|
|---|---|---:|---:|---:|---:|---:|
|
|
36
|
-
| cached_reads | context |
|
|
37
|
-
| cold_first_get | context |
|
|
38
|
-
| dependency_fan_out | context / 32 |
|
|
39
|
-
| dependency_fan_out | context / 256 |
|
|
40
|
-
| set_cell_invalidation | high_fan_out / 512 |
|
|
41
|
-
| memo_equality_suppression | context | 2.
|
|
42
|
-
| effect_flushing | context |
|
|
43
|
-
| batch_storms | context / 64 |
|
|
44
|
-
| typed_cache_reads | context_cell |
|
|
45
|
-
| typed_cache_reads | context_slot |
|
|
36
|
+
| cached_reads | context | 31.707 ns | ± 1.826 ns | 29.867 ns | 78.222 ns | 100 |
|
|
37
|
+
| cold_first_get | context | 698.810 ns | ± 46.591 ns | 600.000 ns | 1.410 us | 100 |
|
|
38
|
+
| dependency_fan_out | context / 32 | 5.263 us | ± 858.863 ns | 4.753 us | 16.289 us | 100 |
|
|
39
|
+
| dependency_fan_out | context / 256 | 47.575 us | ± 3.908 us | 45.595 us | 132.218 us | 100 |
|
|
40
|
+
| set_cell_invalidation | high_fan_out / 512 | 6.692 us | ± 3.311 us | 5.840 us | 111.143 us | 100 |
|
|
41
|
+
| memo_equality_suppression | context | 2.546 us | ± 256.417 ns | 2.553 us | 5.435 us | 100 |
|
|
42
|
+
| effect_flushing | context | 129.166 ns | ± 11.208 ns | 131.217 ns | 406.377 ns | 100 |
|
|
43
|
+
| batch_storms | context / 64 | 12.092 us | ± 123.522 ns | 12.286 us | 14.168 us | 100 |
|
|
44
|
+
| typed_cache_reads | context_cell | 33.701 ns | ± 2.005 ns | 32.755 ns | 55.480 ns | 100 |
|
|
45
|
+
| typed_cache_reads | context_slot | 42.440 ns | ± 2.855 ns | 40.074 ns | 106.268 ns | 100 |
|
|
46
46
|
|
|
47
47
|
<!-- benchmark-results:end -->
|
|
48
48
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazily-hub/lazily-js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "Native JavaScript port of the lazily reactive core: a full reactive graph (Cell/Slot/Signal/Effect), sync/async/thread-safe keyed reactive maps (ReactiveMap/CellMap/SlotMap), the lazily-spec IPC wire types + C-ABI FFI boundary (isomorphic channel + Node native binding), keyed cell collections + LIS reconciliation, the memoized semantic tree, the move-aware sequence CRDT, the Fugue/RGA text CRDT, manufactured text identity, full-Harel state charts, an FFI state-projection consumer, and an in-library instrumentation/benchmark API.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
package/src/reactive.js
CHANGED
|
@@ -36,8 +36,7 @@ function defaultEqual(a, b) {
|
|
|
36
36
|
const bKeys = Object.keys(b);
|
|
37
37
|
return (
|
|
38
38
|
aKeys.length === bKeys.length &&
|
|
39
|
-
aKeys.every((k) =>
|
|
40
|
-
aKeys.every((k) => defaultEqual(a[k], b[k]))
|
|
39
|
+
aKeys.every((k) => k in b && defaultEqual(a[k], b[k]))
|
|
41
40
|
);
|
|
42
41
|
}
|
|
43
42
|
|
|
@@ -74,10 +73,31 @@ export class SignalHandle {
|
|
|
74
73
|
|
|
75
74
|
// -- Context -----------------------------------------------------------------
|
|
76
75
|
|
|
76
|
+
function edgeInsert(edges, id) {
|
|
77
|
+
for (let i = 0; i < edges.length; i++) {
|
|
78
|
+
if (edges[i] === id) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
edges.push(id);
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function edgeRemove(edges, id) {
|
|
87
|
+
for (let i = 0; i < edges.length; i++) {
|
|
88
|
+
if (edges[i] === id) {
|
|
89
|
+
edges[i] = edges[edges.length - 1];
|
|
90
|
+
edges.pop();
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
|
|
77
97
|
class CellNode {
|
|
78
98
|
constructor(value) {
|
|
79
99
|
this.value = value;
|
|
80
|
-
this.dependents =
|
|
100
|
+
this.dependents = [];
|
|
81
101
|
}
|
|
82
102
|
}
|
|
83
103
|
|
|
@@ -87,8 +107,8 @@ class SlotNode {
|
|
|
87
107
|
this.hasValue = false;
|
|
88
108
|
this.memo = memo;
|
|
89
109
|
this.compute = compute;
|
|
90
|
-
this.dependencies =
|
|
91
|
-
this.dependents =
|
|
110
|
+
this.dependencies = [];
|
|
111
|
+
this.dependents = [];
|
|
92
112
|
this.dirty = false;
|
|
93
113
|
this.forceRecompute = false;
|
|
94
114
|
this.inProgress = false;
|
|
@@ -98,18 +118,19 @@ class SlotNode {
|
|
|
98
118
|
class EffectNode {
|
|
99
119
|
constructor(run) {
|
|
100
120
|
this.run = run;
|
|
101
|
-
this.dependencies =
|
|
121
|
+
this.dependencies = [];
|
|
102
122
|
this.cleanup = null;
|
|
103
123
|
this.forceRun = false;
|
|
104
124
|
}
|
|
105
125
|
}
|
|
106
126
|
|
|
107
127
|
export class Context {
|
|
108
|
-
#nodes =
|
|
128
|
+
#nodes = [];
|
|
109
129
|
#nextId = 1;
|
|
110
130
|
#freeIds = [];
|
|
111
131
|
#trackingStack = [];
|
|
112
132
|
#pendingEffects = [];
|
|
133
|
+
#pendingHead = 0;
|
|
113
134
|
#scheduledEffects = new Set();
|
|
114
135
|
#flushingEffects = false;
|
|
115
136
|
#batchDepth = 0;
|
|
@@ -167,7 +188,7 @@ export class Context {
|
|
|
167
188
|
|
|
168
189
|
#cellAny(value) {
|
|
169
190
|
const id = this.#allocId();
|
|
170
|
-
this.#nodes
|
|
191
|
+
this.#nodes[id] = new CellNode(value);
|
|
171
192
|
return id;
|
|
172
193
|
}
|
|
173
194
|
|
|
@@ -185,7 +206,7 @@ export class Context {
|
|
|
185
206
|
|
|
186
207
|
#slotAny(memo, compute) {
|
|
187
208
|
const id = this.#allocId();
|
|
188
|
-
this.#nodes
|
|
209
|
+
this.#nodes[id] = new SlotNode(compute, memo);
|
|
189
210
|
return id;
|
|
190
211
|
}
|
|
191
212
|
|
|
@@ -206,7 +227,7 @@ export class Context {
|
|
|
206
227
|
const id = this.#allocId();
|
|
207
228
|
const node = new EffectNode(run);
|
|
208
229
|
node.forceRun = true; // force the initial run on registration
|
|
209
|
-
this.#nodes
|
|
230
|
+
this.#nodes[id] = node;
|
|
210
231
|
this.#scheduleEffect(id, false);
|
|
211
232
|
this.#flushEffects();
|
|
212
233
|
return id;
|
|
@@ -232,7 +253,7 @@ export class Context {
|
|
|
232
253
|
this.#registerDependency(id, frame);
|
|
233
254
|
}
|
|
234
255
|
this.#refreshSlot(id);
|
|
235
|
-
const node = this.#nodes
|
|
256
|
+
const node = this.#nodes[id];
|
|
236
257
|
if (!(node instanceof SlotNode) || !node.hasValue) {
|
|
237
258
|
throw new Error(`slot ${id} has no value`);
|
|
238
259
|
}
|
|
@@ -244,7 +265,7 @@ export class Context {
|
|
|
244
265
|
if (frame !== undefined) {
|
|
245
266
|
this.#registerDependency(id, frame);
|
|
246
267
|
}
|
|
247
|
-
const node = this.#nodes
|
|
268
|
+
const node = this.#nodes[id];
|
|
248
269
|
if (!(node instanceof CellNode)) {
|
|
249
270
|
throw new Error(`get_cell on non-cell id ${id}`);
|
|
250
271
|
}
|
|
@@ -258,7 +279,7 @@ export class Context {
|
|
|
258
279
|
}
|
|
259
280
|
|
|
260
281
|
#setCellAny(id, value) {
|
|
261
|
-
const node = this.#nodes
|
|
282
|
+
const node = this.#nodes[id];
|
|
262
283
|
if (!(node instanceof CellNode)) {
|
|
263
284
|
throw new Error(`set_cell on non-cell id ${id}`);
|
|
264
285
|
}
|
|
@@ -302,10 +323,20 @@ export class Context {
|
|
|
302
323
|
}
|
|
303
324
|
|
|
304
325
|
#flushBatched() {
|
|
305
|
-
const cells =
|
|
306
|
-
this.#batchedCells
|
|
326
|
+
const cells = this.#batchedCells;
|
|
327
|
+
this.#batchedCells = new Set();
|
|
328
|
+
const roots = [];
|
|
307
329
|
for (const id of cells) {
|
|
308
|
-
this.#
|
|
330
|
+
const node = this.#nodes[id];
|
|
331
|
+
if (node instanceof CellNode) {
|
|
332
|
+
for (const d of node.dependents) {
|
|
333
|
+
roots.push(d);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
const effects = this.#markFrontier(roots);
|
|
338
|
+
for (let i = 0; i < effects.length; i++) {
|
|
339
|
+
this.#scheduleEffect(effects[i][0], effects[i][1]);
|
|
309
340
|
}
|
|
310
341
|
this.#flushEffects();
|
|
311
342
|
}
|
|
@@ -318,18 +349,18 @@ export class Context {
|
|
|
318
349
|
|
|
319
350
|
disposeEffect(handle) {
|
|
320
351
|
const id = handle.id;
|
|
321
|
-
const idx = this.#pendingEffects.indexOf(id);
|
|
352
|
+
const idx = this.#pendingEffects.indexOf(id, this.#pendingHead);
|
|
322
353
|
if (idx !== -1) {
|
|
323
354
|
this.#pendingEffects.splice(idx, 1);
|
|
324
355
|
}
|
|
325
356
|
this.#scheduledEffects.delete(id);
|
|
326
|
-
const node = this.#nodes
|
|
357
|
+
const node = this.#nodes[id];
|
|
327
358
|
if (!(node instanceof EffectNode)) {
|
|
328
359
|
return;
|
|
329
360
|
}
|
|
330
|
-
this.#nodes
|
|
361
|
+
this.#nodes[id] = undefined;
|
|
331
362
|
this.#freeIds.push(id);
|
|
332
|
-
for (const dep of
|
|
363
|
+
for (const dep of node.dependencies) {
|
|
333
364
|
this.#removeDependentEdge(dep, id);
|
|
334
365
|
}
|
|
335
366
|
if (node.cleanup) {
|
|
@@ -338,7 +369,7 @@ export class Context {
|
|
|
338
369
|
}
|
|
339
370
|
|
|
340
371
|
isEffectActive(handle) {
|
|
341
|
-
return this.#nodes
|
|
372
|
+
return this.#nodes[handle.id] instanceof EffectNode;
|
|
342
373
|
}
|
|
343
374
|
|
|
344
375
|
disposeSignal(handle) {
|
|
@@ -350,7 +381,7 @@ export class Context {
|
|
|
350
381
|
}
|
|
351
382
|
|
|
352
383
|
isSet(handle) {
|
|
353
|
-
const node = this.#nodes
|
|
384
|
+
const node = this.#nodes[handle.id];
|
|
354
385
|
if (!(node instanceof SlotNode)) {
|
|
355
386
|
return false;
|
|
356
387
|
}
|
|
@@ -371,36 +402,39 @@ export class Context {
|
|
|
371
402
|
}
|
|
372
403
|
|
|
373
404
|
#registerDependency(depId, parentId) {
|
|
374
|
-
const dep = this.#nodes
|
|
405
|
+
const dep = this.#nodes[depId];
|
|
375
406
|
if (dep instanceof CellNode || dep instanceof SlotNode) {
|
|
376
|
-
|
|
407
|
+
const added = edgeInsert(dep.dependents, parentId);
|
|
408
|
+
if (this.#instrument && added) {
|
|
377
409
|
this.#counters.dependencyEdgesAdded++;
|
|
378
410
|
}
|
|
379
|
-
dep.dependents.add(parentId);
|
|
380
411
|
}
|
|
381
|
-
const parent = this.#nodes
|
|
412
|
+
const parent = this.#nodes[parentId];
|
|
382
413
|
if (parent instanceof SlotNode || parent instanceof EffectNode) {
|
|
383
|
-
parent.dependencies
|
|
414
|
+
edgeInsert(parent.dependencies, depId);
|
|
384
415
|
}
|
|
385
416
|
}
|
|
386
417
|
|
|
387
418
|
#removeDependentEdge(depId, parentId) {
|
|
388
|
-
const dep = this.#nodes
|
|
419
|
+
const dep = this.#nodes[depId];
|
|
389
420
|
if (dep instanceof CellNode || dep instanceof SlotNode) {
|
|
390
|
-
|
|
421
|
+
const removed = edgeRemove(dep.dependents, parentId);
|
|
422
|
+
if (this.#instrument && removed) {
|
|
391
423
|
this.#counters.dependencyEdgesRemoved++;
|
|
392
424
|
}
|
|
393
|
-
dep.dependents.delete(parentId);
|
|
394
425
|
}
|
|
395
426
|
}
|
|
396
427
|
|
|
397
428
|
// -- Internals: refresh / recompute (pull-based, glitch-free) ----------
|
|
398
429
|
|
|
399
430
|
#refreshSlot(id) {
|
|
400
|
-
const node = this.#nodes
|
|
431
|
+
const node = this.#nodes[id];
|
|
401
432
|
if (!(node instanceof SlotNode)) {
|
|
402
433
|
return false;
|
|
403
434
|
}
|
|
435
|
+
if (node.hasValue && !node.dirty && !node.forceRecompute) {
|
|
436
|
+
return false;
|
|
437
|
+
}
|
|
404
438
|
if (node.inProgress) {
|
|
405
439
|
throw new Error(
|
|
406
440
|
`lazily: circular dependency detected at slot ${id}; a computed/memo slot depends on itself`,
|
|
@@ -409,8 +443,8 @@ export class Context {
|
|
|
409
443
|
node.inProgress = true;
|
|
410
444
|
try {
|
|
411
445
|
let dependencyChanged = false;
|
|
412
|
-
for (const dep of
|
|
413
|
-
if (this.#nodes
|
|
446
|
+
for (const dep of node.dependencies) {
|
|
447
|
+
if (this.#nodes[dep] instanceof SlotNode && this.#refreshSlot(dep)) {
|
|
414
448
|
dependencyChanged = true;
|
|
415
449
|
}
|
|
416
450
|
}
|
|
@@ -430,10 +464,11 @@ export class Context {
|
|
|
430
464
|
if (this.#instrument) {
|
|
431
465
|
this.#counters.slotRecomputes++;
|
|
432
466
|
}
|
|
433
|
-
|
|
467
|
+
const oldDeps = node.dependencies;
|
|
468
|
+
node.dependencies = [];
|
|
469
|
+
for (const dep of oldDeps) {
|
|
434
470
|
this.#removeDependentEdge(dep, id);
|
|
435
471
|
}
|
|
436
|
-
node.dependencies.clear();
|
|
437
472
|
this.#trackingStack.push(id);
|
|
438
473
|
let result;
|
|
439
474
|
try {
|
|
@@ -457,13 +492,7 @@ export class Context {
|
|
|
457
492
|
}
|
|
458
493
|
|
|
459
494
|
#notifySlotValueChanged(id) {
|
|
460
|
-
|
|
461
|
-
if (!(node instanceof SlotNode)) {
|
|
462
|
-
return;
|
|
463
|
-
}
|
|
464
|
-
for (const d of [...node.dependents]) {
|
|
465
|
-
this.#invalidateDependentFromChangedValue(d);
|
|
466
|
-
}
|
|
495
|
+
this.#invalidateDependentsNow(id);
|
|
467
496
|
}
|
|
468
497
|
|
|
469
498
|
// -- Internals: invalidation propagation ------------------------------
|
|
@@ -475,58 +504,67 @@ export class Context {
|
|
|
475
504
|
* @returns {boolean}
|
|
476
505
|
*/
|
|
477
506
|
#invalidateCellDependentsNow(id) {
|
|
478
|
-
|
|
479
|
-
if (!(node instanceof CellNode) || node.dependents.size === 0) {
|
|
480
|
-
return false;
|
|
481
|
-
}
|
|
482
|
-
let scheduled = false;
|
|
483
|
-
for (const d of [...node.dependents]) {
|
|
484
|
-
if (this.#invalidateDependentFromChangedValue(d)) {
|
|
485
|
-
scheduled = true;
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
return scheduled;
|
|
507
|
+
return this.#invalidateDependentsNow(id);
|
|
489
508
|
}
|
|
490
509
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
}
|
|
506
|
-
const shouldPropagate = !node.dirty || (force && !node.forceRecompute);
|
|
507
|
-
node.dirty = true;
|
|
508
|
-
if (force) {
|
|
509
|
-
node.forceRecompute = true;
|
|
510
|
-
}
|
|
511
|
-
if (!shouldPropagate) {
|
|
510
|
+
#invalidateDependentsNow(id) {
|
|
511
|
+
const node = this.#nodes[id];
|
|
512
|
+
let roots;
|
|
513
|
+
if (node instanceof CellNode) {
|
|
514
|
+
if (node.dependents.length === 0) {
|
|
515
|
+
return false;
|
|
516
|
+
}
|
|
517
|
+
roots = node.dependents;
|
|
518
|
+
} else if (node instanceof SlotNode) {
|
|
519
|
+
if (node.dependents.length === 0) {
|
|
520
|
+
return false;
|
|
521
|
+
}
|
|
522
|
+
roots = node.dependents;
|
|
523
|
+
} else {
|
|
512
524
|
return false;
|
|
513
525
|
}
|
|
514
|
-
|
|
515
|
-
for (
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
526
|
+
const effects = this.#markFrontier(roots);
|
|
527
|
+
for (let i = 0; i < effects.length; i++) {
|
|
528
|
+
this.#scheduleEffect(effects[i][0], effects[i][1]);
|
|
529
|
+
}
|
|
530
|
+
return effects.length > 0;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
#markFrontier(roots) {
|
|
534
|
+
const effects = [];
|
|
535
|
+
const stack = [];
|
|
536
|
+
const forceStack = [];
|
|
537
|
+
for (const root of roots) {
|
|
538
|
+
stack.push(root);
|
|
539
|
+
forceStack.push(true);
|
|
540
|
+
}
|
|
541
|
+
while (stack.length > 0) {
|
|
542
|
+
const id = stack.pop();
|
|
543
|
+
const force = forceStack.pop();
|
|
544
|
+
const node = this.#nodes[id];
|
|
545
|
+
if (node instanceof SlotNode) {
|
|
546
|
+
const shouldPropagate = !node.dirty || (force && !node.forceRecompute);
|
|
547
|
+
node.dirty = true;
|
|
548
|
+
if (force) {
|
|
549
|
+
node.forceRecompute = true;
|
|
550
|
+
}
|
|
551
|
+
if (shouldPropagate) {
|
|
552
|
+
for (const depId of node.dependents) {
|
|
553
|
+
stack.push(depId);
|
|
554
|
+
forceStack.push(false);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
} else if (node instanceof EffectNode) {
|
|
558
|
+
effects.push([id, force]);
|
|
521
559
|
}
|
|
522
560
|
}
|
|
523
|
-
return
|
|
561
|
+
return effects;
|
|
524
562
|
}
|
|
525
563
|
|
|
526
564
|
// -- Internals: effect scheduling / flush ------------------------------
|
|
527
565
|
|
|
528
566
|
#scheduleEffect(id, force) {
|
|
529
|
-
const node = this.#nodes
|
|
567
|
+
const node = this.#nodes[id];
|
|
530
568
|
if (!(node instanceof EffectNode)) {
|
|
531
569
|
return;
|
|
532
570
|
}
|
|
@@ -537,8 +575,9 @@ export class Context {
|
|
|
537
575
|
this.#pendingEffects.push(id);
|
|
538
576
|
if (this.#instrument) {
|
|
539
577
|
this.#counters.effectQueuePushes++;
|
|
540
|
-
|
|
541
|
-
|
|
578
|
+
const depth = this.#pendingEffects.length - this.#pendingHead;
|
|
579
|
+
if (depth > this.#counters.maxEffectQueueDepth) {
|
|
580
|
+
this.#counters.maxEffectQueueDepth = depth;
|
|
542
581
|
}
|
|
543
582
|
}
|
|
544
583
|
}
|
|
@@ -551,8 +590,10 @@ export class Context {
|
|
|
551
590
|
this.#flushingEffects = true;
|
|
552
591
|
try {
|
|
553
592
|
while (true) {
|
|
554
|
-
const id = this.#pendingEffects
|
|
593
|
+
const id = this.#pendingEffects[this.#pendingHead++];
|
|
555
594
|
if (id === undefined) {
|
|
595
|
+
this.#pendingEffects = [];
|
|
596
|
+
this.#pendingHead = 0;
|
|
556
597
|
return;
|
|
557
598
|
}
|
|
558
599
|
this.#scheduledEffects.delete(id);
|
|
@@ -567,12 +608,12 @@ export class Context {
|
|
|
567
608
|
if (!this.#effectShouldRun(id)) {
|
|
568
609
|
return;
|
|
569
610
|
}
|
|
570
|
-
const node = this.#nodes
|
|
611
|
+
const node = this.#nodes[id];
|
|
571
612
|
if (!(node instanceof EffectNode)) {
|
|
572
613
|
return;
|
|
573
614
|
}
|
|
574
|
-
const oldDeps =
|
|
575
|
-
node.dependencies
|
|
615
|
+
const oldDeps = node.dependencies;
|
|
616
|
+
node.dependencies = [];
|
|
576
617
|
const cleanup = node.cleanup;
|
|
577
618
|
node.cleanup = null;
|
|
578
619
|
node.forceRun = false;
|
|
@@ -589,7 +630,7 @@ export class Context {
|
|
|
589
630
|
} finally {
|
|
590
631
|
this.#trackingStack.pop();
|
|
591
632
|
}
|
|
592
|
-
const current = this.#nodes
|
|
633
|
+
const current = this.#nodes[id];
|
|
593
634
|
if (current instanceof EffectNode) {
|
|
594
635
|
current.cleanup = typeof nextCleanup === "function" ? nextCleanup : null;
|
|
595
636
|
} else if (typeof nextCleanup === "function") {
|
|
@@ -598,7 +639,7 @@ export class Context {
|
|
|
598
639
|
}
|
|
599
640
|
|
|
600
641
|
#effectShouldRun(id) {
|
|
601
|
-
const node = this.#nodes
|
|
642
|
+
const node = this.#nodes[id];
|
|
602
643
|
if (!(node instanceof EffectNode)) {
|
|
603
644
|
return false;
|
|
604
645
|
}
|
|
@@ -606,7 +647,7 @@ export class Context {
|
|
|
606
647
|
return true;
|
|
607
648
|
}
|
|
608
649
|
for (const dep of node.dependencies) {
|
|
609
|
-
if (this.#nodes
|
|
650
|
+
if (this.#nodes[dep] instanceof SlotNode && this.#refreshSlot(dep)) {
|
|
610
651
|
return true;
|
|
611
652
|
}
|
|
612
653
|
}
|