@solidjs/signals 2.0.0-rc.18 → 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 +168 -88
- package/dist/dev.attribution.js +16 -13
- package/dist/dev.js +272 -129
- package/dist/observe/boundaries.js +88 -67
- package/dist/observe/core/action.js +8 -0
- package/dist/observe/core/async.js +46 -34
- package/dist/observe/core/attribution.js +30 -28
- package/dist/observe/core/constants.js +21 -1
- package/dist/observe/core/core.js +164 -136
- package/dist/observe/core/effect.js +6 -6
- package/dist/observe/core/graph.js +3 -3
- package/dist/observe/core/heap.js +5 -5
- package/dist/observe/core/lanes.js +104 -84
- package/dist/observe/core/owner.js +10 -9
- package/dist/observe/core/scheduler.js +38 -35
- package/dist/observe/core/truth.js +6 -6
- package/dist/observe/core/verdict.js +13 -13
- package/dist/observe/deferred.js +39 -40
- package/dist/observe/index.js +2 -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 +2 -2
- package/dist/observe/store/store.js +63 -53
- 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 +55 -43
- package/dist/prod/core/constants.js +14 -1
- package/dist/prod/core/core.js +162 -134
- package/dist/prod/core/effect.js +9 -9
- package/dist/prod/core/graph.js +3 -3
- 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 +43 -40
- package/dist/prod/core/truth.js +6 -6
- package/dist/prod/core/verdict.js +17 -17
- package/dist/prod/deferred.js +39 -40
- package/dist/prod/index.js +2 -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 +2 -2
- package/dist/prod/store/store.js +262 -262
- package/dist/prod/store/utils.js +2 -0
- package/dist/types/core/action.d.ts +8 -0
- package/dist/types/core/constants.d.ts +20 -0
- package/dist/types/core/core.d.ts +14 -1
- 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 +2 -0
- package/dist/types/index.d.ts +3 -1
- package/dist/types/signals.d.ts +37 -0
- package/dist/types/store/target.d.ts +2 -0
- package/package.json +1 -1
|
@@ -26,7 +26,7 @@ import { globalQueue, schedule, heldRunHook, haltReactivity, GlobalQueue } from
|
|
|
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
|
-
!r?.defer && E.
|
|
29
|
+
!r?.defer && E.ie === 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
32
|
function notifyEffectStatus() {
|
|
@@ -86,10 +86,10 @@ function runEffect(t, e) {
|
|
|
86
86
|
// same flush must not be hijacked by a later-arriving error status.
|
|
87
87
|
if (t.m & STATUS_ERROR && t.i === EFFECT_USER) {
|
|
88
88
|
const e = unwrapStatusError(t.T?.F);
|
|
89
|
-
t._t = t.
|
|
89
|
+
t._t = t.le;
|
|
90
90
|
t.Qe = false;
|
|
91
91
|
try {
|
|
92
|
-
t.
|
|
92
|
+
t.Ht ? t.Ht(e, () => {
|
|
93
93
|
const e = t.kt;
|
|
94
94
|
t.kt = undefined;
|
|
95
95
|
e?.();
|
|
@@ -111,7 +111,7 @@ function runEffect(t, e) {
|
|
|
111
111
|
t.kt = undefined;
|
|
112
112
|
try {
|
|
113
113
|
r?.();
|
|
114
|
-
const e = t.Ge(t.
|
|
114
|
+
const e = t.Ge(t.le, t._t);
|
|
115
115
|
if (false && e !== undefined && typeof e !== "function") ;
|
|
116
116
|
// The final cleanup is invoked by disposeChildren at true disposal.
|
|
117
117
|
t.kt = e;
|
|
@@ -120,7 +120,7 @@ function runEffect(t, e) {
|
|
|
120
120
|
t.m |= STATUS_ERROR;
|
|
121
121
|
escalate(t, e);
|
|
122
122
|
} finally {
|
|
123
|
-
t._t = t.
|
|
123
|
+
t._t = t.le;
|
|
124
124
|
t.Qe = false;
|
|
125
125
|
// The run applied: this is the frame now, so the dependency tail the
|
|
126
126
|
// compute pass left linked goes (A30, #3438 — `recompute` defers an
|
|
@@ -172,7 +172,7 @@ GlobalQueue.K = runEffect;
|
|
|
172
172
|
// Status dispatch rides the SHARED notifier (statusNotifierOf keys off
|
|
173
173
|
// _type): its error arm is behavior-identical to the closure that used to
|
|
174
174
|
// live here, without the per-node NodeExtension allocation.
|
|
175
|
-
o.
|
|
175
|
+
o.Z = run;
|
|
176
176
|
// The first run rides the heap like every wake (GlobalQueue._update), so a
|
|
177
177
|
// tracked effect created inside a render-effect callback runs after that
|
|
178
178
|
// pass's staged writes commit, not before.
|
|
@@ -133,7 +133,7 @@ function link(e, n) {
|
|
|
133
133
|
if (s) {
|
|
134
134
|
o = l !== null ? l.G : n.C;
|
|
135
135
|
if (o !== null && o.S === e) {
|
|
136
|
-
o.
|
|
136
|
+
o.Ie = n.Ne;
|
|
137
137
|
n._ = o;
|
|
138
138
|
return;
|
|
139
139
|
}
|
|
@@ -144,14 +144,14 @@ function link(e, n) {
|
|
|
144
144
|
// (the old alien-signals `isValidLink` walk, O(n²) when a computation
|
|
145
145
|
// re-reads earlier deps non-consecutively, e.g. store leaf reads).
|
|
146
146
|
const r = e.Ct;
|
|
147
|
-
if (r !== null && r.u === n && (!s || r.
|
|
147
|
+
if (r !== null && r.u === n && (!s || r.Ie === n.Ne)) return;
|
|
148
148
|
const t = n._ = e.Ct = {
|
|
149
149
|
S: e,
|
|
150
150
|
u: n,
|
|
151
151
|
G: o,
|
|
152
152
|
en: r,
|
|
153
153
|
t: null,
|
|
154
|
-
|
|
154
|
+
Ie: n.Ne
|
|
155
155
|
};
|
|
156
156
|
if (l !== null) l.G = t; else n.C = t;
|
|
157
157
|
if (r !== null) r.t = t; else e.o = t;
|
|
@@ -11,12 +11,12 @@ import { dirtyQueue } from "./scheduler.js";
|
|
|
11
11
|
* callback to the user queue once the pass has committed (see
|
|
12
12
|
* GlobalQueue._update, #3291).
|
|
13
13
|
*/ function enqueueSub(e) {
|
|
14
|
-
if (dirtyQueue.
|
|
14
|
+
if (dirtyQueue.lt > e.Et) dirtyQueue.lt = e.Et;
|
|
15
15
|
insertIntoHeap(e, dirtyQueue);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
function actualInsertIntoHeap(e, t) {
|
|
19
|
-
const E = (e._parent?.Pt ? e._parent.
|
|
19
|
+
const E = (e._parent?.Pt ? e._parent.ht?.Et : e._parent?.Et) ?? -1;
|
|
20
20
|
if (E >= e.Et) e.Et = E + 1;
|
|
21
21
|
const n = e.Et;
|
|
22
22
|
const I = t.gt[n];
|
|
@@ -98,11 +98,11 @@ function markNode(e, t = REACTIVE_DIRTY) {
|
|
|
98
98
|
|
|
99
99
|
function runHeap(e, t) {
|
|
100
100
|
e.p = false;
|
|
101
|
-
for (e.
|
|
102
|
-
let E = e.gt[e.
|
|
101
|
+
for (e.lt = 0; e.lt <= e.jt; e.lt++) {
|
|
102
|
+
let E = e.gt[e.lt];
|
|
103
103
|
while (E !== undefined) {
|
|
104
104
|
if (E.R & REACTIVE_IN_HEAP) t(E); else adjustHeight(E, e);
|
|
105
|
-
E = e.gt[e.
|
|
105
|
+
E = e.gt[e.lt];
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
e.jt = 0;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CONFIG_GUESS, NOT_PENDING, CONFIG_OVERRIDE, REACTIVE_DISPOSED, CONFIG_VERDICT, REACTIVE_PROBE_UNANSWERED, REACTIVE_LANE_DIRTY, CONFIG_HELD, REACTIVE_LANE_READ, STATUS_UNINITIALIZED, REACTIVE_RECOMPUTING_DEPS, CONFIG_AUTHORITATIVE, STATUS_ERROR, CONFIG_CHILDREN_FORBIDDEN,
|
|
1
|
+
import { CONFIG_GUESS, NOT_PENDING, CONFIG_OVERRIDE, REACTIVE_DISPOSED, CONFIG_VERDICT, REACTIVE_PROBE_UNANSWERED, REACTIVE_LANE_DIRTY, CONFIG_HELD, REACTIVE_LANE_READ, STATUS_UNINITIALIZED, REACTIVE_RECOMPUTING_DEPS, CONFIG_AUTHORITATIVE, STATUS_ERROR, CONFIG_CHILDREN_FORBIDDEN, EFFECT_RENDER, REACTIVE_SCREEN_READ, REACTIVE_FRAME_READ, REACTIVE_IN_HEAP, REACTIVE_DIRTY, REACTIVE_CHECK, STATUS_PENDING, CONFIG_INPUTS_PUBLISHED, CONFIG_SLOT_NODE, CONFIG_STAGED, REACTIVE_JOINED, REACTIVE_STAGED_READ } from "./constants.js";
|
|
2
2
|
|
|
3
3
|
import { attrHooks } from "./attribution-hooks.js";
|
|
4
4
|
|
|
@@ -10,7 +10,7 @@ import { laneValueOf } from "./truth.js";
|
|
|
10
10
|
|
|
11
11
|
import { enqueueSub } from "./heap.js";
|
|
12
12
|
|
|
13
|
-
import { schedule, question, nextQuestion, installLaneHooks, GlobalQueue, resolveTx, newTransaction, ownFlights, blocked, txOf, clock, insertSubs, laneDirty, queuePendingNode, list, passLane, sameLane, flushTransaction, commitPendingNode, releaseQueues, holdNode, joinFuture, reruns, merge
|
|
13
|
+
import { schedule, question, nextQuestion, installLaneHooks, GlobalQueue, resolveTx, newTransaction, ownFlights, blocked, txOf, clock, insertSubs, laneDirty, queuePendingNode, list, passLane, setPassLane, sameLane, flushTransaction, commitPendingNode, releaseQueues, holdNode, joinFuture, reruns, merge } from "./scheduler.js";
|
|
14
14
|
|
|
15
15
|
// Lanes (plan sec. 28 replay, 2026-10-02; maintainer rulings 2026-10-01/02). A lane
|
|
16
16
|
// is "a new base of a transition": the sub-frame an optimistic write opens
|
|
@@ -53,7 +53,7 @@ function newLane(e) {
|
|
|
53
53
|
/** What the screen shows for a lane's node: the lane's value once the lane
|
|
54
54
|
* has revealed, the committed truth before. */ function display(e) {
|
|
55
55
|
const n = e.T.V;
|
|
56
|
-
return e.T.Je.It && n !== NOT_PENDING ? n : e.
|
|
56
|
+
return e.T.Je.It && n !== NOT_PENDING ? n : e.le;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
/** Guesses written since the last seam (`[node, value, question, …]`).
|
|
@@ -68,7 +68,7 @@ function newLane(e) {
|
|
|
68
68
|
* on (a store setter's draft too, store/optimistic.ts). */ function guessedValueOf(e) {
|
|
69
69
|
const n = pendingGuessOf(e);
|
|
70
70
|
if (n !== NOT_PENDING) return n;
|
|
71
|
-
return e.l & CONFIG_OVERRIDE ? laneValueOf(e) : e.
|
|
71
|
+
return e.l & CONFIG_OVERRIDE ? laneValueOf(e) : e.le;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
/** The unflushed guess to `n`, else NOT_PENDING. */ function pendingGuessOf(e) {
|
|
@@ -84,7 +84,7 @@ function optimisticWrite(e, n) {
|
|
|
84
84
|
// the guess's question and entangles its frame (#3347) without notifying.
|
|
85
85
|
const s = guessedValueOf(e);
|
|
86
86
|
if (typeof n === "function") n = n(s);
|
|
87
|
-
if (e.
|
|
87
|
+
if (e.je && e.je(s, n) && !(e.l & CONFIG_GUESS)) return n;
|
|
88
88
|
pendingGuesses.push(e, n, question || nextQuestion());
|
|
89
89
|
schedule();
|
|
90
90
|
return n;
|
|
@@ -114,25 +114,25 @@ function applyGuesses(e) {
|
|
|
114
114
|
// The frame already holding the slot: a guess over it is a second
|
|
115
115
|
// suggestion for the slot, and two cannot finish apart (A34 (1)) — the
|
|
116
116
|
// frames entangle.
|
|
117
|
-
let
|
|
117
|
+
let i = null;
|
|
118
118
|
if (t.l & CONFIG_OVERRIDE) {
|
|
119
119
|
// A lane's node already: a guess re-asked, or a derivation guessed
|
|
120
120
|
// over (its staging is void — the guess is the value now). The same
|
|
121
121
|
// value re-asked renews the question; nothing to notify.
|
|
122
|
-
u = !t.
|
|
122
|
+
u = !t.je || !t.je(laneValueOf(t), l);
|
|
123
123
|
if (!(t.l & CONFIG_GUESS)) {
|
|
124
|
-
t.
|
|
124
|
+
t.ie = NOT_PENDING;
|
|
125
125
|
t.l |= CONFIG_GUESS;
|
|
126
126
|
}
|
|
127
127
|
t.T.V = l;
|
|
128
|
-
|
|
128
|
+
i = txOf(t)._parent;
|
|
129
129
|
} else {
|
|
130
130
|
let s;
|
|
131
131
|
// A held node: a truth staged there, or what a corrected guess showed.
|
|
132
132
|
// The guess composes on it, so it ends with that hold — and with its
|
|
133
133
|
// own frame's (F-11): it lands only when both have (the board's rapid
|
|
134
134
|
// adds — a later add's row reverted with an earlier add's landing).
|
|
135
|
-
if (t.l & CONFIG_HELD) s = newLane(
|
|
135
|
+
if (t.l & CONFIG_HELD) s = newLane(i = txOf(t)); else {
|
|
136
136
|
if (e === null && inFlight(t)) e = newTransaction(false);
|
|
137
137
|
if (e === null) continue;
|
|
138
138
|
s = n ??= newLane(e);
|
|
@@ -147,8 +147,8 @@ function applyGuesses(e) {
|
|
|
147
147
|
t.l |= CONFIG_OVERRIDE | CONFIG_GUESS;
|
|
148
148
|
list(t, s);
|
|
149
149
|
}
|
|
150
|
-
if (e !== null &&
|
|
151
|
-
ext(t).
|
|
150
|
+
if (e !== null && i !== null) merge(e, i);
|
|
151
|
+
ext(t).de = pendingGuesses[s + 2];
|
|
152
152
|
if (u) {
|
|
153
153
|
// The lane's members re-derive as its work (REACTIVE_LANE_DIRTY): a
|
|
154
154
|
// blocked lane's re-guess does not re-run its render effects as stale
|
|
@@ -266,7 +266,7 @@ function unlink(e) {
|
|
|
266
266
|
}
|
|
267
267
|
if (!e.i) {
|
|
268
268
|
if (!(e.l & CONFIG_OVERRIDE)) {
|
|
269
|
-
e.
|
|
269
|
+
e.ie = NOT_PENDING;
|
|
270
270
|
e.l = e.l & -1048833 | CONFIG_OVERRIDE;
|
|
271
271
|
ext(e);
|
|
272
272
|
}
|
|
@@ -327,7 +327,7 @@ function supersede(e, n, s) {
|
|
|
327
327
|
dissolveLane(t, l, e);
|
|
328
328
|
// What the screen showed stays the screen until the parent lands (A18
|
|
329
329
|
// (c): display and untracked reads keep the override until the commit).
|
|
330
|
-
if (t.It) e.
|
|
330
|
+
if (t.It) e.le = e.T.V;
|
|
331
331
|
e.l &= -10241;
|
|
332
332
|
e.T.V = NOT_PENDING;
|
|
333
333
|
holdNode(e, l);
|
|
@@ -337,7 +337,7 @@ function supersede(e, n, s) {
|
|
|
337
337
|
// A confirmation is silent (A17); only truth readers are told.
|
|
338
338
|
GlobalQueue.tn?.(t);
|
|
339
339
|
}
|
|
340
|
-
e.
|
|
340
|
+
e.ie = n;
|
|
341
341
|
joinFuture(l);
|
|
342
342
|
}
|
|
343
343
|
|
|
@@ -361,16 +361,16 @@ function supersede(e, n, s) {
|
|
|
361
361
|
e.it = null;
|
|
362
362
|
if (lanes.indexOf(l) !== -1) dissolveLane(l, null);
|
|
363
363
|
}
|
|
364
|
-
for (let t = 0; t < e.
|
|
365
|
-
const l = e.
|
|
364
|
+
for (let t = 0; t < e.Ye.length; t++) {
|
|
365
|
+
const l = e.Ye[t];
|
|
366
366
|
const u = l.T;
|
|
367
367
|
if (l === s || u.Je !== e) continue;
|
|
368
|
-
const
|
|
369
|
-
const
|
|
368
|
+
const i = l.i;
|
|
369
|
+
const r = l.l & CONFIG_GUESS;
|
|
370
370
|
const o = u.V;
|
|
371
371
|
// What the screen shows of it (NOT_PENDING: nothing — a lane pass that
|
|
372
372
|
// errored or pends staged no value), and the lane's latest.
|
|
373
|
-
const E = e.It && !
|
|
373
|
+
const E = e.It && !i ? o : l.le;
|
|
374
374
|
const f = laneValueOf(l);
|
|
375
375
|
u.V = NOT_PENDING;
|
|
376
376
|
l.l &= -10241;
|
|
@@ -381,9 +381,9 @@ function supersede(e, n, s) {
|
|
|
381
381
|
// displayed guess that lifts to something else — the truth staged
|
|
382
382
|
// beneath it (superseded by a landing held there) or the value it
|
|
383
383
|
// covered (reverted: nothing came true).
|
|
384
|
-
if (
|
|
385
|
-
const e = l.
|
|
386
|
-
if (e !== o) attrHooks.optimisticReverted(l, o, e, l.
|
|
384
|
+
if (r && e.It && attrHooks !== null) {
|
|
385
|
+
const e = l.ie !== NOT_PENDING ? l.ie : l.le;
|
|
386
|
+
if (e !== o) attrHooks.optimisticReverted(l, o, e, l.ie !== NOT_PENDING ? "superseded" : "reverted");
|
|
387
387
|
}
|
|
388
388
|
// A derivation the revert re-derives (dirtied by the guess's
|
|
389
389
|
// notification — listed before it), or one still in flight, shows the
|
|
@@ -391,19 +391,19 @@ function supersede(e, n, s) {
|
|
|
391
391
|
// of its flight observes it (#3648, #3651; #3305's commit beneath a
|
|
392
392
|
// flight).
|
|
393
393
|
u.Je = null;
|
|
394
|
-
if (!
|
|
394
|
+
if (!i && !r) l.ie = f;
|
|
395
395
|
commitPendingNode(l);
|
|
396
|
-
if (!
|
|
396
|
+
if (!i) {
|
|
397
397
|
if (l.R & (REACTIVE_IN_HEAP | REACTIVE_DIRTY | REACTIVE_CHECK) || l.m & STATUS_PENDING) l.l |= CONFIG_INPUTS_PUBLISHED;
|
|
398
|
-
if (E !== NOT_PENDING && (!l.
|
|
398
|
+
if (E !== NOT_PENDING && (!l.je || !l.je(E, l.le))) insertSubs(l);
|
|
399
399
|
}
|
|
400
|
-
} else if (
|
|
400
|
+
} else if (r) {
|
|
401
401
|
// A guess of a dissolving lane: its truth — a landing held beneath it,
|
|
402
402
|
// else the value it covered — is the parent's to land, and its readers
|
|
403
403
|
// re-derive from it there. What the screen showed stays the screen
|
|
404
404
|
// until then (A18 (c)).
|
|
405
|
-
if (l.
|
|
406
|
-
if (e.It) l.
|
|
405
|
+
if (l.ie === NOT_PENDING) l.ie = covered(l);
|
|
406
|
+
if (e.It) l.le = o;
|
|
407
407
|
holdNode(l, n);
|
|
408
408
|
insertSubs(l);
|
|
409
409
|
} else {
|
|
@@ -415,18 +415,18 @@ function supersede(e, n, s) {
|
|
|
415
415
|
// answer — it re-asks from the truth (whose notification may not reach
|
|
416
416
|
// it: an input's truth equal to its committed value is silent).
|
|
417
417
|
u.Je = null;
|
|
418
|
-
if (!
|
|
419
|
-
if (E !== NOT_PENDING && e.It) l.
|
|
418
|
+
if (!i) {
|
|
419
|
+
if (E !== NOT_PENDING && e.It) l.ie = E;
|
|
420
420
|
if (l.m & STATUS_PENDING) {
|
|
421
|
-
u.
|
|
421
|
+
u.Ee = null;
|
|
422
422
|
enqueueSub(l);
|
|
423
423
|
}
|
|
424
424
|
}
|
|
425
425
|
if (e.It) {
|
|
426
426
|
commitPendingNode(l);
|
|
427
|
-
if (!
|
|
427
|
+
if (!i) l.l |= CONFIG_INPUTS_PUBLISHED;
|
|
428
428
|
} else {
|
|
429
|
-
if (u.
|
|
429
|
+
if (u.ft !== null || u.Nt !== null) GlobalQueue.et(l, false, true);
|
|
430
430
|
l.l &= -1048961;
|
|
431
431
|
}
|
|
432
432
|
}
|
|
@@ -459,7 +459,11 @@ function supersede(e, n, s) {
|
|
|
459
459
|
* has shown, or one the seam has not judged yet (its run waits on the
|
|
460
460
|
* verdict: released if the lane shows, held if it blocks) — and sees the
|
|
461
461
|
* lane's latest. NOT_PENDING falls through: lane work's read of a pending
|
|
462
|
-
* member throws like any.
|
|
462
|
+
* member throws like any. A verdict reader entering a lane the seam has
|
|
463
|
+
* not judged reads ahead of it like a staging read: if the seam holds the
|
|
464
|
+
* lane, the pass re-derives on the screen (`entered`, #4284) — `latest()`
|
|
465
|
+
* beside the lane's nodes tears ahead of the hold however the reads are
|
|
466
|
+
* grouped into effects. */ function laneRead(e, n) {
|
|
463
467
|
const s = n.l & CONFIG_GUESS;
|
|
464
468
|
const t = txOf(n);
|
|
465
469
|
// A lane a correction folded under (D-34): store/optimistic.ts reads it.
|
|
@@ -476,6 +480,7 @@ function supersede(e, n, s) {
|
|
|
476
480
|
// An errored member's lane value predates its error; the error is the answer (#4106).
|
|
477
481
|
if (!s && u & STATUS_ERROR) return NOT_PENDING;
|
|
478
482
|
if (e === null) return laneValueOf(n);
|
|
483
|
+
const i = !s && u & STATUS_PENDING;
|
|
479
484
|
if (!tracking || e.l & CONFIG_CHILDREN_FORBIDDEN) {
|
|
480
485
|
// An untracked read inside lane work derives from the lane like a
|
|
481
486
|
// tracked one, minus the subscription — `untrack` is about
|
|
@@ -484,45 +489,51 @@ function supersede(e, n, s) {
|
|
|
484
489
|
// lanes read by one pass link, as for tracked reads). Outside lane
|
|
485
490
|
// work, the screen.
|
|
486
491
|
if (passLane === null || e.l & CONFIG_CHILDREN_FORBIDDEN) return display(n);
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
if (
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
492
|
+
} else if (e.i === EFFECT_RENDER) {
|
|
493
|
+
// A flight whose value has never shown: nothing to show — the leaf
|
|
494
|
+
// waits on it, and its own frame holds (A15, #3334), not the lane's
|
|
495
|
+
// parent. The landing lands the frame: its own pass (the flush joined
|
|
496
|
+
// its transaction) reads the answer it waited on — the lane's staging,
|
|
497
|
+
// revealed at this seam.
|
|
498
|
+
const l = i && n.T.V === NOT_PENDING;
|
|
499
|
+
// D-28: a leaf a verdict read moved into the lane still throws on such
|
|
500
|
+
// a flight — the pass leaves the lane, and its frame holds as if the
|
|
501
|
+
// read had come first (S-39, #4275). Nor does a verdict lane's seat carry
|
|
502
|
+
// the leaf into another lane (no link): it reads that lane as from the
|
|
503
|
+
// frame's seat (#4284).
|
|
504
|
+
if ((l || passLane !== t) && passLane?._parent.it === passLane) setPassLane(null);
|
|
505
|
+
if (passLane === null || !sameLane(passLane, t)) {
|
|
506
|
+
// (A lane that has never shown: the screen is the committed value.)
|
|
507
|
+
if (l && t.It) return NOT_PENDING;
|
|
508
|
+
if (!s && n.ie !== NOT_PENDING && e.l & CONFIG_HELD && flushTransaction !== null && resolveTx(flushTransaction) === txOf(e)) return n.ie;
|
|
509
|
+
if (t.ln || i) {
|
|
510
|
+
if (!s && !t.It && u & STATUS_UNINITIALIZED) throw new NotReadyError(null);
|
|
511
|
+
// Re-derived at the reveal; once per pass (the pass may also be a stale
|
|
512
|
+
// reader of a transaction, REACTIVE_FRAME_READ — both reruns apply).
|
|
513
|
+
if (!(e.R & REACTIVE_SCREEN_READ)) {
|
|
514
|
+
e.R |= REACTIVE_FRAME_READ | REACTIVE_SCREEN_READ;
|
|
515
|
+
t.En.push(e);
|
|
516
|
+
}
|
|
517
|
+
return display(n);
|
|
508
518
|
}
|
|
509
|
-
|
|
519
|
+
// A verdict reader reads ahead of the seam's verdict: re-derived on the
|
|
520
|
+
// screen if the lane holds (#4284).
|
|
521
|
+
if (e.l & CONFIG_VERDICT && t._parent.it !== t) entered.push(e);
|
|
510
522
|
}
|
|
511
523
|
}
|
|
512
524
|
enterLane(t, e);
|
|
513
|
-
|
|
514
|
-
return laneValueOf(n);
|
|
525
|
+
return i ? NOT_PENDING : laneValueOf(n);
|
|
515
526
|
}
|
|
516
527
|
|
|
517
528
|
/** The value a guess covered: the node's committed value — a store slot's
|
|
518
529
|
* asked of the store (a chained link follows the inner store's commits). */ function covered(e) {
|
|
519
|
-
return e.l & CONFIG_SLOT_NODE && GlobalQueue.fn !== undefined ? GlobalQueue.fn(e) : e.
|
|
530
|
+
return e.l & CONFIG_SLOT_NODE && GlobalQueue.fn !== undefined ? GlobalQueue.fn(e) : e.le;
|
|
520
531
|
}
|
|
521
532
|
|
|
522
533
|
/** A guess in `l` whose own truth is in flight (its refetch — authoritative:
|
|
523
534
|
* the landing supersedes, not the body's end). */ function guessFlights(e) {
|
|
524
|
-
for (let n = 0; n < e.
|
|
525
|
-
const s = e.
|
|
535
|
+
for (let n = 0; n < e.Ye.length; n++) {
|
|
536
|
+
const s = e.Ye[n];
|
|
526
537
|
if (s.l & CONFIG_GUESS && s.T.Je === e && inFlight(s)) return true;
|
|
527
538
|
}
|
|
528
539
|
return false;
|
|
@@ -552,12 +563,12 @@ function supersede(e, n, s) {
|
|
|
552
563
|
// Each guess's truth: the one beneath it (a landing held there), else
|
|
553
564
|
// the one it covered. (A correction dissolves the lane — the others are
|
|
554
565
|
// re-homed with theirs, `dissolveLane`.)
|
|
555
|
-
for (let e = 0; e < t.
|
|
556
|
-
const n = t.
|
|
566
|
+
for (let e = 0; e < t.Ye.length && lanes.indexOf(t) !== -1; e++) {
|
|
567
|
+
const n = t.Ye[e];
|
|
557
568
|
if (n.l & CONFIG_GUESS && n.T.Je === t) {
|
|
558
|
-
const e = n.
|
|
559
|
-
reverting = n.
|
|
560
|
-
supersede(n, e, !n.
|
|
569
|
+
const e = n.ie !== NOT_PENDING ? n.ie : covered(n);
|
|
570
|
+
reverting = n.ie === NOT_PENDING;
|
|
571
|
+
supersede(n, e, !n.je || !n.je(n.T.V, e));
|
|
561
572
|
reverting = false;
|
|
562
573
|
}
|
|
563
574
|
}
|
|
@@ -575,21 +586,21 @@ function laneSeam(e, n) {
|
|
|
575
586
|
e.ln = false;
|
|
576
587
|
e.It = true;
|
|
577
588
|
reruns(e);
|
|
578
|
-
for (let n = 0; n < e.
|
|
579
|
-
const s = e.
|
|
589
|
+
for (let n = 0; n < e.Ye.length; n++) {
|
|
590
|
+
const s = e.Ye[n];
|
|
580
591
|
if (s.R & LEFT_SHOWN) {
|
|
581
592
|
s.R ^= LEFT_SHOWN;
|
|
582
593
|
insertSubs(s);
|
|
583
594
|
}
|
|
584
595
|
const t = s.T;
|
|
585
596
|
if (t.Je !== e) continue;
|
|
586
|
-
if (!(s.l & CONFIG_GUESS) && !s.i && s.
|
|
587
|
-
t.V = s.
|
|
588
|
-
s.
|
|
597
|
+
if (!(s.l & CONFIG_GUESS) && !s.i && s.ie !== NOT_PENDING) {
|
|
598
|
+
t.V = s.ie;
|
|
599
|
+
s.ie = NOT_PENDING;
|
|
589
600
|
}
|
|
590
601
|
// The frame this pass built is the screen's now; the one it replaced
|
|
591
602
|
// goes (as `commitPendingNode` retires a committed pass's).
|
|
592
|
-
if (t.
|
|
603
|
+
if (t.ft !== null || t.Nt !== null) GlobalQueue.et(s, false, true);
|
|
593
604
|
s.l &= ~CONFIG_STAGED;
|
|
594
605
|
}
|
|
595
606
|
releaseQueues(e);
|
|
@@ -639,17 +650,17 @@ function laneSeam(e, n) {
|
|
|
639
650
|
// folds a current one beneath it (D-34): true.
|
|
640
651
|
const s = stale(e, question);
|
|
641
652
|
if (GlobalQueue.an?.(e, n, s) || s) {
|
|
642
|
-
e.
|
|
653
|
+
e.ie = n;
|
|
643
654
|
e.l |= CONFIG_HELD;
|
|
644
655
|
schedule();
|
|
645
656
|
return n;
|
|
646
657
|
}
|
|
647
|
-
supersede(e, n, !e.
|
|
658
|
+
supersede(e, n, !e.je || !e.je(e.T.V, n));
|
|
648
659
|
} else {
|
|
649
660
|
const s = txOf(e);
|
|
650
|
-
if (e.
|
|
651
|
-
if (s.It) e.
|
|
652
|
-
if (e.X !== undefined) e.
|
|
661
|
+
if (e.je && e.je(laneValueOf(e), n)) return n;
|
|
662
|
+
if (s.It) e.ie = n; else e.T.V = n;
|
|
663
|
+
if (e.X !== undefined) e.ae = clock;
|
|
653
664
|
insertSubs(e);
|
|
654
665
|
laneDirty(e, s);
|
|
655
666
|
}
|
|
@@ -671,18 +682,18 @@ function laneSeam(e, n) {
|
|
|
671
682
|
* A24). The guess's own question answering supersedes as usual. */ function laneOutcome(e, n, s) {
|
|
672
683
|
if (s) return true;
|
|
673
684
|
if (stale(e, answered(e))) {
|
|
674
|
-
e.
|
|
685
|
+
e.ie = n;
|
|
675
686
|
e.l |= CONFIG_HELD;
|
|
676
687
|
return true;
|
|
677
688
|
}
|
|
678
|
-
supersede(e, n, !e.
|
|
689
|
+
supersede(e, n, !e.je || !e.je(e.T.V, n));
|
|
679
690
|
return true;
|
|
680
691
|
}
|
|
681
692
|
|
|
682
693
|
/** THE provenance test, for a derivation's pass and a write alike: an
|
|
683
694
|
* answer to question `q` landing on a guess is stale when `q` is older
|
|
684
695
|
* than the guess's own; unstamped (0) and mainline are current. */ function stale(e, n) {
|
|
685
|
-
return n !== 0 && n < e.T.
|
|
696
|
+
return n !== 0 && n < e.T.de;
|
|
686
697
|
}
|
|
687
698
|
|
|
688
699
|
/** The question a derivation's pass answered: the newest among the stamped
|
|
@@ -691,7 +702,7 @@ function laneSeam(e, n) {
|
|
|
691
702
|
let n = 0;
|
|
692
703
|
for (let s = e.C; s !== null; s = s.G) {
|
|
693
704
|
const e = s.S;
|
|
694
|
-
if (e.T !== null && e.T.
|
|
705
|
+
if (e.T !== null && e.T.de > n && e.ae === clock) n = e.T.de;
|
|
695
706
|
}
|
|
696
707
|
return n;
|
|
697
708
|
}
|
|
@@ -733,16 +744,25 @@ const laneSeams = e => {
|
|
|
733
744
|
if (l != null && l.V && n.indexOf(l) === -1) n.push(l);
|
|
734
745
|
}
|
|
735
746
|
for (let e = Math.min(judged, lanes.length) - 1; e >= 0; e--) laneSeam(lanes[e], n.indexOf(lanes[e]) !== -1);
|
|
747
|
+
// A verdict reader that entered a lane this seam held: its run waits with
|
|
748
|
+
// the lane, and the verdict beside it would freeze — it re-derives next
|
|
749
|
+
// round from the frame's seat, a stale reader of the lane (#4284).
|
|
750
|
+
for (let e; e = entered.pop(); ) if (e.T.Je?.ln) {
|
|
751
|
+
e.R &= ~REACTIVE_LANE_DIRTY;
|
|
752
|
+
enqueueSub(e);
|
|
753
|
+
}
|
|
736
754
|
};
|
|
737
755
|
|
|
756
|
+
/** Render effects that read a verdict and entered a lane before its seam (#4284). */ const entered = [];
|
|
757
|
+
|
|
738
758
|
installLaneHooks(laneRead, laneStage, laneOutcome, laneWrite, applyGuessesAt, laneSeams, () => lanes.length !== 0 && laneCorrections(), endLanes, lanesBlocked);
|
|
739
759
|
|
|
740
|
-
GlobalQueue.
|
|
760
|
+
GlobalQueue.xe = e => {
|
|
741
761
|
const n = [];
|
|
742
762
|
for (let s = 0; s < lanes.length; s++) {
|
|
743
763
|
const t = lanes[s];
|
|
744
764
|
if (resolveTx(t._parent) !== e) continue;
|
|
745
|
-
for (let e = 0; e < t.
|
|
765
|
+
for (let e = 0; e < t.Ye.length; e++) if (t.Ye[e].l & CONFIG_GUESS && t.Ye[e].T.Je === t) n.push(t.Ye[e]);
|
|
746
766
|
}
|
|
747
767
|
return n;
|
|
748
768
|
};
|
|
@@ -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
|
|
@@ -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,7 +309,7 @@ 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,
|