@solidjs/signals 2.0.0-rc.16 → 2.0.0-rc.18
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-shared.js +1972 -1751
- package/dist/dev.attribution.js +16 -16
- package/dist/dev.js +672 -319
- package/dist/observe/boundaries.js +2 -2
- package/dist/observe/core/async.js +151 -116
- package/dist/observe/core/constants.js +3 -1
- package/dist/observe/core/context.js +2 -0
- package/dist/observe/core/core.js +51 -35
- package/dist/observe/core/effect.js +29 -30
- package/dist/observe/core/graph.js +13 -11
- package/dist/observe/core/lanes.js +25 -14
- package/dist/observe/core/owner.js +77 -51
- package/dist/observe/core/scheduler.js +202 -175
- package/dist/observe/core/verdict.js +5 -2
- package/dist/observe/deferred.js +16 -13
- package/dist/observe/from.js +248 -0
- package/dist/observe/index.js +2 -0
- package/dist/observe/map.js +2 -2
- package/dist/observe/signals.js +6 -1
- package/dist/observe/store/affects.js +13 -13
- package/dist/observe/store/optimistic.js +112 -71
- package/dist/observe/store/reconcile.js +137 -121
- package/dist/observe/store/store.js +612 -626
- package/dist/observe/store/target.js +1 -5
- package/dist/prod/boundaries.js +2 -2
- package/dist/prod/core/async.js +211 -176
- package/dist/prod/core/constants.js +3 -1
- package/dist/prod/core/context.js +2 -0
- package/dist/prod/core/core.js +63 -47
- package/dist/prod/core/effect.js +24 -25
- package/dist/prod/core/graph.js +12 -10
- package/dist/prod/core/heap.js +1 -1
- package/dist/prod/core/lanes.js +39 -28
- package/dist/prod/core/owner.js +70 -44
- package/dist/prod/core/scheduler.js +78 -51
- package/dist/prod/core/truth.js +4 -4
- package/dist/prod/core/verdict.js +11 -8
- package/dist/prod/deferred.js +17 -14
- package/dist/prod/from.js +248 -0
- package/dist/prod/index.js +2 -0
- package/dist/prod/map.js +2 -2
- package/dist/prod/signals.js +18 -13
- package/dist/prod/store/optimistic.js +112 -71
- package/dist/prod/store/reconcile.js +137 -121
- package/dist/prod/store/store.js +109 -121
- package/dist/prod/store/target.js +13 -17
- package/dist/types/core/async.d.ts +12 -2
- package/dist/types/core/constants.d.ts +5 -0
- package/dist/types/core/core.d.ts +29 -6
- package/dist/types/core/dev.d.ts +9 -1
- package/dist/types/core/error.d.ts +9 -1
- package/dist/types/core/owner.d.ts +4 -0
- package/dist/types/core/scheduler.d.ts +38 -7
- package/dist/types/from.d.ts +47 -0
- package/dist/types/index.d.ts +1 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { notifyStatus, releaseFlightTeardown, handleAsync, clearStatus, parkLoadingWindow, stageError, settlePendingSource, releaseIfSettledUnobserved, settleErroredDependents } from "./async.js";
|
|
2
2
|
|
|
3
|
-
import { STATUS_UNINITIALIZED, REACTIVE_LAZY, REACTIVE_NONE, defaultContext, CONFIG_TRANSPARENT, CONFIG_OWNED_WRITE, CONFIG_AUTO_DISPOSE, CONFIG_SYNC, CONFIG_NO_SNAPSHOT, CONFIG_PLUMBING, CONFIG_IN_SNAPSHOT_SCOPE, NOT_PENDING, NO_SNAPSHOT, CONFIG_HAS_SNAPSHOT, CONFIG_HELD, REACTIVE_MANUAL_WRITE, STATUS_ERROR, REACTIVE_DIRTY, CONFIG_OVERRIDE, REACTIVE_DISPOSED, REACTIVE_CHECK, REACTIVE_IN_HEAP, REACTIVE_REASK, REACTIVE_SNAPSHOT_STALE, CONFIG_CHILDREN_FORBIDDEN, CONFIG_VERDICT, STATUS_PENDING, REACTIVE_LANE_DIRTY, CONFIG_GUESS, REACTIVE_RECOMPUTING_DEPS, CONFIG_LAGS, EFFECT_RENDER, CONFIG_HELD_FRAME, EFFECT_TRACKED, CONFIG_STAGED, CONFIG_STAGED_ERROR, REACTIVE_ZOMBIE, REACTIVE_MISSED_WAKE, REACTIVE_JOINED, REACTIVE_STAGED_READ, REACTIVE_PROBED, REACTIVE_FRAME_READ, REACTIVE_LANE_READ, REACTIVE_SCREEN_READ, REACTIVE_PROBE_UNANSWERED, CAUGHT_REARMED, CONFIG_PROMOTED, CONFIG_INPUTS_PUBLISHED, CONFIG_FRESH_READ, REACTIVE_IN_HEAP_HEIGHT, CONFIG_SLOT_NODE, EFFECT_USER, STATUS_NONE } from "./constants.js";
|
|
3
|
+
import { STATUS_UNINITIALIZED, REACTIVE_LAZY, REACTIVE_NONE, defaultContext, CONFIG_TRANSPARENT, CONFIG_OWNED_WRITE, CONFIG_AUTO_DISPOSE, CONFIG_SYNC, CONFIG_NO_SNAPSHOT, CONFIG_PLUMBING, CONFIG_IN_SNAPSHOT_SCOPE, NOT_PENDING, NO_SNAPSHOT, CONFIG_HAS_SNAPSHOT, CONFIG_HELD, REACTIVE_MANUAL_WRITE, STATUS_ERROR, REACTIVE_DIRTY, CONFIG_OVERRIDE, REACTIVE_DISPOSED, REACTIVE_CHECK, REACTIVE_IN_HEAP, REACTIVE_REASK, REACTIVE_SNAPSHOT_STALE, CONFIG_CHILDREN_FORBIDDEN, CONFIG_VERDICT, STATUS_PENDING, REACTIVE_LANE_DIRTY, CONFIG_GUESS, REACTIVE_RECOMPUTING_DEPS, CONFIG_LAGS, EFFECT_RENDER, CONFIG_HELD_FRAME, EFFECT_TRACKED, CONFIG_STAGED, CONFIG_STAGED_ERROR, REACTIVE_ZOMBIE, REACTIVE_MISSED_WAKE, REACTIVE_JOINED, REACTIVE_STAGED_READ, REACTIVE_PROBED, REACTIVE_FRAME_READ, REACTIVE_LANE_READ, REACTIVE_SCREEN_READ, REACTIVE_PROBE_UNANSWERED, CAUGHT_REARMED, CONFIG_PROMOTED, CONFIG_INPUTS_PUBLISHED, CONFIG_FRESH_READ, REACTIVE_IN_HEAP_HEIGHT, CONFIG_SLOT_NODE, EFFECT_USER, NewSet, STATUS_NONE } from "./constants.js";
|
|
4
4
|
|
|
5
5
|
import { NotReadyError } from "./error.js";
|
|
6
6
|
|
|
@@ -14,7 +14,7 @@ import { attrHooks } from "./attribution-hooks.js";
|
|
|
14
14
|
|
|
15
15
|
import { inheritId, linkChild, disposeChildren, markDisposal } from "./owner.js";
|
|
16
16
|
|
|
17
|
-
import { schedule, externalUntrackHook, clock, txOf, flushTransaction, laneWriteHook, joinFuture, queuePendingNode, globalQueue, notifyEpoch, insertSubs, dirtyQueue, observeFlightHook,
|
|
17
|
+
import { schedule, externalUntrackHook, clock, txOf, flushTransaction, laneWriteHook, joinFuture, queuePendingNode, globalQueue, notifyEpoch, insertSubs, dirtyQueue, observeFlightHook, laneStageHook, passLane, laneReadHook, wireExternalSourceHook, bumpNotifyEpoch, setPassLane, joinPassTx, freshHook, GlobalQueue, laneOutcomeHook, passTx, holdNode, laneDirty, heldTrim, staleReader, laneSeamsHook, stagedReaders, deferZombie } from "./scheduler.js";
|
|
18
18
|
|
|
19
19
|
// The heap's per-node step. A tracked effect's heap visit is its compute
|
|
20
20
|
// phase — empty, like a user effect whose compute reads nothing — and hands
|
|
@@ -82,7 +82,7 @@ function ownerInSnapshotScope(e) {
|
|
|
82
82
|
|
|
83
83
|
function setSnapshotCapture(e) {
|
|
84
84
|
snapshotCaptureActive = e;
|
|
85
|
-
if (e && !snapshotSources) snapshotSources = new
|
|
85
|
+
if (e && !snapshotSources) snapshotSources = new NewSet;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
function markSnapshotScope(e) {
|
|
@@ -222,7 +222,9 @@ function recompute(e, t = false) {
|
|
|
222
222
|
// and own nothing to park.
|
|
223
223
|
if (n === EFFECT_TRACKED || e.l & CONFIG_STAGED) {
|
|
224
224
|
// A lazy node only those children read is released when this pass
|
|
225
|
-
// ends, if the pass doesn't read it again (#4142).
|
|
225
|
+
// ends, if the pass doesn't read it again (#4142). A cleanup or
|
|
226
|
+
// `unobserved` hook that throws here neither stops the pass nor skips
|
|
227
|
+
// that drain: its error is rethrown when the flush ends (owner.ts).
|
|
226
228
|
const t = deferredReleases;
|
|
227
229
|
deferReleases(o = []);
|
|
228
230
|
disposeChildren(e);
|
|
@@ -240,15 +242,15 @@ function recompute(e, t = false) {
|
|
|
240
242
|
// A conditional can drop its pending source and recover to an unchanged
|
|
241
243
|
// value, leaving blocked dependents outside that source’s settle walk.
|
|
242
244
|
const u = e.m & STATUS_ERROR ? e.T?.F : undefined;
|
|
243
|
-
const
|
|
244
|
-
const
|
|
245
|
+
const a = (e.m & STATUS_PENDING) !== 0;
|
|
246
|
+
const _ = a ? e.T?.se : undefined;
|
|
245
247
|
// Pending SOURCE-hood, captured before the compute clears status: a node
|
|
246
248
|
// whose own flight parked dependents self-registers in _pendingSources
|
|
247
249
|
// (notifyStatus, isSource). If this recompute supersedes that flight and
|
|
248
250
|
// settles synchronously, those dependents settle HERE — asyncWrite's
|
|
249
251
|
// settlePendingSource walk never runs for a landing that was preempted
|
|
250
252
|
// (#3181).
|
|
251
|
-
const I = e.T?.
|
|
253
|
+
const I = e.T?.se?.has(e);
|
|
252
254
|
// D-41: this pass's error is staged, not published (`stageError`).
|
|
253
255
|
let N = false;
|
|
254
256
|
// A19 exc. 2: a `refresh()` re-asks the question already answered — quiet
|
|
@@ -273,7 +275,7 @@ function recompute(e, t = false) {
|
|
|
273
275
|
// L2: did this pass read the future (REACTIVE_JOINED, set by read) — or,
|
|
274
276
|
// a lane's mount, a staging of this flush (#3851, #4166)?
|
|
275
277
|
let R = false;
|
|
276
|
-
let
|
|
278
|
+
let C = tracking;
|
|
277
279
|
tracking = true;
|
|
278
280
|
try {
|
|
279
281
|
if (!false && e.l & CONFIG_SYNC) {
|
|
@@ -317,7 +319,7 @@ function recompute(e, t = false) {
|
|
|
317
319
|
// D-41: a pass of a held node, or one that read a staging of this
|
|
318
320
|
// flush, stages its error (`stageError`).
|
|
319
321
|
if (n || !(N = stageError(e, t, true))) {
|
|
320
|
-
if (n) ext(e).
|
|
322
|
+
if (n) ext(e).fe = true;
|
|
321
323
|
notifyStatus(e, n ? STATUS_PENDING : STATUS_ERROR, t);
|
|
322
324
|
// The replacement source is fully propagated now. If no new flight
|
|
323
325
|
// re-owned self, retire the superseded flight and its dependent copies.
|
|
@@ -332,10 +334,10 @@ function recompute(e, t = false) {
|
|
|
332
334
|
// re-park twin of the unchanged-value recovery sweep below; dependents
|
|
333
335
|
// with another path keep the source (retryReaches). A staged error
|
|
334
336
|
// answered what the pass carried: the same.
|
|
335
|
-
if ((n || N) &&
|
|
337
|
+
if ((n || N) && _) for (const t of _) if (t !== e && !e.T?.se?.has(t)) settlePendingSource(e, t);
|
|
336
338
|
}
|
|
337
339
|
} finally {
|
|
338
|
-
tracking =
|
|
340
|
+
tracking = C;
|
|
339
341
|
if (laneStageHook) laneRoot = E;
|
|
340
342
|
// Consume the missed-wake latch (#3037, set by insertSubs): a dep write
|
|
341
343
|
// landed beneath this pass on a link it had already validated. The wipe
|
|
@@ -343,7 +345,10 @@ function recompute(e, t = false) {
|
|
|
343
345
|
// (markNode(c) in read()) marks the running node as part of ordinary
|
|
344
346
|
// bookkeeping, and those marks are correctly discarded here.
|
|
345
347
|
A = (e.R & REACTIVE_MISSED_WAKE) !== 0;
|
|
346
|
-
|
|
348
|
+
// A mount's staged read joins it — except a nested derivation left
|
|
349
|
+
// pending: no render effect reaches it yet, so it holds nothing and
|
|
350
|
+
// reaches no boundary (Reader, #4203). A reader that does is caught.
|
|
351
|
+
R = !!(e.R & (l && (s || n || !(e.m & STATUS_PENDING)) ? REACTIVE_JOINED | REACTIVE_STAGED_READ : REACTIVE_JOINED));
|
|
347
352
|
// A verdict reader is one for as long as it probes, or reads a memo
|
|
348
353
|
// that does (`read`): a pass that did neither (REACTIVE_PROBED, the same
|
|
349
354
|
// bit) is an ordinary derivation again.
|
|
@@ -386,12 +391,12 @@ function recompute(e, t = false) {
|
|
|
386
391
|
// seat that read none of the lane's world has left it: its result is the
|
|
387
392
|
// frame's (a derivation whose branch no longer reaches the guess). A
|
|
388
393
|
// guess is written, not derived — it never leaves this way.
|
|
389
|
-
let
|
|
394
|
+
let d = !laneStageHook ? null : passLane;
|
|
390
395
|
// Listed before its staging, a pending pass included (the lane's own
|
|
391
396
|
// flight is the lane's); false: the pass left the lane (lanes.ts).
|
|
392
397
|
const O = !!e.T?.F || N;
|
|
393
|
-
if (laneStageHook &&
|
|
394
|
-
if (laneStageHook(e,
|
|
398
|
+
if (laneStageHook && d !== null) {
|
|
399
|
+
if (laneStageHook(e, d, t, O)) setPassLane(d); else d = null;
|
|
395
400
|
}
|
|
396
401
|
if (!O) {
|
|
397
402
|
// Observe-tier fan-in (HUGE_FAN_IN): the validated prefix [_deps.._depsTail]
|
|
@@ -440,7 +445,7 @@ function recompute(e, t = false) {
|
|
|
440
445
|
// Lane work's run is the lane's (released at its reveal) — a first
|
|
441
446
|
// pass under a lane too: `effect()` skips the synchronous first run of
|
|
442
447
|
// a lane's node.
|
|
443
|
-
if (!t ||
|
|
448
|
+
if (!t || d !== null) globalQueue.enqueue(n, e.W ??= GlobalQueue.K.bind(null, e), d);
|
|
444
449
|
}
|
|
445
450
|
// Lanes. A written guess's own pass from the frame: its source
|
|
446
451
|
// recomputed it — the truth (A18: "the source is whatever recomputes the
|
|
@@ -448,7 +453,7 @@ function recompute(e, t = false) {
|
|
|
448
453
|
// confirm is a landing too — the lane's end commits it) and notifies
|
|
449
454
|
// only a correction: an equal truth re-runs nothing (lanes.ts). (As the
|
|
450
455
|
// lane's own work the lane judged it — `laneStage`.)
|
|
451
|
-
if (!t &&
|
|
456
|
+
if (!t && d === null && e.l & CONFIG_GUESS && laneOutcomeHook(e, S, O)) ; else if (O) ; else if (s) {
|
|
452
457
|
// L2: a first pass is born held — staged, committed with the
|
|
453
458
|
// transaction, its effect's first run the landing's (A29; `effect()`
|
|
454
459
|
// skips the synchronous first run on a staged value) — when the flush
|
|
@@ -465,14 +470,14 @@ function recompute(e, t = false) {
|
|
|
465
470
|
// pass that runs before anything joins: the pass's input, not a verdict
|
|
466
471
|
// after it. An effect still carrying an uncommitted staged value
|
|
467
472
|
// re-stages: the commit applies the latest pass, not the born-held one.
|
|
468
|
-
if (
|
|
473
|
+
if (d !== null) {
|
|
469
474
|
// Lane work: the lane's value — into the slot while the lane has not
|
|
470
475
|
// revealed (its reveal shows it), as its staging once it has (the
|
|
471
476
|
// next reveal promotes it; the screen keeps the revealed value). An
|
|
472
477
|
// effect's value slot is private: the run the lane holds is what
|
|
473
478
|
// shows it — unless the effect was born held, whose first run is the
|
|
474
479
|
// commit's (A29): it re-stages, as below.
|
|
475
|
-
if (n && e.ue === NOT_PENDING) e.Ie = S; else if (n ||
|
|
480
|
+
if (n && e.ue === NOT_PENDING) e.Ie = S; else if (n || d.It) e.ue = S; else e.T.V = S;
|
|
476
481
|
} else if (t ? !((flushTransaction !== null || passTx !== null) && (R || creatorPass(T)?.R & REACTIVE_JOINED)) : n && e.ue === NOT_PENDING) {
|
|
477
482
|
// A first pass publishes directly. So does an effect: its value slot
|
|
478
483
|
// is private (only its own run reads it), and the staging round-trip
|
|
@@ -512,7 +517,7 @@ function recompute(e, t = false) {
|
|
|
512
517
|
if (e.o !== null) {
|
|
513
518
|
insertSubs(e);
|
|
514
519
|
// Lane work re-staged: the lane's members re-derive as its work.
|
|
515
|
-
if (
|
|
520
|
+
if (d !== null) laneDirty(e, d);
|
|
516
521
|
}
|
|
517
522
|
} else if (e.Et != c) {
|
|
518
523
|
for (let t = e.o; t !== null; t = t.t) insertIntoHeapHeight(t.u, dirtyQueue);
|
|
@@ -526,7 +531,7 @@ function recompute(e, t = false) {
|
|
|
526
531
|
if (u !== undefined) settleErroredDependents(e, u);
|
|
527
532
|
// Self-registration (this node's own superseded flight) is the #3181
|
|
528
533
|
// sweep's business below — retiring it here too would walk twice.
|
|
529
|
-
if (
|
|
534
|
+
if (_) for (const t of _) if (t !== e) settlePendingSource(e, t);
|
|
530
535
|
}
|
|
531
536
|
// #3181: a synchronous settle supersedes the old landing callback, so
|
|
532
537
|
// recompute owns its pending-source sweep. An uninitialized node without
|
|
@@ -541,7 +546,7 @@ function recompute(e, t = false) {
|
|
|
541
546
|
// hold's commit (D-12) — but the hold does not wait for it: its only
|
|
542
547
|
// readers are behind the fallback (`_hidden`; F-4, #3540). A boundary
|
|
543
548
|
// re-armed by `on` owns what it collects outright (D-14): not held.
|
|
544
|
-
if (t &&
|
|
549
|
+
if (t && d === null && R && e.m & STATUS_PENDING) {
|
|
545
550
|
const t = flushTransaction ?? passTx;
|
|
546
551
|
if (t && !t.V && freshHook?.(e) !== CAUGHT_REARMED) holdNode(e, t);
|
|
547
552
|
}
|
|
@@ -567,7 +572,7 @@ function recompute(e, t = false) {
|
|
|
567
572
|
}
|
|
568
573
|
// Observe: the posture the run executed under — lane work (optimistic:
|
|
569
574
|
// overlay, never waste), a transaction's pass (held), or plain.
|
|
570
|
-
if (attrHooks !== null) attrHooks.recomputeEnd(e, t, i,
|
|
575
|
+
if (attrHooks !== null) attrHooks.recomputeEnd(e, t, i, d !== null, flushTransaction !== null || passTx !== null || (e.l & CONFIG_HELD) !== 0, e.ue !== NOT_PENDING);
|
|
571
576
|
// A staged value, a parked frame (L2: the commit retires it), or status the
|
|
572
577
|
// commit sweep must settle (a pending or uninitialized pass), queues the
|
|
573
578
|
// node for this flush's commit. A first pass queues only when pending or
|
|
@@ -576,7 +581,7 @@ function recompute(e, t = false) {
|
|
|
576
581
|
if (e.ue !== NOT_PENDING || e.T !== null && (e.T.Nt !== null || e.T.ft !== null) || (e.m & (STATUS_PENDING | STATUS_UNINITIALIZED)) !== 0 && (!t || (e.m & STATUS_PENDING) !== 0)) {
|
|
577
582
|
e.l |= CONFIG_STAGED;
|
|
578
583
|
// Lane work is the lane's to reveal (its seam), not this flush's commit.
|
|
579
|
-
if (
|
|
584
|
+
if (d === null) queuePendingNode(e);
|
|
580
585
|
}
|
|
581
586
|
setPassLane(s);
|
|
582
587
|
// Missed-wake reschedule (see the finally above): values this pass read
|
|
@@ -656,10 +661,10 @@ function computed(e, t) {
|
|
|
656
661
|
_: null,
|
|
657
662
|
Ne: 0,
|
|
658
663
|
o: null,
|
|
659
|
-
|
|
664
|
+
Ct: null,
|
|
660
665
|
_parent: context,
|
|
661
666
|
Xe: null,
|
|
662
|
-
|
|
667
|
+
dt: null,
|
|
663
668
|
ze: null,
|
|
664
669
|
R: t?.lazy ? REACTIVE_LAZY : REACTIVE_NONE,
|
|
665
670
|
m: o ? 0 : STATUS_UNINITIALIZED,
|
|
@@ -689,8 +694,8 @@ function computed(e, t) {
|
|
|
689
694
|
Se: null,
|
|
690
695
|
ae: null,
|
|
691
696
|
F: undefined,
|
|
692
|
-
se: undefined,
|
|
693
697
|
fe: undefined,
|
|
698
|
+
se: undefined,
|
|
694
699
|
Dt: undefined,
|
|
695
700
|
lt: undefined,
|
|
696
701
|
Nt: null,
|
|
@@ -726,6 +731,12 @@ function computed(e, t) {
|
|
|
726
731
|
if (t.m & STATUS_UNINITIALIZED || e.i !== EFFECT_RENDER) joinPassTx(txOf(t), !e.i && e.m & STATUS_UNINITIALIZED || freshHook?.(e));
|
|
727
732
|
}
|
|
728
733
|
|
|
734
|
+
/** `joinPass` into the hold `t` (a folded guess's: store/optimistic.ts
|
|
735
|
+
* `foldedRead`, D-34) — a reader is never a render effect there. */ function joinPassAt(e, t) {
|
|
736
|
+
e.R |= REACTIVE_JOINED;
|
|
737
|
+
joinPassTx(t, !e.i && e.m & STATUS_UNINITIALIZED || freshHook?.(e));
|
|
738
|
+
}
|
|
739
|
+
|
|
729
740
|
/** A15's stale reader (shared-hole and reveal corollaries): a render effect
|
|
730
741
|
* reading a node held by a transaction that is not the flush's. It is served
|
|
731
742
|
* the committed value — a pending one's too — publishes mainline, and is
|
|
@@ -827,10 +838,10 @@ function computed(e, t) {
|
|
|
827
838
|
_: null,
|
|
828
839
|
Ne: 0,
|
|
829
840
|
o: null,
|
|
830
|
-
|
|
841
|
+
Ct: null,
|
|
831
842
|
_parent: context,
|
|
832
843
|
Xe: null,
|
|
833
|
-
|
|
844
|
+
dt: null,
|
|
834
845
|
ze: null,
|
|
835
846
|
R: REACTIVE_LAZY,
|
|
836
847
|
m: STATUS_UNINITIALIZED,
|
|
@@ -875,7 +886,7 @@ function setEffectStatusNotify(e) {
|
|
|
875
886
|
* 0, and only effect literals carry the field) get the shared notifier.
|
|
876
887
|
* Presence doubles as the "display consumer" membership test in the status
|
|
877
888
|
* walks, exactly as the per-node field did when every effect carried one. */ function statusNotifierOf(e) {
|
|
878
|
-
return e.i ? effectStatusNotify
|
|
889
|
+
return e.i ? effectStatusNotify : null;
|
|
879
890
|
}
|
|
880
891
|
|
|
881
892
|
const lazyOptions = {
|
|
@@ -908,7 +919,7 @@ function signal(e, t) {
|
|
|
908
919
|
l: (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (t?.le ? CONFIG_NO_SNAPSHOT : 0),
|
|
909
920
|
Ie: e,
|
|
910
921
|
o: null,
|
|
911
|
-
|
|
922
|
+
Ct: null,
|
|
912
923
|
ie: clock,
|
|
913
924
|
ue: NOT_PENDING,
|
|
914
925
|
Ot: -1,
|
|
@@ -946,7 +957,7 @@ function isEqual(e, t) {
|
|
|
946
957
|
l: CONFIG_OWNED_WRITE | CONFIG_SLOT_NODE,
|
|
947
958
|
Ie: e,
|
|
948
959
|
o: null,
|
|
949
|
-
|
|
960
|
+
Ct: null,
|
|
950
961
|
ie: clock,
|
|
951
962
|
ue: NOT_PENDING,
|
|
952
963
|
Ot: -1,
|
|
@@ -1197,7 +1208,9 @@ function read(e) {
|
|
|
1197
1208
|
// Committed visibility for untracked and children-forbidden readers.
|
|
1198
1209
|
if (!t || e.ue === NOT_PENDING || t.l & CONFIG_CHILDREN_FORBIDDEN) return e.Ie;
|
|
1199
1210
|
// (A plain signal is never uninitialized — the carve-out is `serve`'s.)
|
|
1200
|
-
|
|
1211
|
+
// Lane work over a folded lane (D-34), once the frame has a
|
|
1212
|
+
// transaction, reads the screen too.
|
|
1213
|
+
if ((observeFlightHook && t.l & CONFIG_VERDICT || laneStageHook && passLane?.folded) && stagedScreen(t)) return e.Ie;
|
|
1201
1214
|
stagedRead(t);
|
|
1202
1215
|
return e.ue;
|
|
1203
1216
|
}
|
|
@@ -1338,7 +1351,10 @@ function read(e) {
|
|
|
1338
1351
|
|
|
1339
1352
|
/** A pass read a staging of this flush: it derives from what the flush may
|
|
1340
1353
|
* yet hold (REACTIVE_STAGED_READ — the seam decides). Lane work too (plan sec. 28, a
|
|
1341
|
-
* lane sees the screen plus its own guesses)
|
|
1354
|
+
* lane sees the screen plus its own guesses) — a folded lane's only before
|
|
1355
|
+
* the frame has a transaction (after, it reads the screen like a verdict
|
|
1356
|
+
* reader, `stagedScreen` — a list pass never pairs a held length with the
|
|
1357
|
+
* guess's rows, D-34): the staging is the screen if
|
|
1342
1358
|
* the frame commits — one pass, the common case — and a held write if it
|
|
1343
1359
|
* parks, which the seam repairs (`stagedReaders`): the pass re-derives on
|
|
1344
1360
|
* the committed world and its lane's runs wait that round, so the held
|
|
@@ -1580,4 +1596,4 @@ function staleValues(e, t = true) {
|
|
|
1580
1596
|
}
|
|
1581
1597
|
}
|
|
1582
1598
|
|
|
1583
|
-
export { bornInto, captureWriteSnapshot, clearSnapshots, computed, context, createEffectNode, effectStatusNotify, ext, isEqual, isSame, joinPass, markLateLinker, markRefresh, markSnapshotScope, markUnflushedStaged, notePromotedWrite, prepareComputed, pullComputed, read, readerSeesCommitted, recompute, releaseSnapshotScope, resyncUnflushedCompanions, runWithOwner, serve, setEffectStatusNotify, setMemo, setSignal, setSnapshotCapture, setVerdict, setWriteHook, signal, slotSignal, snapshotCaptureActive, snapshotSources, spectating, stagedRead, stagedScreen, staleValues, stashFlushed, statusNotifierOf, tracking, unflushedStaged, unflushedValue, untrack, verdict, writeHook };
|
|
1599
|
+
export { bornInto, captureWriteSnapshot, clearSnapshots, computed, context, createEffectNode, effectStatusNotify, ext, isEqual, isSame, joinPass, joinPassAt, markLateLinker, markRefresh, markSnapshotScope, markUnflushedStaged, notePromotedWrite, prepareComputed, pullComputed, read, readerSeesCommitted, recompute, releaseSnapshotScope, resyncUnflushedCompanions, runWithOwner, serve, setEffectStatusNotify, setMemo, setSignal, setSnapshotCapture, setVerdict, setWriteHook, signal, slotSignal, snapshotCaptureActive, snapshotSources, spectating, stagedRead, stagedScreen, staleValues, stashFlushed, statusNotifierOf, tracking, unflushedStaged, unflushedValue, untrack, verdict, writeHook };
|
|
@@ -18,43 +18,48 @@ import { globalQueue, schedule, heldRunHook, haltReactivity, GlobalQueue } from
|
|
|
18
18
|
* Effects are the leaf nodes of our reactive graph. When their sources change, they are
|
|
19
19
|
* automatically added to the queue of effects to re-execute, which will cause them to fetch their
|
|
20
20
|
* sources and recompute
|
|
21
|
-
*/ function effect(t, e, o,
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
recompute(
|
|
21
|
+
*/ function effect(t, e, o, r) {
|
|
22
|
+
const u = !!r?.user;
|
|
23
|
+
const E = createEffectNode(t, e, o, u ? EFFECT_USER : EFFECT_RENDER, r);
|
|
24
|
+
recompute(E, true);
|
|
25
25
|
// A first pass that read a staged value was staged itself (recompute: born
|
|
26
26
|
// staged); the flush's commit replays this effect. Its first run is not this
|
|
27
27
|
// creation's (A29). A lane's node likewise: its run is the lane's, queued
|
|
28
28
|
// by the pass and released at the reveal (lanes.ts).
|
|
29
|
-
!
|
|
29
|
+
!r?.defer && E.ue === NOT_PENDING && !E.T?.Je?.V && (E.i === EFFECT_USER || r?.schedule ? globalQueue.enqueue(E.i, runEffect.bind(null, E)) : runEffect(E, E.i));
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
function notifyEffectStatus(
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
if (o & STATUS_ERROR) {
|
|
32
|
+
function notifyEffectStatus() {
|
|
33
|
+
const t = this.m;
|
|
34
|
+
const e = this.T?.F;
|
|
35
|
+
if (t & STATUS_ERROR) {
|
|
37
36
|
globalQueue.notify(this, STATUS_PENDING, 0);
|
|
38
37
|
if (this.i === EFFECT_USER) {
|
|
39
38
|
// The error handler is the error arm of the effect phase (#2840 ruling):
|
|
40
39
|
// queue it like the effect function. It runs in the same imperative,
|
|
41
40
|
// writable scope and throws escalate the same way. No payload is queued — the node already carries `_statusFlags`/`_error`,
|
|
42
41
|
// and the runner dispatches on them, so a recovery before the effect
|
|
43
|
-
// phase takes the success arm instead.
|
|
44
|
-
//
|
|
45
|
-
// re-propagates unblocked at commit.
|
|
42
|
+
// phase takes the success arm instead. (Re-read: the boundary
|
|
43
|
+
// notify above can run a redraw.)
|
|
46
44
|
if (this.m & STATUS_ERROR) {
|
|
47
45
|
this.Qe = true;
|
|
48
46
|
globalQueue.enqueue(this.i, this.W ??= runEffect.bind(null, this));
|
|
49
47
|
}
|
|
50
48
|
return;
|
|
51
49
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
50
|
+
// Dev locates the halt at the node that threw: the StatusError's
|
|
51
|
+
// source, upstream of this effect when a computed failed.
|
|
52
|
+
escalate(this, unwrapStatusError(e), e, this);
|
|
56
53
|
} else if (this.i === EFFECT_RENDER) {
|
|
57
|
-
globalQueue.notify(this, STATUS_PENDING | STATUS_ERROR,
|
|
54
|
+
globalQueue.notify(this, STATUS_PENDING | STATUS_ERROR, t, e);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** An effect's error no boundary takes halts the system (boundary-or-halt):
|
|
59
|
+
* halted with `cause`, `thrown` rethrown; dev locates it at `subject`. */ function escalate(t, e, o = e, r = t) {
|
|
60
|
+
if (!globalQueue.notify(t, STATUS_ERROR, STATUS_ERROR)) {
|
|
61
|
+
haltReactivity(e);
|
|
62
|
+
throw o;
|
|
58
63
|
}
|
|
59
64
|
}
|
|
60
65
|
|
|
@@ -90,10 +95,7 @@ function runEffect(t, e) {
|
|
|
90
95
|
e?.();
|
|
91
96
|
}) : console.error(e);
|
|
92
97
|
} catch (e) {
|
|
93
|
-
|
|
94
|
-
haltReactivity(e);
|
|
95
|
-
throw e;
|
|
96
|
-
}
|
|
98
|
+
escalate(t, e);
|
|
97
99
|
}
|
|
98
100
|
return;
|
|
99
101
|
}
|
|
@@ -105,10 +107,10 @@ function runEffect(t, e) {
|
|
|
105
107
|
// cascade an observer reports) and what times the callback (the `effect`
|
|
106
108
|
// record) — facts a production observer needs, not only a dev console.
|
|
107
109
|
if (attrHooks !== null) attrHooks.effectRunStart(t);
|
|
108
|
-
const
|
|
110
|
+
const r = t.kt;
|
|
109
111
|
t.kt = undefined;
|
|
110
112
|
try {
|
|
111
|
-
|
|
113
|
+
r?.();
|
|
112
114
|
const e = t.Ge(t.Ie, t._t);
|
|
113
115
|
if (false && e !== undefined && typeof e !== "function") ;
|
|
114
116
|
// The final cleanup is invoked by disposeChildren at true disposal.
|
|
@@ -116,10 +118,7 @@ function runEffect(t, e) {
|
|
|
116
118
|
} catch (e) {
|
|
117
119
|
ext(t).F = new StatusError(t, e);
|
|
118
120
|
t.m |= STATUS_ERROR;
|
|
119
|
-
|
|
120
|
-
haltReactivity(e);
|
|
121
|
-
throw e;
|
|
122
|
-
}
|
|
121
|
+
escalate(t, e);
|
|
123
122
|
} finally {
|
|
124
123
|
t._t = t.Ie;
|
|
125
124
|
t.Qe = false;
|
|
@@ -157,8 +156,8 @@ GlobalQueue.K = runEffect;
|
|
|
157
156
|
const e = o.kt;
|
|
158
157
|
o.kt = undefined;
|
|
159
158
|
e?.();
|
|
160
|
-
const
|
|
161
|
-
o.kt =
|
|
159
|
+
const r = staleValues(t);
|
|
160
|
+
o.kt = r;
|
|
162
161
|
}, {
|
|
163
162
|
...e,
|
|
164
163
|
lazy: true
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { REACTIVE_RECOMPUTING_DEPS, CONFIG_AUTO_DISPOSE, STATUS_PENDING, REACTIVE_DISPOSED, REACTIVE_ZOMBIE, CONFIG_SLOT_NODE } from "./constants.js";
|
|
1
|
+
import { NewSet, REACTIVE_RECOMPUTING_DEPS, CONFIG_AUTO_DISPOSE, STATUS_PENDING, REACTIVE_DISPOSED, REACTIVE_ZOMBIE, CONFIG_SLOT_NODE } from "./constants.js";
|
|
2
2
|
|
|
3
3
|
import { deleteFromHeap } from "./heap.js";
|
|
4
4
|
|
|
5
|
-
import { disposeChildren } from "./owner.js";
|
|
5
|
+
import { disposeChildren, runCleanup } from "./owner.js";
|
|
6
6
|
|
|
7
7
|
import { bumpNotifyEpoch, dirtyQueue } from "./scheduler.js";
|
|
8
8
|
|
|
@@ -21,13 +21,15 @@ function unlinkSubs(e) {
|
|
|
21
21
|
const l = e.G;
|
|
22
22
|
const o = e.t;
|
|
23
23
|
const s = e.en;
|
|
24
|
-
if (o !== null) o.en = s; else n.
|
|
24
|
+
if (o !== null) o.en = s; else n.Ct = s;
|
|
25
25
|
if (s !== null) s.t = o; else {
|
|
26
26
|
n.o = o;
|
|
27
27
|
if (o === null) {
|
|
28
28
|
// Store slot nodes dispatch to the ONE shared hook — no per-node
|
|
29
29
|
// unobserved closure, no NodeExtension to hold it.
|
|
30
|
-
|
|
30
|
+
// A user hook that throws is collected like a cleanup (owner.ts): the
|
|
31
|
+
// unlink and the teardown around it carry on.
|
|
32
|
+
if (n.l & CONFIG_SLOT_NODE) slotUnobservedHook(n); else if (n.T?.Dt) runCleanup(n.T.Dt);
|
|
31
33
|
// No more subscribers; only tear down if CONFIG_AUTO_DISPOSE is set.
|
|
32
34
|
// A pending node is exempt: its in-flight async work is an observer —
|
|
33
35
|
// tearing down would orphan the work and re-execute it on the next
|
|
@@ -105,7 +107,7 @@ function unobserved(e) {
|
|
|
105
107
|
* stays alive and serves its cache, uniform with observed memos) while
|
|
106
108
|
* reclamation still happens within one microtask — the enqueue site arms
|
|
107
109
|
* schedule(), so a flush is guaranteed even when no other work is queued.
|
|
108
|
-
*/ const dormantNodes = new
|
|
110
|
+
*/ const dormantNodes = new NewSet;
|
|
109
111
|
|
|
110
112
|
function sweepDormant() {
|
|
111
113
|
if (dormantNodes.size === 0) return;
|
|
@@ -141,18 +143,18 @@ function link(e, n) {
|
|
|
141
143
|
// [deps.._depsTail] prefix — the O(1) equivalent of scanning the dep list
|
|
142
144
|
// (the old alien-signals `isValidLink` walk, O(n²) when a computation
|
|
143
145
|
// re-reads earlier deps non-consecutively, e.g. store leaf reads).
|
|
144
|
-
const
|
|
145
|
-
if (
|
|
146
|
-
const
|
|
146
|
+
const r = e.Ct;
|
|
147
|
+
if (r !== null && r.u === n && (!s || r.Te === n.Ne)) return;
|
|
148
|
+
const t = n._ = e.Ct = {
|
|
147
149
|
S: e,
|
|
148
150
|
u: n,
|
|
149
151
|
G: o,
|
|
150
|
-
en:
|
|
152
|
+
en: r,
|
|
151
153
|
t: null,
|
|
152
154
|
Te: n.Ne
|
|
153
155
|
};
|
|
154
|
-
if (l !== null) l.G =
|
|
155
|
-
if (
|
|
156
|
+
if (l !== null) l.G = t; else n.C = t;
|
|
157
|
+
if (r !== null) r.t = t; else e.o = t;
|
|
156
158
|
// New subscriber edge: staged-rewrite skips (§12d) must not miss it.
|
|
157
159
|
bumpNotifyEpoch();
|
|
158
160
|
}
|
|
@@ -461,12 +461,21 @@ function supersede(e, n, s) {
|
|
|
461
461
|
* lane's latest. NOT_PENDING falls through: lane work's read of a pending
|
|
462
462
|
* member throws like any. */ function laneRead(e, n) {
|
|
463
463
|
const s = n.l & CONFIG_GUESS;
|
|
464
|
+
const t = txOf(n);
|
|
465
|
+
// A lane a correction folded under (D-34): store/optimistic.ts reads it.
|
|
466
|
+
// Before the authoritative arm: an `until` outside the lane reads the
|
|
467
|
+
// same truth there, and joins the hold it folded into — its posture over
|
|
468
|
+
// a foreign hold (D-31), which the truth read alone would not take.
|
|
469
|
+
const l = e !== null && t.folded;
|
|
470
|
+
if (l) {
|
|
471
|
+
const s = l(e, n);
|
|
472
|
+
if (s !== NOT_PENDING) return s;
|
|
473
|
+
}
|
|
464
474
|
if (e !== null && e.l & CONFIG_AUTHORITATIVE) return GlobalQueue.rn(n);
|
|
465
|
-
const
|
|
475
|
+
const u = n.m;
|
|
466
476
|
// An errored member's lane value predates its error; the error is the answer (#4106).
|
|
467
|
-
if (!s &&
|
|
477
|
+
if (!s && u & STATUS_ERROR) return NOT_PENDING;
|
|
468
478
|
if (e === null) return laneValueOf(n);
|
|
469
|
-
const l = txOf(n);
|
|
470
479
|
if (!tracking || e.l & CONFIG_CHILDREN_FORBIDDEN) {
|
|
471
480
|
// An untracked read inside lane work derives from the lane like a
|
|
472
481
|
// tracked one, minus the subscription — `untrack` is about
|
|
@@ -475,33 +484,33 @@ function supersede(e, n, s) {
|
|
|
475
484
|
// lanes read by one pass link, as for tracked reads). Outside lane
|
|
476
485
|
// work, the screen.
|
|
477
486
|
if (passLane === null || e.l & CONFIG_CHILDREN_FORBIDDEN) return display(n);
|
|
478
|
-
enterLane(
|
|
479
|
-
if (!s &&
|
|
487
|
+
enterLane(t, e);
|
|
488
|
+
if (!s && u & STATUS_PENDING) return NOT_PENDING;
|
|
480
489
|
return laneValueOf(n);
|
|
481
490
|
}
|
|
482
|
-
if (e.i === EFFECT_RENDER && (passLane === null || !sameLane(passLane,
|
|
491
|
+
if (e.i === EFFECT_RENDER && (passLane === null || !sameLane(passLane, t))) {
|
|
483
492
|
if (!s) {
|
|
484
493
|
// A flight whose value has never shown: nothing to show — the leaf
|
|
485
494
|
// waits on it, and its own frame holds (A15, #3334), not the lane's
|
|
486
495
|
// parent. The landing lands the frame: its own pass (the flush
|
|
487
496
|
// joined its transaction) reads the answer it waited on — the lane's
|
|
488
497
|
// staging, revealed at this seam.
|
|
489
|
-
if (
|
|
498
|
+
if (u & STATUS_PENDING && n.T.V === NOT_PENDING) return NOT_PENDING;
|
|
490
499
|
if (n.ue !== NOT_PENDING && e.l & CONFIG_HELD && flushTransaction !== null && resolveTx(flushTransaction) === txOf(e)) return n.ue;
|
|
491
500
|
}
|
|
492
|
-
if (
|
|
493
|
-
if (!s && !
|
|
501
|
+
if (t.ln || !s && u & STATUS_PENDING) {
|
|
502
|
+
if (!s && !t.It && u & STATUS_UNINITIALIZED) throw new NotReadyError(null);
|
|
494
503
|
// Re-derived at the reveal; once per pass (the pass may also be a stale
|
|
495
504
|
// reader of a transaction, REACTIVE_FRAME_READ — both reruns apply).
|
|
496
505
|
if (!(e.R & REACTIVE_SCREEN_READ)) {
|
|
497
506
|
e.R |= REACTIVE_FRAME_READ | REACTIVE_SCREEN_READ;
|
|
498
|
-
|
|
507
|
+
t.En.push(e);
|
|
499
508
|
}
|
|
500
509
|
return display(n);
|
|
501
510
|
}
|
|
502
511
|
}
|
|
503
|
-
enterLane(
|
|
504
|
-
if (!s &&
|
|
512
|
+
enterLane(t, e);
|
|
513
|
+
if (!s && u & STATUS_PENDING) return NOT_PENDING;
|
|
505
514
|
return laneValueOf(n);
|
|
506
515
|
}
|
|
507
516
|
|
|
@@ -626,10 +635,12 @@ function laneSeam(e, n) {
|
|
|
626
635
|
// question is the one being asked (`question`: a slice's, a derive's
|
|
627
636
|
// continuation's — the flight's); unstamped writes and mainline are
|
|
628
637
|
// current.
|
|
629
|
-
|
|
638
|
+
// A store re-bases a keyed arrangement guess over an older truth, and
|
|
639
|
+
// folds a current one beneath it (D-34): true.
|
|
640
|
+
const s = stale(e, question);
|
|
641
|
+
if (GlobalQueue.an?.(e, n, s) || s) {
|
|
630
642
|
e.ue = n;
|
|
631
643
|
e.l |= CONFIG_HELD;
|
|
632
|
-
GlobalQueue.an?.(e, n);
|
|
633
644
|
schedule();
|
|
634
645
|
return n;
|
|
635
646
|
}
|