@solidjs/signals 2.0.0-rc.17 → 2.0.0-rc.19
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 +426 -241
- package/dist/dev.attribution.js +19 -16
- package/dist/dev.js +768 -409
- package/dist/observe/boundaries.js +88 -67
- package/dist/observe/core/action.js +8 -0
- package/dist/observe/core/async.js +183 -138
- package/dist/observe/core/attribution.js +30 -28
- package/dist/observe/core/constants.js +23 -1
- package/dist/observe/core/core.js +185 -157
- package/dist/observe/core/effect.js +34 -37
- package/dist/observe/core/graph.js +8 -8
- package/dist/observe/core/heap.js +5 -5
- package/dist/observe/core/lanes.js +104 -84
- package/dist/observe/core/owner.js +17 -16
- package/dist/observe/core/scheduler.js +61 -47
- package/dist/observe/core/truth.js +6 -6
- package/dist/observe/core/verdict.js +15 -15
- package/dist/observe/deferred.js +40 -38
- package/dist/observe/from.js +248 -0
- package/dist/observe/index.js +4 -2
- package/dist/observe/map.js +22 -22
- package/dist/observe/reveal.js +5 -5
- package/dist/observe/signals.js +57 -15
- package/dist/observe/store/affects.js +1 -1
- package/dist/observe/store/hooks.js +3 -0
- package/dist/observe/store/optimistic.js +11 -11
- package/dist/observe/store/projection.js +34 -21
- package/dist/observe/store/reconcile.js +138 -122
- package/dist/observe/store/store.js +628 -641
- package/dist/observe/store/target.js +1 -5
- package/dist/observe/store/utils.js +7 -2
- package/dist/prod/boundaries.js +153 -132
- package/dist/prod/core/action.js +8 -0
- package/dist/prod/core/async.js +242 -197
- package/dist/prod/core/constants.js +16 -1
- package/dist/prod/core/core.js +166 -138
- package/dist/prod/core/effect.js +31 -34
- package/dist/prod/core/graph.js +5 -5
- package/dist/prod/core/heap.js +12 -12
- package/dist/prod/core/lanes.js +98 -78
- package/dist/prod/core/owner.js +15 -14
- package/dist/prod/core/scheduler.js +64 -50
- package/dist/prod/core/truth.js +6 -6
- package/dist/prod/core/verdict.js +17 -17
- package/dist/prod/deferred.js +40 -38
- package/dist/prod/from.js +248 -0
- package/dist/prod/index.js +4 -2
- package/dist/prod/map.js +14 -14
- package/dist/prod/reveal.js +8 -8
- package/dist/prod/signals.js +46 -4
- package/dist/prod/store/affects.js +1 -1
- package/dist/prod/store/hooks.js +3 -0
- package/dist/prod/store/optimistic.js +16 -16
- package/dist/prod/store/projection.js +22 -9
- package/dist/prod/store/reconcile.js +138 -122
- package/dist/prod/store/store.js +308 -331
- package/dist/prod/store/target.js +13 -17
- package/dist/prod/store/utils.js +2 -0
- package/dist/types/core/action.d.ts +8 -0
- package/dist/types/core/async.d.ts +12 -2
- package/dist/types/core/constants.d.ts +25 -0
- package/dist/types/core/core.d.ts +33 -4
- package/dist/types/core/dev.d.ts +1 -1
- package/dist/types/core/index.d.ts +1 -1
- package/dist/types/core/lanes.d.ts +5 -1
- package/dist/types/core/scheduler.d.ts +17 -3
- package/dist/types/from.d.ts +47 -0
- package/dist/types/index.d.ts +4 -1
- package/dist/types/signals.d.ts +37 -0
- package/dist/types/store/target.d.ts +2 -0
- package/package.json +1 -1
|
@@ -8,7 +8,7 @@ import { unobserved, clearDeps } from "./graph.js";
|
|
|
8
8
|
|
|
9
9
|
import { deleteFromHeap } from "./heap.js";
|
|
10
10
|
|
|
11
|
-
import { globalQueue, schedule, dirtyQueue } from "./scheduler.js";
|
|
11
|
+
import { globalQueue, pendingNodes, schedule, dirtyQueue } from "./scheduler.js";
|
|
12
12
|
|
|
13
13
|
// L2 (step 2): the parked frame. A pass parks the previous frame's children
|
|
14
14
|
// and `_disposal` list on `_x._pendingFirstChild` / `_x._pendingDisposal`
|
|
@@ -69,21 +69,22 @@ function disposeChildren(e, n = false, r) {
|
|
|
69
69
|
// cleanups never run and it stays subscribed, rerunning in a torn-down
|
|
70
70
|
// tree (#3561). Death only (`self`): a re-pass leaves the frame reacting
|
|
71
71
|
// until the commit.
|
|
72
|
-
if (n && !r && e.T !== null && (e.T.
|
|
72
|
+
if (n && !r && e.T !== null && (e.T.ft !== null || e.T.Nt !== null)) disposeChildren(e, false, true);
|
|
73
73
|
if (n) {
|
|
74
74
|
e.R = t | REACTIVE_DISPOSED;
|
|
75
75
|
if (e._parent === null && e.Pt) unregisterRoot(e);
|
|
76
76
|
}
|
|
77
77
|
if (n && e.X) {
|
|
78
|
-
if (e.T !== null) e.T.
|
|
78
|
+
if (e.T !== null) e.T.Ee = null;
|
|
79
79
|
// A death that can unblock the future (L2, `futureBlocked`): a held
|
|
80
80
|
// flight's — a transaction's or a lane's (its `_x._transaction`) — or a
|
|
81
81
|
// stale reader's (REACTIVE_FRAME_READ — a frame that derived from one).
|
|
82
|
+
// An idle pending queue would also pin it until an unrelated flush.
|
|
82
83
|
// Nothing else re-examines a dead node, so make sure a seam comes.
|
|
83
|
-
if (e.m & STATUS_PENDING && e.T?.Je != null || t & REACTIVE_FRAME_READ) schedule();
|
|
84
|
+
if (e.m & STATUS_PENDING && e.T?.Je != null || t & REACTIVE_FRAME_READ || pendingNodes.length) schedule();
|
|
84
85
|
}
|
|
85
|
-
let l = r ? e.T.
|
|
86
|
-
if (r) e.T.
|
|
86
|
+
let l = r ? e.T.ft : e.ze;
|
|
87
|
+
if (r) e.T.ft = null; else e.ze = null;
|
|
87
88
|
while (l) {
|
|
88
89
|
const e = l;
|
|
89
90
|
// Owner teardown is death regardless of the child's own lifecycle
|
|
@@ -107,7 +108,7 @@ function disposeChildren(e, n = false, r) {
|
|
|
107
108
|
// on the fresh head and survives. Pointing each drained child's prev at
|
|
108
109
|
// itself routes its later splice onto the detached chain, never the head,
|
|
109
110
|
// and keeps the dev owner-chain-head invariant honest for those children.
|
|
110
|
-
l.
|
|
111
|
+
l.dt = l;
|
|
111
112
|
disposeChildren(l, true);
|
|
112
113
|
// Read after, not before: a sibling this disposal made dormant spliced
|
|
113
114
|
// itself out of the detached chain, and a cleanup may then have linked
|
|
@@ -122,11 +123,11 @@ function disposeChildren(e, n = false, r) {
|
|
|
122
123
|
// node._nextSibling intact so outer walks that already advanced past us
|
|
123
124
|
// still reach later siblings.
|
|
124
125
|
if (n && !r && !(t & REACTIVE_ZOMBIE) && e._parent !== null && !(e._parent.R & REACTIVE_DISPOSED)) {
|
|
125
|
-
const n = e.
|
|
126
|
+
const n = e.dt;
|
|
126
127
|
const r = e.Xe;
|
|
127
128
|
if (n !== null) n.Xe = r; else e._parent.ze = r;
|
|
128
|
-
if (r !== null) r.
|
|
129
|
-
e.
|
|
129
|
+
if (r !== null) r.dt = n;
|
|
130
|
+
e.dt = null;
|
|
130
131
|
}
|
|
131
132
|
runDisposal(e, r);
|
|
132
133
|
// Final effect-returned cleanup fires at true disposal, after `_disposal`
|
|
@@ -140,9 +141,9 @@ function disposeChildren(e, n = false, r) {
|
|
|
140
141
|
|
|
141
142
|
function linkChild(e, n) {
|
|
142
143
|
const r = e.ze;
|
|
143
|
-
n.
|
|
144
|
+
n.dt = null;
|
|
144
145
|
n.Xe = r;
|
|
145
|
-
if (r !== null) r.
|
|
146
|
+
if (r !== null) r.dt = n;
|
|
146
147
|
e.ze = n;
|
|
147
148
|
}
|
|
148
149
|
|
|
@@ -153,9 +154,9 @@ function runDisposal(e, n) {
|
|
|
153
154
|
// walk ran every entry a second time. Detached, the re-entrant drain finds
|
|
154
155
|
// nothing. The same shape latched a throwing cleanup: the list survived the
|
|
155
156
|
// throw and every later drain re-ran and re-threw it.
|
|
156
|
-
const r = n ? e.T.
|
|
157
|
+
const r = n ? e.T.Nt : e.rt;
|
|
157
158
|
if (!r) return;
|
|
158
|
-
if (n) e.T.
|
|
159
|
+
if (n) e.T.Nt = null; else e.rt = null;
|
|
159
160
|
if (Array.isArray(r)) {
|
|
160
161
|
// Unwind order (#3572, restores 1.x #1562): later registrations run
|
|
161
162
|
// before earlier ones. Children have already been disposed by the caller,
|
|
@@ -308,10 +309,10 @@ function disposeRootSelf(e = true) {
|
|
|
308
309
|
// was 12 KB and most of disposal's time (2026-10-17).
|
|
309
310
|
R: REACTIVE_NONE,
|
|
310
311
|
Pt: true,
|
|
311
|
-
|
|
312
|
+
ht: n?.Pt ? n.ht : n,
|
|
312
313
|
ze: null,
|
|
313
314
|
Xe: null,
|
|
314
|
-
|
|
315
|
+
dt: null,
|
|
315
316
|
rt: null,
|
|
316
317
|
A: n?.A || defaultContext,
|
|
317
318
|
ct: 0,
|
|
@@ -130,7 +130,7 @@ function setExternalHooks(e, o) {
|
|
|
130
130
|
|
|
131
131
|
/** A transaction (listed here) or a lane (lanes.ts lists its own). */ function newTransaction(e, o = null) {
|
|
132
132
|
const n = {
|
|
133
|
-
|
|
133
|
+
Ye: [],
|
|
134
134
|
un: [ [], [] ],
|
|
135
135
|
En: [],
|
|
136
136
|
eo: null,
|
|
@@ -232,7 +232,7 @@ function resolveTx(e) {
|
|
|
232
232
|
e.eo = o;
|
|
233
233
|
o.tt += e.tt;
|
|
234
234
|
o.nt ||= e.nt;
|
|
235
|
-
append(o.
|
|
235
|
+
append(o.Ye, e.Ye);
|
|
236
236
|
append(o.un[0], e.un[0]);
|
|
237
237
|
append(o.un[1], e.un[1]);
|
|
238
238
|
append(o.En, e.En);
|
|
@@ -262,7 +262,7 @@ function resolveTx(e) {
|
|
|
262
262
|
judge ??= e;
|
|
263
263
|
// An action still running in it holds it open (action.ts). Its lanes
|
|
264
264
|
// (lanes.ts): a lane under `t` blocks it while blocked itself.
|
|
265
|
-
const n = e.tt !== 0 || blockedBy(e.
|
|
265
|
+
const n = e.tt !== 0 || blockedBy(e.Ye, e) || !!lanesBlockedHook?.(e);
|
|
266
266
|
judge = o;
|
|
267
267
|
return n;
|
|
268
268
|
}
|
|
@@ -273,8 +273,8 @@ let judge = null;
|
|
|
273
273
|
* frame reader observes (`blockedBy`'s predicate, collected; the readers
|
|
274
274
|
* themselves are not sources). */ function blockersOf(e) {
|
|
275
275
|
const o = [];
|
|
276
|
-
for (let n = 0; n < e.
|
|
277
|
-
const t = e.
|
|
276
|
+
for (let n = 0; n < e.Ye.length; n++) {
|
|
277
|
+
const t = e.Ye[n];
|
|
278
278
|
if (!t.i && t.m & STATUS_PENDING && !(t.R & REACTIVE_DISPOSED) && (t.T.Je === null || txOf(t) === e) && blockedBy([ t ], e)) o.push(t);
|
|
279
279
|
}
|
|
280
280
|
return o;
|
|
@@ -285,13 +285,13 @@ let judge = null;
|
|
|
285
285
|
* not (lanes.ts, the body-end corollary). A pending reader counts only if
|
|
286
286
|
* one of the sources it waits on is `t`'s (a render effect marked pending
|
|
287
287
|
* by a lane flight, never re-run, is the lane's wait, not the frame's). */ function ownFlights(e) {
|
|
288
|
-
return blockedBy(e.
|
|
288
|
+
return blockedBy(e.Ye, e, true);
|
|
289
289
|
}
|
|
290
290
|
|
|
291
291
|
/** A source the pending node waits on that is `t`'s (held by it, or by no
|
|
292
292
|
* one) — not another transaction's or lane's work. Itself, when it is the
|
|
293
293
|
* source. */ function ownSource(e, o) {
|
|
294
|
-
const n = e.T?.
|
|
294
|
+
const n = e.T?.se;
|
|
295
295
|
if (n === undefined) return true;
|
|
296
296
|
for (const e of n) {
|
|
297
297
|
const n = e.T?.Je;
|
|
@@ -323,7 +323,7 @@ function blockedBy(e, o, n = false) {
|
|
|
323
323
|
// lane-membership ruling, 2026-10-05). An output that never committed
|
|
324
324
|
// shows nothing — a mount the frame has not revealed — and holds
|
|
325
325
|
// nothing.
|
|
326
|
-
if ((n.i === EFFECT_RENDER && n.m & STATUS_PENDING || n.R & REACTIVE_FRAME_READ || n.l & CONFIG_REDERIVE && n.m === STATUS_PENDING) && (e.
|
|
326
|
+
if ((n.i === EFFECT_RENDER && n.m & STATUS_PENDING || n.R & REACTIVE_FRAME_READ || n.l & CONFIG_REDERIVE && n.m === STATUS_PENDING) && (e.Ie === n.Ne || n.T?.F != null) && !(n.R & REACTIVE_DISPOSED) && onScreen(n, judge ?? o)) return true;
|
|
327
327
|
}
|
|
328
328
|
}
|
|
329
329
|
return false;
|
|
@@ -385,7 +385,7 @@ function deferZombie(e) {
|
|
|
385
385
|
const n = ext(e);
|
|
386
386
|
if (n.Je === o) return;
|
|
387
387
|
n.Je = o;
|
|
388
|
-
o.
|
|
388
|
+
o.Ye.push(e);
|
|
389
389
|
}
|
|
390
390
|
|
|
391
391
|
/** Hold the (uncommitted) frame under a held pass: every computed in the
|
|
@@ -427,7 +427,7 @@ function deferZombie(e) {
|
|
|
427
427
|
const dirtyQueue = {
|
|
428
428
|
gt: new Array(2e3).fill(undefined),
|
|
429
429
|
p: false,
|
|
430
|
-
|
|
430
|
+
lt: 0,
|
|
431
431
|
jt: 0
|
|
432
432
|
};
|
|
433
433
|
|
|
@@ -509,12 +509,14 @@ function schedule() {
|
|
|
509
509
|
* hook machinery (pay-for-use).
|
|
510
510
|
*/ const ROOT_ERROR_HOOK = Symbol.for("solid-js/root-error-hook");
|
|
511
511
|
|
|
512
|
+
const HALTED = "[REACTIVITY_HALTED]";
|
|
513
|
+
|
|
512
514
|
function haltReactivity(e) {
|
|
513
515
|
if (halted) return;
|
|
514
516
|
halted = true;
|
|
515
517
|
// A cleanup error collected before the halt is dropped (owner.ts).
|
|
516
518
|
rethrowCleanupError(true);
|
|
517
|
-
let o =
|
|
519
|
+
let o = HALTED;
|
|
518
520
|
// Surface the cause here too: callers rethrow it, but a creation-time throw
|
|
519
521
|
// unwinds through ancestor recomputes that convert it to status instead of
|
|
520
522
|
// surfacing it (#2884), so the rethrow alone cannot guarantee visibility.
|
|
@@ -542,7 +544,7 @@ function haltReactivity(e) {
|
|
|
542
544
|
function notifyHalted() {
|
|
543
545
|
if (haltNotified) return;
|
|
544
546
|
haltNotified = true;
|
|
545
|
-
console.error(
|
|
547
|
+
console.error(HALTED);
|
|
546
548
|
}
|
|
547
549
|
|
|
548
550
|
/** @internal Test/dev-reload hook. Revives scheduling after a halt. */ function resetErrorHalt() {
|
|
@@ -573,7 +575,7 @@ class GlobalQueue {
|
|
|
573
575
|
clock++;
|
|
574
576
|
// A write the commit sweep staged with no subscriber to dirty is work
|
|
575
577
|
// too — the next round commits it.
|
|
576
|
-
scheduled = dirtyQueue.jt >= dirtyQueue.
|
|
578
|
+
scheduled = dirtyQueue.jt >= dirtyQueue.lt || pendingNodes.length !== 0;
|
|
577
579
|
this.run(EFFECT_RENDER);
|
|
578
580
|
this.run(EFFECT_USER);
|
|
579
581
|
if (true && holding) {
|
|
@@ -627,18 +629,18 @@ class GlobalQueue {
|
|
|
627
629
|
// stale stamp was the bridge that entangled unrelated updates
|
|
628
630
|
// with a never-resolving flight). Its observers are held and
|
|
629
631
|
// wait on its flight (`blocked`).
|
|
630
|
-
if (e.
|
|
632
|
+
if (e.ie === NOT_PENDING && e.m & STATUS_UNINITIALIZED && e.i !== EFFECT_RENDER) continue;
|
|
631
633
|
// A34 (2): a tick whose writes net to the committed value made no
|
|
632
634
|
// proposal (`setShow(false); setShow(true)` on a committed true) —
|
|
633
635
|
// not staged, not held, pends nothing; a later write to it is
|
|
634
636
|
// plain mainline. (A node already held stays held when written
|
|
635
637
|
// back: A34 (1).)
|
|
636
|
-
if (e.
|
|
638
|
+
if (e.ie !== NOT_PENDING && !(e.m & (STATUS_PENDING | STATUS_UNINITIALIZED)) && e.T?.ft == null && e.T?.Nt == null &&
|
|
637
639
|
// The comparator last: a pass that created children (an object
|
|
638
640
|
// owning a fresh async memo, #4111) must not have it read them.
|
|
639
641
|
// A NotReady read there is a change (`isSame`).
|
|
640
|
-
isSame(e, e.
|
|
641
|
-
e.
|
|
642
|
+
isSame(e, e.le, e.ie)) {
|
|
643
|
+
e.ie = NOT_PENDING;
|
|
642
644
|
e.l &= ~CONFIG_STAGED;
|
|
643
645
|
continue;
|
|
644
646
|
}
|
|
@@ -692,11 +694,12 @@ class GlobalQueue {
|
|
|
692
694
|
// This flush's own runs, the lanes' reveals, and the landings' held runs
|
|
693
695
|
// are collected apart and ordered below.
|
|
694
696
|
const n = this.un;
|
|
695
|
-
const t =
|
|
697
|
+
const t = [ [], [] ];
|
|
696
698
|
// The passes that read this frame's stagings as the screen read held
|
|
697
699
|
// writes if it parked: the lane seam re-derives them on the committed
|
|
698
700
|
// world (a lane's runs wait this round).
|
|
699
701
|
if (laneSeamsHook) {
|
|
702
|
+
this.un = t;
|
|
700
703
|
laneSeamsHook(o !== null ? stagedReaders : null);
|
|
701
704
|
if (stagedReaders.length) stagedReaders.length = 0;
|
|
702
705
|
}
|
|
@@ -819,37 +822,40 @@ class GlobalQueue {
|
|
|
819
822
|
* or without the held read) owes nothing. A run this transaction stashed
|
|
820
823
|
* for it is void (#3322: an effect has one value slot, and the stale pass
|
|
821
824
|
* overwrote the value that run was for) — the re-derivation's run replaces
|
|
822
|
-
* it. */ function reruns(e) {
|
|
823
|
-
|
|
824
|
-
|
|
825
|
+
* it. Truthy when an effect re-derives (`land`). */ function reruns(e) {
|
|
826
|
+
let o;
|
|
827
|
+
for (let n = 0; n < e.En.length; n++) {
|
|
828
|
+
const t = e.En[n];
|
|
825
829
|
// An errored reader's display is its error (D-39): the landing gives it
|
|
826
830
|
// nothing to re-derive, and a re-run would be a read that retries the
|
|
827
831
|
// errored source (#3945).
|
|
828
|
-
if (
|
|
829
|
-
if (
|
|
830
|
-
|
|
831
|
-
|
|
832
|
+
if (t.R & REACTIVE_DISPOSED || t.m & STATUS_ERROR) continue;
|
|
833
|
+
if (t.R & REACTIVE_FRAME_READ || t.l & CONFIG_VERDICT) {
|
|
834
|
+
o ||= t.i;
|
|
835
|
+
t.R &= ~REACTIVE_FRAME_READ;
|
|
836
|
+
t.Qe = false;
|
|
832
837
|
// Its frame is replaced by the re-derivation: a run one of its
|
|
833
838
|
// children queued this flush (re-run by the same landing) is for the
|
|
834
839
|
// frame being replaced — void too (#3404).
|
|
835
|
-
for (let e =
|
|
836
|
-
enqueueSub(
|
|
840
|
+
for (let e = t.ze; e !== null; e = e.Xe) if (e.i) e.Qe = false;
|
|
841
|
+
enqueueSub(t);
|
|
837
842
|
}
|
|
838
843
|
}
|
|
839
844
|
e.En.length = 0;
|
|
845
|
+
return o;
|
|
840
846
|
}
|
|
841
847
|
|
|
842
848
|
/** The landing: the held frame becomes the frame. */ function land(e) {
|
|
843
849
|
// Attribution hook: judged complete — its held writes commit next
|
|
844
850
|
// (`_nodes` still lists them).
|
|
845
851
|
if (attrHooks !== null) attrHooks.transitionSettled(e);
|
|
846
|
-
reruns(e);
|
|
847
|
-
if (e.I !== null) releaseMarksHook(e.I);
|
|
852
|
+
const o = reruns(e);
|
|
853
|
+
if (releaseMarksHook && e.I !== null) releaseMarksHook(e.I);
|
|
848
854
|
// Old children die in the commits (cleanups first), then the stashed
|
|
849
855
|
// effects run ahead of this flush's own. A node a lane took over since
|
|
850
856
|
// (a guess written over the staged truth) is the lane's to land.
|
|
851
|
-
for (let o = 0; o < e.
|
|
852
|
-
const n = e.
|
|
857
|
+
for (let o = 0; o < e.Ye.length; o++) {
|
|
858
|
+
const n = e.Ye[o];
|
|
853
859
|
if (n.T.Je === null || txOf(n) !== e) continue;
|
|
854
860
|
n.T.Je = null;
|
|
855
861
|
commitPendingNode(n);
|
|
@@ -858,7 +864,15 @@ class GlobalQueue {
|
|
|
858
864
|
// `u`, which has left `transactions`: the write is this landing's, and
|
|
859
865
|
// the next flush must not park in it (#4089).
|
|
860
866
|
if (flushTransaction === e) flushTransaction = null;
|
|
861
|
-
|
|
867
|
+
// An effect re-derives next round (`reruns`): the held runs wait for it
|
|
868
|
+
// (D-9, the A15 reveal corollary, #4071). Its re-derivation can discover a
|
|
869
|
+
// flight — a stale reader whose mainline pass stopped reading it, now
|
|
870
|
+
// reading the landed world — and a landing that showed its runs first
|
|
871
|
+
// would reveal the held write beside a frame that can only show its old
|
|
872
|
+
// value. Queued last in this effect phase, the release hands them to the
|
|
873
|
+
// next round's own runs, ahead of the re-derivation's: they show with it,
|
|
874
|
+
// or park with it in the hold its pending opens. The commits stand.
|
|
875
|
+
if (o) globalQueue.un[1].push(() => releaseQueues(e)); else releaseQueues(e);
|
|
862
876
|
}
|
|
863
877
|
|
|
864
878
|
/** The held runs join this flush's effect phase after its own runs (the
|
|
@@ -914,7 +928,7 @@ function insertSubs(e) {
|
|
|
914
928
|
// masked read of the always-present _config instead of missing-property
|
|
915
929
|
// lookups in the hottest notify loop.
|
|
916
930
|
const o = e.l;
|
|
917
|
-
const n = (o & CONFIG_HAS_SNAPSHOT) !== 0 && e.T?.
|
|
931
|
+
const n = (o & CONFIG_HAS_SNAPSHOT) !== 0 && e.T?.st !== undefined;
|
|
918
932
|
// Observe-tier fan-out: this walk visits every subscriber edge anyway, so
|
|
919
933
|
// the graph-size count is one local increment here and no field anywhere.
|
|
920
934
|
let t = 0;
|
|
@@ -929,7 +943,7 @@ function insertSubs(e) {
|
|
|
929
943
|
// pass either re-reads them fresh or trims them), and neither does the
|
|
930
944
|
// tail link: it is the read IN FLIGHT — read() links before it pulls, so
|
|
931
945
|
// this very commit is what that read returns.
|
|
932
|
-
if (e.R & REACTIVE_RECOMPUTING_DEPS && o.
|
|
946
|
+
if (e.R & REACTIVE_RECOMPUTING_DEPS && o.Ie === e.Ne && o !== e._) e.R |= REACTIVE_MISSED_WAKE;
|
|
933
947
|
if (n && e.l & CONFIG_IN_SNAPSHOT_SCOPE) {
|
|
934
948
|
e.R |= REACTIVE_SNAPSHOT_STALE;
|
|
935
949
|
continue;
|
|
@@ -966,32 +980,32 @@ function commitPendingNode(e) {
|
|
|
966
980
|
// commits a value.
|
|
967
981
|
if (!o.X) {
|
|
968
982
|
e.l &= -385;
|
|
969
|
-
if (e.
|
|
970
|
-
e.
|
|
971
|
-
e.
|
|
983
|
+
if (e.ie !== NOT_PENDING) {
|
|
984
|
+
e.le = e.ie;
|
|
985
|
+
e.ie = NOT_PENDING;
|
|
972
986
|
}
|
|
973
987
|
return;
|
|
974
988
|
}
|
|
975
989
|
// Computeds only from here (`_statusFlags` is not a signal field). A
|
|
976
990
|
// commit beneath a flight publishes its inputs (A15 reveal corollary,
|
|
977
991
|
// #3305): a reveal of it must observe, not show the pre-flight value.
|
|
978
|
-
const n = e.
|
|
992
|
+
const n = e.ie === NOT_PENDING && (o.m & STATUS_PENDING) !== 0;
|
|
979
993
|
const t = e.l & CONFIG_STAGED_ERROR;
|
|
980
994
|
// An answer commits: the error it replaces leaves the display (D-41).
|
|
981
|
-
if (!n && o.T) o.T.
|
|
995
|
+
if (!n && o.T) o.T.Te = undefined;
|
|
982
996
|
e.l = n ? e.l & -1048833 | CONFIG_INPUTS_PUBLISHED : e.l & -3147137;
|
|
983
997
|
// D-41: the staged answer is an error — published now, when a value would
|
|
984
998
|
// show, through the ordinary error path (`stageError`). Its readers first
|
|
985
999
|
// see it in the cycle after this seam: a tracked re-read there is not a
|
|
986
1000
|
// retry (`read`), or a boundary re-derived by it would retry it forever.
|
|
987
1001
|
if (t) {
|
|
988
|
-
const n = e.
|
|
989
|
-
e.
|
|
990
|
-
e.
|
|
1002
|
+
const n = e.ie;
|
|
1003
|
+
e.ie = NOT_PENDING;
|
|
1004
|
+
e.ae = clock + 1;
|
|
991
1005
|
notifyStatus(o, STATUS_ERROR, n);
|
|
992
|
-
} else if (e.
|
|
993
|
-
e.
|
|
994
|
-
e.
|
|
1006
|
+
} else if (e.ie !== NOT_PENDING) {
|
|
1007
|
+
e.le = e.ie;
|
|
1008
|
+
e.ie = NOT_PENDING;
|
|
995
1009
|
// A node born staged (recompute) initializes at this commit.
|
|
996
1010
|
o.m &= ~STATUS_UNINITIALIZED;
|
|
997
1011
|
// A quiet re-ask's classification survives its landing and dies with
|
|
@@ -1021,7 +1035,7 @@ function commitPendingNode(e) {
|
|
|
1021
1035
|
if (!(o.m & STATUS_PENDING)) o.m &= ~STATUS_UNINITIALIZED;
|
|
1022
1036
|
// L2: the children this commit publishes are the frame's now — the frame
|
|
1023
1037
|
// they replace, parked by the pass (`recompute`), goes.
|
|
1024
|
-
if (!n && o.T != null && (o.T.
|
|
1038
|
+
if (!n && o.T != null && (o.T.ft !== null || o.T.Nt !== null)) GlobalQueue.et(o, false, true);
|
|
1025
1039
|
}
|
|
1026
1040
|
|
|
1027
1041
|
function commitPendingNodes() {
|
|
@@ -1085,4 +1099,4 @@ function runQueue(e, o) {
|
|
|
1085
1099
|
for (let n = 0; n < e.length; n++) e[n](o);
|
|
1086
1100
|
}
|
|
1087
1101
|
|
|
1088
|
-
export { GlobalQueue, MAINLINE_QUESTION, ROOT_ERROR_HOOK, actionDepth, applyGuessesHook, blocked, blockersOf, boundarySeamHook, bumpNotifyEpoch, catchHook, clock, commitPendingNode, deferZombie, dirtyQueue, endLanesHook, enforceLoadingBoundary, enterAction, exitAction, externalUntrackHook, flush, flushTransaction, freshHook, globalQueue, haltReactivity, haltReactivityAt, heldRunHook, heldTrim, heldUnder, hiddenHook, holdNode, insertSubs, installBoundaryHooks, installLaneHooks, installMarkHooks, installStoreHooks, installVerdictHooks, joinFuture, joinPassTx, laneCorrectionsHook, laneDirty, laneOutcomeHook, laneReadHook, laneSeamsHook, laneStageHook, laneWriteHook, lanesBlockedHook, list, liveTx, merge, newTransaction, nextQuestion, notifyEpoch, observeFlightHook, ownFlights, passLane, passTx, question, queuePendingNode, releaseAmbientMarksHook, releaseMarksHook, releaseQueues, reruns, resetErrorHalt, resolveTx, sameLane, schedule, setExternalHooks, setPassLane, setQuestion, stagedReaders, staleReader, storeCommitHook, storeParkHook, txOf, unhold, wireExternalSourceHook };
|
|
1102
|
+
export { GlobalQueue, MAINLINE_QUESTION, ROOT_ERROR_HOOK, actionDepth, applyGuessesHook, blocked, blockersOf, boundarySeamHook, bumpNotifyEpoch, catchHook, clock, commitPendingNode, deferZombie, dirtyQueue, endLanesHook, enforceLoadingBoundary, enterAction, exitAction, externalUntrackHook, flush, flushTransaction, freshHook, globalQueue, haltReactivity, haltReactivityAt, heldRunHook, heldTrim, heldUnder, hiddenHook, holdNode, insertSubs, installBoundaryHooks, installLaneHooks, installMarkHooks, installStoreHooks, installVerdictHooks, joinFuture, joinPassTx, laneCorrectionsHook, laneDirty, laneOutcomeHook, laneReadHook, laneSeamsHook, laneStageHook, laneWriteHook, lanesBlockedHook, list, liveTx, merge, newTransaction, nextQuestion, notifyEpoch, observeFlightHook, ownFlights, passLane, passTx, pendingNodes, question, queuePendingNode, releaseAmbientMarksHook, releaseMarksHook, releaseQueues, reruns, resetErrorHalt, resolveTx, sameLane, schedule, setExternalHooks, setPassLane, setQuestion, stagedReaders, staleReader, storeCommitHook, storeParkHook, txOf, unhold, wireExternalSourceHook };
|
|
@@ -12,9 +12,9 @@ import { enqueueSub } from "./heap.js";
|
|
|
12
12
|
* staging a shown lane holds for its next reveal, else its slot; the
|
|
13
13
|
* committed value while it has none (its first lane pass in flight). */
|
|
14
14
|
function laneValueOf(r) {
|
|
15
|
-
if (!(r.l & CONFIG_GUESS) && r.
|
|
15
|
+
if (!(r.l & CONFIG_GUESS) && r.ie !== NOT_PENDING) return r.ie;
|
|
16
16
|
const t = r.T.V;
|
|
17
|
-
return t !== NOT_PENDING ? t : r.
|
|
17
|
+
return t !== NOT_PENDING ? t : r.le;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/** Truth readers (`until`, `refresh`; CONFIG_AUTHORITATIVE) — pay-for-use:
|
|
@@ -25,19 +25,19 @@ function laneValueOf(r) {
|
|
|
25
25
|
* value. */ function truthRead(r) {
|
|
26
26
|
if (truth(r)) return laneValueOf(r);
|
|
27
27
|
if (!(r.l & CONFIG_GUESS)) throw new NotReadyError(null);
|
|
28
|
-
return r.
|
|
28
|
+
return r.ie !== NOT_PENDING ? r.ie : r.le;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
/** A guess confirmed: its lane's truth readers re-read — of the lane's
|
|
32
32
|
* derivations too, which may be the truth now (#3943). */ function truthConfirmed(r) {
|
|
33
|
-
for (let t = 0; t < r.
|
|
33
|
+
for (let t = 0; t < r.Ye.length; t++) for (let e = r.Ye[t].o; e !== null; e = e.t) if (e.u.l & CONFIG_AUTHORITATIVE) enqueueSub(e.u);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
/** A lane node's value is the truth when every guess beneath it is
|
|
37
37
|
* confirmed — its source staged a value equal to the guess — and nothing
|
|
38
38
|
* beneath it is held in the parent: same inputs, same value. */ function truth(r) {
|
|
39
|
-
if (r.l & CONFIG_GUESS) return r.
|
|
40
|
-
if (r.
|
|
39
|
+
if (r.l & CONFIG_GUESS) return r.ie !== NOT_PENDING && r.je && r.je(r.T.V, r.ie);
|
|
40
|
+
if (r.ie !== NOT_PENDING) return;
|
|
41
41
|
if (r.l & CONFIG_OVERRIDE) for (let t = r.C; t; t = t.G) if (!truth(t.S)) return;
|
|
42
42
|
return 1;
|
|
43
43
|
}
|
|
@@ -139,8 +139,8 @@ let probeFound = false;
|
|
|
139
139
|
* when every flight it waits on is. */ function quietPending(e) {
|
|
140
140
|
const t = e.T;
|
|
141
141
|
if (t === null) return false;
|
|
142
|
-
if (t.
|
|
143
|
-
for (const e of t.
|
|
142
|
+
if (t.se !== undefined) {
|
|
143
|
+
for (const e of t.se) if (!e.T?.ut) return false;
|
|
144
144
|
return true;
|
|
145
145
|
}
|
|
146
146
|
return t.ut;
|
|
@@ -159,7 +159,7 @@ let probeFound = false;
|
|
|
159
159
|
* flight — a staging equal to what is displayed changes nothing when it
|
|
160
160
|
* lands: final now (maintainer, 2026-10-02). */ function heldNotFinal(e) {
|
|
161
161
|
if ((e.m & STATUS_PENDING) !== 0 && !quietPending(e)) return true;
|
|
162
|
-
return e.
|
|
162
|
+
return e.ie !== NOT_PENDING && !latestActive && !e.T.ut && !(e.m & STATUS_UNINITIALIZED) && (!!e.T.Te || !e.je || !e.je(e.le, e.ie));
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
/** `isPending`'s answer for an unheld node whose flight is up (cause ii). */ function pendingVerdict(e, t) {
|
|
@@ -177,7 +177,7 @@ let probeFound = false;
|
|
|
177
177
|
* a router's link claim probing inside the computation that creates the
|
|
178
178
|
* anchor). A throw still reaches the pass through `untrack`. */ function markVerdictReader(e) {
|
|
179
179
|
let t = context;
|
|
180
|
-
if (t?.Pt) t = t.
|
|
180
|
+
if (t?.Pt) t = t.ht;
|
|
181
181
|
if (t !== null && tracking) {
|
|
182
182
|
t.l |= CONFIG_VERDICT;
|
|
183
183
|
t.R |= REACTIVE_PROBED;
|
|
@@ -261,8 +261,8 @@ let probeFound = false;
|
|
|
261
261
|
}
|
|
262
262
|
|
|
263
263
|
/** The staging, or the staged error thrown (D-41). */ function staged(e) {
|
|
264
|
-
if (e.l & CONFIG_STAGED_ERROR) throw e.
|
|
265
|
-
return e.
|
|
264
|
+
if (e.l & CONFIG_STAGED_ERROR) throw e.ie;
|
|
265
|
+
return e.ie;
|
|
266
266
|
}
|
|
267
267
|
|
|
268
268
|
/** `latest()` of a held node: the flushed staging — the proposal — and, for
|
|
@@ -322,7 +322,7 @@ let probeFound = false;
|
|
|
322
322
|
if (i) verdictRead(t, r._parent ?? r);
|
|
323
323
|
const u = (n.m & STATUS_PENDING) !== 0 && !quietPending(n);
|
|
324
324
|
if (e.l & CONFIG_GUESS) {
|
|
325
|
-
if (probing && (u || e.
|
|
325
|
+
if (probing && (u || e.ie !== NOT_PENDING && (!e.je || !e.je(e.T.V, e.ie)))) probeFound = true;
|
|
326
326
|
// A lane a correction folded under (D-34): `latest()` shows the hold's
|
|
327
327
|
// world whole — the truth beneath each guess, else what it covered.
|
|
328
328
|
if (latestActive && r.folded) return r.folded(null, e);
|
|
@@ -342,28 +342,28 @@ let probeFound = false;
|
|
|
342
342
|
// `isPending` is answered below either way.
|
|
343
343
|
if (i && !probing && t.i === EFFECT_RENDER && passLane === null && r.it !== null && r.it.ln && flushTransaction !== r) {
|
|
344
344
|
staleReader(t, r.it);
|
|
345
|
-
return e.
|
|
345
|
+
return e.le;
|
|
346
346
|
}
|
|
347
347
|
if (i) verdictRead(t, r);
|
|
348
348
|
if (probing && heldNotFinal(n)) probeFound = true;
|
|
349
|
-
if (latestActive && e.
|
|
349
|
+
if (latestActive && e.ie !== NOT_PENDING) {
|
|
350
350
|
// An action body reading another transaction's proposal derives
|
|
351
351
|
// from it: the two settle as one (A15; posture C, 2026-09-15). An
|
|
352
352
|
// untracked read elsewhere has no transaction to entangle.
|
|
353
353
|
if (!i && flushTransaction !== null && !globalQueue.q) joinFuture(txOf(e));
|
|
354
354
|
return heldLatest(e, i ? t : null);
|
|
355
355
|
}
|
|
356
|
-
return e.
|
|
356
|
+
return e.le;
|
|
357
357
|
}
|
|
358
358
|
// Born into the future: its staging is its only value. It has no
|
|
359
359
|
// committed value, so no verdict: `isPending` reads it as its read does —
|
|
360
360
|
// the owner joins the hold (held with it, or caught by a boundary that
|
|
361
361
|
// has not shown content) and answers for the commit, where it is final.
|
|
362
|
-
if (e.
|
|
362
|
+
if (e.ie !== NOT_PENDING && !(n.m & STATUS_PENDING)) {
|
|
363
363
|
if (probing && r) joinPass(t, e);
|
|
364
364
|
return staged(e);
|
|
365
365
|
}
|
|
366
|
-
} else if (e.
|
|
366
|
+
} else if (e.ie !== NOT_PENDING) {
|
|
367
367
|
if (n.m & STATUS_PENDING && !u) pendingVerdict(i ? t : null, n); else if (i && globalQueue.q) provisionalVerdict(t, !latestActive && !u);
|
|
368
368
|
// Outside a flush, the staging is an unflushed write (A28): the probe
|
|
369
369
|
// answers for the flushed world, and watches the seam of the flush that
|
|
@@ -378,12 +378,12 @@ let probeFound = false;
|
|
|
378
378
|
if (globalQueue.q) return staged(e);
|
|
379
379
|
if (t !== null) markLateLinker(t);
|
|
380
380
|
}
|
|
381
|
-
return e.
|
|
381
|
+
return e.le;
|
|
382
382
|
}
|
|
383
383
|
if (n.m & STATUS_PENDING) {
|
|
384
384
|
if (!u) {
|
|
385
385
|
pendingVerdict(i ? t : null, n);
|
|
386
|
-
return e.
|
|
386
|
+
return e.le;
|
|
387
387
|
}
|
|
388
388
|
// As `read()`: an untracked read of a pending node still re-runs its
|
|
389
389
|
// reader when the node settles (a projection's pull reads its firewall
|
|
@@ -392,7 +392,7 @@ let probeFound = false;
|
|
|
392
392
|
throw n.T?.F;
|
|
393
393
|
}
|
|
394
394
|
if (n.X !== undefined && n.m & STATUS_ERROR) throw n.T.F;
|
|
395
|
-
return e.
|
|
395
|
+
return e.le;
|
|
396
396
|
}
|
|
397
397
|
|
|
398
398
|
// Installed at module evaluation — present exactly when something imports
|
package/dist/observe/deferred.js
CHANGED
|
@@ -123,7 +123,7 @@ GlobalQueue.er = e => lagging.size !== 0 ? lagOf(e, new Set) : undefined;
|
|
|
123
123
|
* does not replace (D-40) — restaged as the committed value, the hold's
|
|
124
124
|
* readers would re-derive against the old input. */ const serve = e => {
|
|
125
125
|
const r = handleAsync(t, e);
|
|
126
|
-
return t.l & CONFIG_HELD && t.
|
|
126
|
+
return t.l & CONFIG_HELD && t.ie !== NOT_PENDING ? t.ie : r;
|
|
127
127
|
};
|
|
128
128
|
const end = () => t.R & REACTIVE_DISPOSED && land(o);
|
|
129
129
|
return accessor(computed(r => {
|
|
@@ -135,49 +135,51 @@ GlobalQueue.er = e => lagging.size !== 0 ? lagOf(e, new Set) : undefined;
|
|
|
135
135
|
// Sticky, and read before this wrapper ever opens the window itself.
|
|
136
136
|
const n = l ||= !(t.m & STATUS_UNINITIALIZED) && !t.ce;
|
|
137
137
|
if (n) t.ce = true, t.l |= CONFIG_LAGS;
|
|
138
|
-
let c;
|
|
139
138
|
try {
|
|
140
|
-
|
|
139
|
+
const t = e(r);
|
|
140
|
+
if (n && typeof t === "object" && t !== null) {
|
|
141
|
+
if (isThenable(t)) {
|
|
142
|
+
ask(t);
|
|
143
|
+
const done = () => land(t, true);
|
|
144
|
+
t.then(done, done);
|
|
145
|
+
return serve(t);
|
|
146
|
+
}
|
|
147
|
+
const e = t[Symbol.asyncIterator];
|
|
148
|
+
if (e) {
|
|
149
|
+
// A stream's flight ends at its first yield (or its end).
|
|
150
|
+
const r = e.call(t);
|
|
151
|
+
let o = true;
|
|
152
|
+
const done = () => land(n, true);
|
|
153
|
+
const n = {
|
|
154
|
+
// Registered ahead of the engine's own reaction to the same
|
|
155
|
+
// promise, as for a plain flight: verdict and value in one flush.
|
|
156
|
+
next: e => {
|
|
157
|
+
const t = r.next(e);
|
|
158
|
+
if (o) {
|
|
159
|
+
o = false;
|
|
160
|
+
if (isThenable(t)) t.then(done, done); else done();
|
|
161
|
+
}
|
|
162
|
+
return t;
|
|
163
|
+
},
|
|
164
|
+
return: e => r.return?.(e) ?? Promise.resolve({
|
|
165
|
+
done: true,
|
|
166
|
+
value: e
|
|
167
|
+
})
|
|
168
|
+
};
|
|
169
|
+
ask(n);
|
|
170
|
+
return serve({
|
|
171
|
+
[Symbol.asyncIterator]: () => n
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
land(o);
|
|
176
|
+
return t;
|
|
141
177
|
} catch (e) {
|
|
142
178
|
// The wrapping form: an unready source parks the node in the window,
|
|
143
179
|
// and the wait is its flight until the source's settle re-runs it.
|
|
144
|
-
if (n && e instanceof NotReadyError) ask(e);
|
|
180
|
+
if (n && e instanceof NotReadyError) ask(e); else land(o);
|
|
145
181
|
throw e;
|
|
146
182
|
}
|
|
147
|
-
if (n && typeof c === "object" && c !== null) {
|
|
148
|
-
if (isThenable(c)) {
|
|
149
|
-
ask(c);
|
|
150
|
-
const done = () => land(c, true);
|
|
151
|
-
c.then(done, done);
|
|
152
|
-
return serve(c);
|
|
153
|
-
}
|
|
154
|
-
const e = c[Symbol.asyncIterator];
|
|
155
|
-
if (e) {
|
|
156
|
-
// A stream's flight ends at its first yield (or its end).
|
|
157
|
-
const r = e.call(c);
|
|
158
|
-
let t = true;
|
|
159
|
-
const done = () => land(o, true);
|
|
160
|
-
const o = {
|
|
161
|
-
// Registered ahead of the engine's own reaction to the same
|
|
162
|
-
// promise, as for a plain flight: verdict and value in one flush.
|
|
163
|
-
next: e => {
|
|
164
|
-
const o = r.next(e);
|
|
165
|
-
if (t) t = false, o.then(done, done);
|
|
166
|
-
return o;
|
|
167
|
-
},
|
|
168
|
-
return: e => r.return?.(e) ?? Promise.resolve({
|
|
169
|
-
done: true,
|
|
170
|
-
value: e
|
|
171
|
-
})
|
|
172
|
-
};
|
|
173
|
-
ask(o);
|
|
174
|
-
return serve({
|
|
175
|
-
[Symbol.asyncIterator]: () => o
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
land(o);
|
|
180
|
-
return c;
|
|
181
183
|
}, r));
|
|
182
184
|
}
|
|
183
185
|
|