@solidjs/signals 2.0.0-rc.2 → 2.0.0-rc.4
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/dist/dev.js +1454 -118
- package/dist/node.cjs +2709 -1396
- package/dist/prod/affects.js +13 -12
- package/dist/prod/boundaries.js +39 -34
- package/dist/prod/core/action.js +3 -3
- package/dist/prod/core/async.js +48 -46
- package/dist/prod/core/core.js +99 -67
- package/dist/prod/core/effect.js +25 -28
- package/dist/prod/core/external.js +2 -2
- package/dist/prod/core/graph.js +85 -49
- package/dist/prod/core/heap.js +10 -10
- package/dist/prod/core/lanes.js +19 -19
- package/dist/prod/core/optimistic.js +66 -41
- package/dist/prod/core/owner.js +13 -13
- package/dist/prod/core/scheduler.js +131 -85
- package/dist/prod/core/verdict.js +36 -15
- package/dist/prod/index.js +4 -0
- package/dist/prod/map.js +101 -101
- package/dist/prod/signals.js +1 -1
- package/dist/prod/store/index.js +2 -0
- package/dist/prod/store/next/optimistic.js +65 -11
- package/dist/prod/store/next/patch-hooks.js +13 -0
- package/dist/prod/store/next/patch.js +614 -0
- package/dist/prod/store/next/projection.js +107 -45
- package/dist/prod/store/next/reconcile.js +307 -120
- package/dist/prod/store/next/store.js +321 -92
- package/dist/prod/store/next/target.js +13 -4
- package/dist/prod/store/store.js +5 -5
- package/dist/types/core/core.d.ts +15 -1
- package/dist/types/core/dev.d.ts +8 -0
- package/dist/types/core/graph.d.ts +22 -0
- package/dist/types/core/invariants.d.ts +1 -1
- package/dist/types/core/scheduler.d.ts +12 -0
- package/dist/types/store/index.d.ts +2 -0
- package/dist/types/store/next/patch-hooks.d.ts +41 -0
- package/dist/types/store/next/patch.d.ts +91 -0
- package/dist/types/store/next/reconcile.d.ts +14 -0
- package/dist/types/store/next/store.d.ts +30 -2
- package/dist/types/store/next/target.d.ts +58 -8
- package/dist/types-cjs/core/core.d.cts +15 -1
- package/dist/types-cjs/core/dev.d.cts +8 -0
- package/dist/types-cjs/core/graph.d.cts +22 -0
- package/dist/types-cjs/core/invariants.d.cts +1 -1
- package/dist/types-cjs/core/scheduler.d.cts +12 -0
- package/dist/types-cjs/store/index.d.cts +2 -0
- package/dist/types-cjs/store/next/patch-hooks.d.cts +41 -0
- package/dist/types-cjs/store/next/patch.d.cts +91 -0
- package/dist/types-cjs/store/next/reconcile.d.cts +14 -0
- package/dist/types-cjs/store/next/store.d.cts +30 -2
- package/dist/types-cjs/store/next/target.d.cts +58 -8
- package/package.json +14 -14
- package/dist/types/store/optimistic.d.ts +0 -45
- package/dist/types/store/projection.d.ts +0 -70
- package/dist/types/store/reconcile.d.ts +0 -46
- package/dist/types-cjs/store/optimistic.d.cts +0 -45
- package/dist/types-cjs/store/projection.d.cts +0 -70
- package/dist/types-cjs/store/reconcile.d.cts +0 -46
package/dist/prod/core/core.js
CHANGED
|
@@ -4,7 +4,7 @@ import { EFFECT_TRACKED, REACTIVE_OPTIMISTIC_DIRTY, CONFIG_OPTIMISTIC, NOT_PENDI
|
|
|
4
4
|
|
|
5
5
|
import { NotReadyError } from "./error.js";
|
|
6
6
|
|
|
7
|
-
import { trimStaleDeps, link,
|
|
7
|
+
import { trimStaleDeps, link, dormantNodes } from "./graph.js";
|
|
8
8
|
|
|
9
9
|
import { deleteFromHeap, queueFor, insertIntoHeapHeight, enqueueSub, markNode, markHeap, insertIntoHeap } from "./heap.js";
|
|
10
10
|
|
|
@@ -49,7 +49,7 @@ let snapshotSources = null;
|
|
|
49
49
|
function ownerInSnapshotScope(e) {
|
|
50
50
|
while (e) {
|
|
51
51
|
if (e.He) return true;
|
|
52
|
-
e = e.
|
|
52
|
+
e = e.ke;
|
|
53
53
|
}
|
|
54
54
|
return false;
|
|
55
55
|
}
|
|
@@ -70,13 +70,13 @@ function releaseSnapshotScope(e) {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
function releaseSubtree(e) {
|
|
73
|
-
let t = e.
|
|
73
|
+
let t = e.ve;
|
|
74
74
|
while (t) {
|
|
75
75
|
if (t.He) {
|
|
76
76
|
t = t.Ve;
|
|
77
77
|
continue;
|
|
78
78
|
}
|
|
79
|
-
if (t.
|
|
79
|
+
if (t.oe) {
|
|
80
80
|
const e = t;
|
|
81
81
|
e.T &= ~CONFIG_IN_SNAPSHOT_SCOPE;
|
|
82
82
|
if (e.ie & REACTIVE_SNAPSHOT_STALE) {
|
|
@@ -114,13 +114,13 @@ function recompute(e, t = false) {
|
|
|
114
114
|
deleteFromHeap(e, queueFor(e));
|
|
115
115
|
if (e.o !== null) e.o.Ee = null;
|
|
116
116
|
// Tracked effects run after finalizePureQueue, so dispose immediately instead of deferring
|
|
117
|
-
if (e._e || n === EFFECT_TRACKED) disposeChildren(e); else if (e.
|
|
117
|
+
if (e._e || n === EFFECT_TRACKED) disposeChildren(e); else if (e.ve !== null || e.he !== null) {
|
|
118
118
|
markDisposal(e);
|
|
119
119
|
const t = ext(e);
|
|
120
120
|
t.We = e.he;
|
|
121
|
-
t.qe = e.
|
|
121
|
+
t.qe = e.ve;
|
|
122
122
|
e.he = null;
|
|
123
|
-
e.
|
|
123
|
+
e.ve = null;
|
|
124
124
|
e.Me = 0;
|
|
125
125
|
} else ;
|
|
126
126
|
}
|
|
@@ -148,8 +148,8 @@ function recompute(e, t = false) {
|
|
|
148
148
|
let c = e.Pe === NOT_PENDING ? e.be : e.Pe;
|
|
149
149
|
let _ = e.Le;
|
|
150
150
|
let f = false;
|
|
151
|
-
let
|
|
152
|
-
let
|
|
151
|
+
let N = tracking;
|
|
152
|
+
let E = currentOptimisticLane;
|
|
153
153
|
tracking = true;
|
|
154
154
|
// A computed's fn establishes its OWN dependencies, so it must never run
|
|
155
155
|
// inside a latest() read window: read() short-circuits through the
|
|
@@ -157,7 +157,7 @@ function recompute(e, t = false) {
|
|
|
157
157
|
// computed) inside latest(fn) came out permanently dependency-less (#2926).
|
|
158
158
|
// latestRead() already suspends the flag for its pull-recomputes; this
|
|
159
159
|
// covers creation-time computes and flushes that run inside the window.
|
|
160
|
-
const
|
|
160
|
+
const I = latestReadActive;
|
|
161
161
|
latestReadActive = false;
|
|
162
162
|
// Lane posture lives with the engine: OPTIMISTIC_DIRTY is only ever set by
|
|
163
163
|
// engine-driven paths, and _optimisticNodes is only pushed by
|
|
@@ -179,12 +179,12 @@ function recompute(e, t = false) {
|
|
|
179
179
|
currentOptimisticLane = t;
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
|
-
const
|
|
183
|
-
const
|
|
184
|
-
if (
|
|
182
|
+
const d = n && n !== EFFECT_USER;
|
|
183
|
+
const T = stale;
|
|
184
|
+
if (d) stale = true;
|
|
185
185
|
try {
|
|
186
186
|
if (!false && e.T & CONFIG_SYNC) {
|
|
187
|
-
c = e.
|
|
187
|
+
c = e.oe(c);
|
|
188
188
|
if (e.o !== null) e.o.Ee = null;
|
|
189
189
|
e.Ie = false;
|
|
190
190
|
} else {
|
|
@@ -194,7 +194,7 @@ function recompute(e, t = false) {
|
|
|
194
194
|
// clobber the fresh subscription, so we skip it and let the internally-registered
|
|
195
195
|
// iteration drive updates.
|
|
196
196
|
const t = e.o?.Ee;
|
|
197
|
-
const n = e.
|
|
197
|
+
const n = e.oe(c);
|
|
198
198
|
const i = typeof n === "object" && n !== null;
|
|
199
199
|
const u = e.o?.Ee !== t;
|
|
200
200
|
c = u || !i ? n : handleAsync(e, n);
|
|
@@ -232,16 +232,16 @@ function recompute(e, t = false) {
|
|
|
232
232
|
if (n && currentOptimisticLane) GlobalQueue.$e(e);
|
|
233
233
|
let i = false;
|
|
234
234
|
if (n) {
|
|
235
|
-
ext(e).
|
|
235
|
+
ext(e).fe = true;
|
|
236
236
|
if (GlobalQueue.Je !== null) i = GlobalQueue.Je(e, s);
|
|
237
237
|
}
|
|
238
238
|
notifyStatus(e, n ? STATUS_PENDING : STATUS_ERROR, t, undefined, n ? e.o?.Be : undefined);
|
|
239
|
-
if (i) GlobalQueue.
|
|
239
|
+
if (i) GlobalQueue.k(e);
|
|
240
240
|
}
|
|
241
241
|
} finally {
|
|
242
|
-
tracking =
|
|
243
|
-
latestReadActive =
|
|
244
|
-
if (
|
|
242
|
+
tracking = N;
|
|
243
|
+
latestReadActive = I;
|
|
244
|
+
if (d) stale = T;
|
|
245
245
|
// Consume the missed-wake latch (#3037, set by insertSubs): a dep write
|
|
246
246
|
// landed beneath this pass on a link it had already validated. The wipe
|
|
247
247
|
// below must not key off DIRTY/CHECK — the read-time pull protocol
|
|
@@ -316,8 +316,8 @@ function recompute(e, t = false) {
|
|
|
316
316
|
if (a) e.Ie = true;
|
|
317
317
|
// see the held branch above (#2990)
|
|
318
318
|
} else if (e.Le != _) {
|
|
319
|
-
for (let t = e.u; t !== null; t = t.
|
|
320
|
-
insertIntoHeapHeight(t.
|
|
319
|
+
for (let t = e.u; t !== null; t = t.ae) {
|
|
320
|
+
insertIntoHeapHeight(t.ce, queueFor(t.ce));
|
|
321
321
|
}
|
|
322
322
|
}
|
|
323
323
|
// Silent recovery: errored → unchanged value fires no notification, but
|
|
@@ -327,7 +327,7 @@ function recompute(e, t = false) {
|
|
|
327
327
|
// (el._x?._error re-set), so this only runs on a genuinely clean recovery.
|
|
328
328
|
if (o !== undefined && !r && !e.o?._) settleErroredDependents(e, o);
|
|
329
329
|
}
|
|
330
|
-
currentOptimisticLane =
|
|
330
|
+
currentOptimisticLane = E;
|
|
331
331
|
const S = e.Pe !== NOT_PENDING || e.o !== null && (e.o.qe !== null || e.o.We !== null) || (e.S & (STATUS_PENDING | STATUS_UNINITIALIZED)) !== 0;
|
|
332
332
|
// Override-covered holds (hasOverride) always queue: their commit belongs
|
|
333
333
|
// to their own transition's schedule (A18 re-rule) and is unobservable
|
|
@@ -357,7 +357,7 @@ function updateIfNecessary(e) {
|
|
|
357
357
|
for (let t = e.nt; t; t = t.it) {
|
|
358
358
|
const n = t.ut;
|
|
359
359
|
const i = n.lt || n;
|
|
360
|
-
if (i.
|
|
360
|
+
if (i.oe) {
|
|
361
361
|
updateIfNecessary(i);
|
|
362
362
|
}
|
|
363
363
|
if (e.ie & REACTIVE_DIRTY) {
|
|
@@ -385,7 +385,7 @@ function computed(e, t) {
|
|
|
385
385
|
C: context?.C ?? globalQueue,
|
|
386
386
|
we: context?.we ?? defaultContext,
|
|
387
387
|
Me: 0,
|
|
388
|
-
|
|
388
|
+
oe: e,
|
|
389
389
|
be: i ? t.loadingValue : undefined,
|
|
390
390
|
Le: 0,
|
|
391
391
|
ot: undefined,
|
|
@@ -395,10 +395,10 @@ function computed(e, t) {
|
|
|
395
395
|
Ze: 0,
|
|
396
396
|
u: null,
|
|
397
397
|
rt: null,
|
|
398
|
-
|
|
398
|
+
ke: context,
|
|
399
399
|
Ve: null,
|
|
400
400
|
ct: null,
|
|
401
|
-
|
|
401
|
+
ve: null,
|
|
402
402
|
ie: t?.lazy ? REACTIVE_LAZY : REACTIVE_NONE,
|
|
403
403
|
// A loadingValue node is born committed: commit #0 is already in _value.
|
|
404
404
|
S: i ? 0 : STATUS_UNINITIALIZED,
|
|
@@ -424,24 +424,24 @@ function computed(e, t) {
|
|
|
424
424
|
* Never call ext() just to store a field's default. */ function ext(e) {
|
|
425
425
|
return e.o ??= {
|
|
426
426
|
De: undefined,
|
|
427
|
-
|
|
427
|
+
Nt: undefined,
|
|
428
428
|
Be: undefined,
|
|
429
429
|
Ge: undefined,
|
|
430
430
|
ge: undefined,
|
|
431
|
-
|
|
431
|
+
Et: undefined,
|
|
432
432
|
t: 0,
|
|
433
433
|
Ee: null,
|
|
434
434
|
_: undefined,
|
|
435
|
-
|
|
435
|
+
fe: undefined,
|
|
436
436
|
le: undefined,
|
|
437
|
-
|
|
437
|
+
h: undefined,
|
|
438
438
|
pe: false,
|
|
439
|
-
|
|
439
|
+
i: null,
|
|
440
440
|
ft: undefined,
|
|
441
441
|
Qe: undefined,
|
|
442
442
|
We: null,
|
|
443
443
|
qe: null,
|
|
444
|
-
|
|
444
|
+
It: undefined
|
|
445
445
|
};
|
|
446
446
|
}
|
|
447
447
|
|
|
@@ -450,17 +450,17 @@ function computed(e, t) {
|
|
|
450
450
|
* so V8 sees the full hidden class shape at construction time. Effects always run in lazy
|
|
451
451
|
* mode (recompute is called explicitly by `effect()`), so we hardcode the lazy bits and skip
|
|
452
452
|
* the auto-dispose CONFIG bit (effect() previously cleared it post-construction).
|
|
453
|
-
*/ function createEffectNode(e, t, n, i, u
|
|
454
|
-
const
|
|
455
|
-
const
|
|
456
|
-
id: inheritId(
|
|
457
|
-
T: (
|
|
453
|
+
*/ function createEffectNode(e, t, n, i, u) {
|
|
454
|
+
const l = u?.transparent ?? false;
|
|
455
|
+
const o = {
|
|
456
|
+
id: inheritId(u, l, context),
|
|
457
|
+
T: (l ? CONFIG_TRANSPARENT : 0) | (u?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (u?.sync ? CONFIG_SYNC : 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
|
|
458
458
|
Ue: false,
|
|
459
459
|
he: null,
|
|
460
460
|
C: context?.C ?? globalQueue,
|
|
461
461
|
we: context?.we ?? defaultContext,
|
|
462
462
|
Me: 0,
|
|
463
|
-
|
|
463
|
+
oe: e,
|
|
464
464
|
be: undefined,
|
|
465
465
|
Le: 0,
|
|
466
466
|
ot: undefined,
|
|
@@ -470,10 +470,10 @@ function computed(e, t) {
|
|
|
470
470
|
Ze: 0,
|
|
471
471
|
u: null,
|
|
472
472
|
rt: null,
|
|
473
|
-
|
|
473
|
+
ke: context,
|
|
474
474
|
Ve: null,
|
|
475
475
|
ct: null,
|
|
476
|
-
|
|
476
|
+
ve: null,
|
|
477
477
|
ie: REACTIVE_LAZY,
|
|
478
478
|
S: STATUS_UNINITIALIZED,
|
|
479
479
|
Te: clock,
|
|
@@ -482,18 +482,43 @@ function computed(e, t) {
|
|
|
482
482
|
_t: -1,
|
|
483
483
|
Ie: false,
|
|
484
484
|
Xe: false,
|
|
485
|
-
|
|
486
|
-
|
|
485
|
+
dt: undefined,
|
|
486
|
+
Tt: t,
|
|
487
487
|
St: n,
|
|
488
488
|
At: undefined,
|
|
489
489
|
Re: i,
|
|
490
490
|
o: null
|
|
491
491
|
};
|
|
492
|
-
//
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
492
|
+
// Effects dispatch status through the SHARED notifier (statusNotifierOf,
|
|
493
|
+
// keyed off _type) — storing it per node forced a full NodeExtension
|
|
494
|
+
// allocation on EVERY effect at creation (an alloc + 19 field stores,
|
|
495
|
+
// +23% effect creation, caught by the creation benches). Only genuinely
|
|
496
|
+
// per-node channels (boundaries) live on _x.
|
|
497
|
+
if (u?.unobserved) ext(o).ft = u.unobserved;
|
|
498
|
+
setupComputedNode(o, lazyOptions);
|
|
499
|
+
return o;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* The shared status notifier for effect nodes, installed once by effect.ts
|
|
504
|
+
* at module evaluation (`this`-dispatched — one function serves every
|
|
505
|
+
* effect, so nodes never store it). Boundary computeds keep their own
|
|
506
|
+
* per-node channel on `_x._notifyStatus`, which takes precedence.
|
|
507
|
+
*/ let effectStatusNotify = null;
|
|
508
|
+
|
|
509
|
+
function setEffectStatusNotify(e) {
|
|
510
|
+
effectStatusNotify = e;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/** Resolve a node's status notifier: an own `_x` channel (boundaries) wins;
|
|
514
|
+
* effect nodes (`_type` — EFFECT_PURE is 0, and only effect literals carry
|
|
515
|
+
* the field) fall back to the shared notifier. Presence doubles as the
|
|
516
|
+
* "display consumer" membership test in the status walks, exactly as the
|
|
517
|
+
* per-node field did when every effect carried one. */ function statusNotifierOf(e) {
|
|
518
|
+
const t = e.o;
|
|
519
|
+
const n = t !== null && t !== undefined ? t.h : undefined;
|
|
520
|
+
if (n !== undefined) return n;
|
|
521
|
+
return e.Re ? effectStatusNotify ?? undefined : undefined;
|
|
497
522
|
}
|
|
498
523
|
|
|
499
524
|
const lazyOptions = {
|
|
@@ -504,13 +529,13 @@ function setupComputedNode(e, t) {
|
|
|
504
529
|
e.st = e;
|
|
505
530
|
const n = context?.Ct ? context.Ot : context;
|
|
506
531
|
if (context) {
|
|
507
|
-
const t = context.
|
|
532
|
+
const t = context.ve;
|
|
508
533
|
if (t === null) {
|
|
509
|
-
context.
|
|
534
|
+
context.ve = e;
|
|
510
535
|
} else {
|
|
511
536
|
e.Ve = t;
|
|
512
537
|
t.ct = e;
|
|
513
|
-
context.
|
|
538
|
+
context.ve = e;
|
|
514
539
|
}
|
|
515
540
|
}
|
|
516
541
|
if (n) e.Le = n.Le + 1;
|
|
@@ -534,7 +559,7 @@ function signal(e, t, n = null) {
|
|
|
534
559
|
rt: null,
|
|
535
560
|
Te: clock,
|
|
536
561
|
lt: n,
|
|
537
|
-
|
|
562
|
+
Se: n?.o?.i || null,
|
|
538
563
|
Pe: NOT_PENDING,
|
|
539
564
|
// Signal-literal diet (§12e): NO _time/_fn/_statusFlags slots. Stores
|
|
540
565
|
// materialize one signal per touched leaf, so signal bytes are store
|
|
@@ -547,7 +572,7 @@ function signal(e, t, n = null) {
|
|
|
547
572
|
};
|
|
548
573
|
if (t?.unobserved) ext(i).ft = t.unobserved;
|
|
549
574
|
if (n) {
|
|
550
|
-
ext(n).
|
|
575
|
+
ext(n).i = i;
|
|
551
576
|
n.T |= CONFIG_FW_CHILDREN;
|
|
552
577
|
}
|
|
553
578
|
if (snapshotCaptureActive && !(i.T & CONFIG_NO_SNAPSHOT) && !((n?.S ?? 0) & STATUS_PENDING)) {
|
|
@@ -644,7 +669,7 @@ function isEqual(e, t) {
|
|
|
644
669
|
* snapshot / transition / lane / dev-strictRead state all take the full
|
|
645
670
|
* resolution. Anything slow returns READ_SLOW; the caller then calls read().
|
|
646
671
|
*/ function readNodeFast(e) {
|
|
647
|
-
if (latestReadActive || pendingCheckActive || e.
|
|
672
|
+
if (latestReadActive || pendingCheckActive || e.oe || e.lt || e.o?.De !== undefined || e.o?.Qe !== undefined || activeTransition !== null || currentOptimisticLane !== null || snapshotCaptureActive || false) return READ_SLOW;
|
|
648
673
|
let t = context;
|
|
649
674
|
if (t?.Ct) t = t.Ot;
|
|
650
675
|
if (t && tracking) link(e, t);
|
|
@@ -671,17 +696,17 @@ function read(e) {
|
|
|
671
696
|
// recompute don't collect into the probe.
|
|
672
697
|
if (pendingCheckActive) {
|
|
673
698
|
GlobalQueue.Dt(e, t, u, i);
|
|
674
|
-
} else if (typeof n.
|
|
699
|
+
} else if (typeof n.oe === "function") {
|
|
675
700
|
prepareComputed(e, false);
|
|
676
701
|
}
|
|
677
|
-
if (!n.
|
|
702
|
+
if (!n.oe && u === e && e.o?.De === undefined && e.o?.Qe === undefined && activeTransition === null && currentOptimisticLane === null && !snapshotCaptureActive && true) {
|
|
678
703
|
if (t && tracking) link(e, t);
|
|
679
704
|
// Committed visibility for children-forbidden readers — see readNodeFast.
|
|
680
705
|
return !t || e.Pe === NOT_PENDING || t.T & CONFIG_CHILDREN_FORBIDDEN ? e.be : e.Pe;
|
|
681
706
|
}
|
|
682
707
|
if (t && tracking) {
|
|
683
708
|
link(e, t, pendingCheckActive);
|
|
684
|
-
if (u.
|
|
709
|
+
if (u.oe) {
|
|
685
710
|
const n = queueFor(e);
|
|
686
711
|
if (u.Le >= n.xe) {
|
|
687
712
|
markNode(t);
|
|
@@ -690,7 +715,7 @@ function read(e) {
|
|
|
690
715
|
}
|
|
691
716
|
const i = u.Le;
|
|
692
717
|
// parent check is shallow, might need to be recursive
|
|
693
|
-
if (i >= t.Le && e.
|
|
718
|
+
if (i >= t.Le && e.ke !== t) {
|
|
694
719
|
t.Le = i + 1;
|
|
695
720
|
}
|
|
696
721
|
}
|
|
@@ -725,7 +750,7 @@ function read(e) {
|
|
|
725
750
|
// firewall-backed reads follow the same rules (memo parity, #2897 ruling):
|
|
726
751
|
// an errored derive throws for every late reader instead of silently
|
|
727
752
|
// serving node values (the seed, or last-good data after a failed refetch).
|
|
728
|
-
if (u.
|
|
753
|
+
if (u.oe && u.S & STATUS_ERROR) {
|
|
729
754
|
// Only a genuine reactive re-read may retry an errored async source:
|
|
730
755
|
// - tracking: owned/tracked scope only (never events / `untrack` / effect side-effect phase)
|
|
731
756
|
// - !pendingCheckActive: an `isPending` probe observes the error, never refetches
|
|
@@ -768,8 +793,15 @@ function read(e) {
|
|
|
768
793
|
// Record that this isPending() probe observed the fresh pending value, so
|
|
769
794
|
// the probe doesn't pair "pending" with the new value (#2831).
|
|
770
795
|
if (pendingCheckActive) GlobalQueue.Ht(e, l);
|
|
771
|
-
if (!t && u === e && typeof n.
|
|
772
|
-
unobserved(
|
|
796
|
+
if (!t && u === e && typeof n.oe === "function" && e.T & CONFIG_AUTO_DISPOSE && !(u.S & STATUS_PENDING) && !e.u) {
|
|
797
|
+
// Deferred, not inline (#3078): an inline unobserved() here made untracked
|
|
798
|
+
// reads destructive — dispose on this read, full revival recompute on the
|
|
799
|
+
// next — so consecutive reads could answer differently with no write in
|
|
800
|
+
// between (the revival samples the ambient transition/lane context).
|
|
801
|
+
// The sweep at flush finalization re-validates and reclaims; schedule()
|
|
802
|
+
// guarantees that flush happens even if nothing else is queued.
|
|
803
|
+
dormantNodes.add(e);
|
|
804
|
+
schedule();
|
|
773
805
|
}
|
|
774
806
|
return l;
|
|
775
807
|
}
|
|
@@ -781,7 +813,7 @@ function setSignal(e, t) {
|
|
|
781
813
|
// _overrideValue slot (flagged by CONFIG_OPTIMISTIC — a masked read of the
|
|
782
814
|
// always-present config instead of a missing-property probe), and every
|
|
783
815
|
// module that installs one installs the engine first.
|
|
784
|
-
if (e.T & CONFIG_OPTIMISTIC && !projectionWriteActive) return GlobalQueue.
|
|
816
|
+
if (e.T & CONFIG_OPTIMISTIC && !projectionWriteActive) return GlobalQueue.kt(e, t);
|
|
785
817
|
const n = e.Pe === NOT_PENDING ? e.be : e.Pe;
|
|
786
818
|
if (typeof t === "function") t = t(n);
|
|
787
819
|
// Uninitialized check first: the first commit has no previous value, so the
|
|
@@ -799,7 +831,7 @@ function setSignal(e, t) {
|
|
|
799
831
|
e.T & CONFIG_HAS_COMPANIONS && GlobalQueue.Oe !== null && GlobalQueue.Oe(e, t);
|
|
800
832
|
// _time is a computed-only slot (§12e): writing it on a signal would fork
|
|
801
833
|
// the lean shape. Every read site is computed-typed.
|
|
802
|
-
if (e.
|
|
834
|
+
if (e.oe !== undefined) e.Te = clock;
|
|
803
835
|
// Staged-rewrite fast path (§12d): a re-write to a node whose subscribers
|
|
804
836
|
// were already walked — and where nothing has recomputed or linked since
|
|
805
837
|
// (epoch) — re-stages the value and stops. The walk is idempotent (subs
|
|
@@ -824,7 +856,7 @@ function setSignal(e, t) {
|
|
|
824
856
|
schedule();
|
|
825
857
|
}
|
|
826
858
|
e.ie = e.ie & -4 | REACTIVE_MANUAL_WRITE;
|
|
827
|
-
e.
|
|
859
|
+
e.vt = clock;
|
|
828
860
|
}
|
|
829
861
|
|
|
830
862
|
/**
|
|
@@ -903,7 +935,7 @@ function staleValues(e, t = true) {
|
|
|
903
935
|
if (!t) {
|
|
904
936
|
return;
|
|
905
937
|
}
|
|
906
|
-
if (typeof t.
|
|
938
|
+
if (typeof t.oe === "function" && !(t.ie & REACTIVE_DISPOSED)) {
|
|
907
939
|
if (t.ie & REACTIVE_MANUAL_WRITE) {
|
|
908
940
|
// A manual write in the CURRENT tick wins over the refresh (#2692).
|
|
909
941
|
// A mask stamped in an earlier tick only survives because a
|
|
@@ -911,7 +943,7 @@ function staleValues(e, t = true) {
|
|
|
911
943
|
// refresh is a later, explicit re-ask and lifts the mask — otherwise
|
|
912
944
|
// any setStore early in an action silently swallows every refresh()
|
|
913
945
|
// for the rest of the transaction (#3026).
|
|
914
|
-
if (t.
|
|
946
|
+
if (t.vt === clock) return;
|
|
915
947
|
t.ie &= ~REACTIVE_MANUAL_WRITE;
|
|
916
948
|
// No REASK below: the batch carries a manual value change, so the
|
|
917
949
|
// recompute is not a quiet re-ask of an unchanged question.
|
|
@@ -933,4 +965,4 @@ function staleValues(e, t = true) {
|
|
|
933
965
|
}
|
|
934
966
|
}
|
|
935
967
|
|
|
936
|
-
export { READ_SLOW, clearSnapshots, computed, context, createEffectNode, currentOptimisticLane, ext, isEqual, latestReadActive, markSnapshotScope, optimisticComputed, optimisticSignal, pendingCheckActive, prepareComputed, read, readNodeFast, recompute, refresh, releaseSnapshotScope, runWithOwner, setContextInternal, setLatestReadActive, setMemo, setPendingCheckActive, setSignal, setSnapshotCapture, signal, snapshotCaptureActive, snapshotSources, stale, staleValues, suppressComputedRecompute, tracking, untrack };
|
|
968
|
+
export { READ_SLOW, clearSnapshots, computed, context, createEffectNode, currentOptimisticLane, effectStatusNotify, ext, isEqual, latestReadActive, markSnapshotScope, optimisticComputed, optimisticSignal, pendingCheckActive, prepareComputed, read, readNodeFast, recompute, refresh, releaseSnapshotScope, runWithOwner, setContextInternal, setEffectStatusNotify, setLatestReadActive, setMemo, setPendingCheckActive, setSignal, setSnapshotCapture, signal, snapshotCaptureActive, snapshotSources, stale, staleValues, statusNotifierOf, suppressComputedRecompute, tracking, untrack };
|
package/dist/prod/core/effect.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { REACTIVE_DISPOSED, STATUS_ERROR, EFFECT_USER, CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, EFFECT_TRACKED,
|
|
1
|
+
import { REACTIVE_DISPOSED, STATUS_ERROR, EFFECT_USER, CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, EFFECT_TRACKED, EFFECT_RENDER, STATUS_PENDING } from "./constants.js";
|
|
2
2
|
|
|
3
|
-
import { ext, createEffectNode, recompute, computed, staleValues } from "./core.js";
|
|
3
|
+
import { setEffectStatusNotify, ext, createEffectNode, recompute, computed, staleValues } from "./core.js";
|
|
4
4
|
|
|
5
5
|
import { unwrapStatusError, StatusError } from "./error.js";
|
|
6
6
|
|
|
@@ -10,17 +10,17 @@ import { GlobalQueue, haltReactivity } from "./scheduler.js";
|
|
|
10
10
|
* Effects are the leaf nodes of our reactive graph. When their sources change, they are
|
|
11
11
|
* automatically added to the queue of effects to re-execute, which will cause them to fetch their
|
|
12
12
|
* sources and recompute
|
|
13
|
-
*/ function effect(t, e, E,
|
|
14
|
-
const r = !!
|
|
15
|
-
const
|
|
16
|
-
recompute(
|
|
17
|
-
!
|
|
13
|
+
*/ function effect(t, e, E, f) {
|
|
14
|
+
const r = !!f?.user;
|
|
15
|
+
const R = createEffectNode(t, e, E, r ? EFFECT_USER : EFFECT_RENDER, f);
|
|
16
|
+
recompute(R, true);
|
|
17
|
+
!f?.defer && (R.Re === EFFECT_USER || f?.schedule ? R.C.enqueue(R.Re, runEffect.bind(null, R)) : runEffect(R));
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
function notifyEffectStatus(t, e) {
|
|
21
21
|
// Use passed values if provided, otherwise read from node
|
|
22
22
|
const E = t !== undefined ? t : this.S;
|
|
23
|
-
const
|
|
23
|
+
const f = e !== undefined ? e : this.o?._;
|
|
24
24
|
if (E & STATUS_ERROR) {
|
|
25
25
|
this.C.notify(this, STATUS_PENDING, 0);
|
|
26
26
|
if (this.Re === EFFECT_USER) {
|
|
@@ -40,11 +40,11 @@ function notifyEffectStatus(t, e) {
|
|
|
40
40
|
return;
|
|
41
41
|
}
|
|
42
42
|
if (!this.C.notify(this, STATUS_ERROR, STATUS_ERROR)) {
|
|
43
|
-
haltReactivity(unwrapStatusError(
|
|
44
|
-
throw
|
|
43
|
+
haltReactivity(unwrapStatusError(f));
|
|
44
|
+
throw f;
|
|
45
45
|
}
|
|
46
46
|
} else if (this.Re === EFFECT_RENDER) {
|
|
47
|
-
this.C.notify(this, STATUS_PENDING | STATUS_ERROR, E,
|
|
47
|
+
this.C.notify(this, STATUS_PENDING | STATUS_ERROR, E, f);
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
|
|
@@ -62,7 +62,7 @@ function runEffect(t) {
|
|
|
62
62
|
// same flush must not be hijacked by a later-arriving error status.
|
|
63
63
|
if (t.S & STATUS_ERROR && t.Re === EFFECT_USER) {
|
|
64
64
|
const e = unwrapStatusError(t.o?._);
|
|
65
|
-
t.
|
|
65
|
+
t.dt = t.be;
|
|
66
66
|
t.Xe = false;
|
|
67
67
|
try {
|
|
68
68
|
t.St ? t.St(e, () => {
|
|
@@ -82,7 +82,7 @@ function runEffect(t) {
|
|
|
82
82
|
t.At = undefined;
|
|
83
83
|
try {
|
|
84
84
|
e?.();
|
|
85
|
-
const E = t.
|
|
85
|
+
const E = t.Tt(t.be, t.dt);
|
|
86
86
|
if (false && E !== undefined && typeof E !== "function") ;
|
|
87
87
|
// The final cleanup is invoked by disposeChildren at true disposal.
|
|
88
88
|
t.At = E;
|
|
@@ -94,7 +94,7 @@ function runEffect(t) {
|
|
|
94
94
|
throw e;
|
|
95
95
|
}
|
|
96
96
|
} finally {
|
|
97
|
-
t.
|
|
97
|
+
t.dt = t.be;
|
|
98
98
|
t.Xe = false;
|
|
99
99
|
}
|
|
100
100
|
}
|
|
@@ -117,8 +117,8 @@ GlobalQueue.tt = runEffect;
|
|
|
117
117
|
const e = E.At;
|
|
118
118
|
E.At = undefined;
|
|
119
119
|
e?.();
|
|
120
|
-
const
|
|
121
|
-
E.At =
|
|
120
|
+
const f = staleValues(t);
|
|
121
|
+
E.At = f;
|
|
122
122
|
}, {
|
|
123
123
|
...e,
|
|
124
124
|
lazy: true
|
|
@@ -127,19 +127,16 @@ GlobalQueue.tt = runEffect;
|
|
|
127
127
|
E.T = E.T & ~CONFIG_AUTO_DISPOSE | CONFIG_CHILDREN_FORBIDDEN;
|
|
128
128
|
E.Xe = true;
|
|
129
129
|
E.Re = EFFECT_TRACKED;
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const t = e !== undefined ? e : E.o?._;
|
|
135
|
-
if (!E.C.notify(E, STATUS_ERROR, STATUS_ERROR)) {
|
|
136
|
-
haltReactivity(unwrapStatusError(t));
|
|
137
|
-
throw t;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
};
|
|
141
|
-
E.Ut = run;
|
|
130
|
+
// Status dispatch rides the SHARED notifier (statusNotifierOf keys off
|
|
131
|
+
// _type): its error arm is behavior-identical to the closure that used to
|
|
132
|
+
// live here, without the per-node NodeExtension allocation.
|
|
133
|
+
E.Ut = run;
|
|
142
134
|
E.C.enqueue(EFFECT_USER, run);
|
|
143
135
|
}
|
|
144
136
|
|
|
137
|
+
// Install the shared effect status notifier (statusNotifierOf serves it to
|
|
138
|
+
// every effect node) — module-scope: any bundle that creates effects
|
|
139
|
+
// evaluates this module.
|
|
140
|
+
setEffectStatusNotify(notifyEffectStatus);
|
|
141
|
+
|
|
145
142
|
export { effect, trackedEffect };
|
|
@@ -46,11 +46,11 @@ function wireExternalSource(e) {
|
|
|
46
46
|
equals: false,
|
|
47
47
|
ownedWrite: true
|
|
48
48
|
});
|
|
49
|
-
const r = externalSourceConfig.factory(e.
|
|
49
|
+
const r = externalSourceConfig.factory(e.oe, () => {
|
|
50
50
|
setSignal(n, undefined);
|
|
51
51
|
});
|
|
52
52
|
cleanup(() => r.dispose());
|
|
53
|
-
e.
|
|
53
|
+
e.oe = e => {
|
|
54
54
|
read(n);
|
|
55
55
|
return r.track(e);
|
|
56
56
|
};
|