@solidjs/signals 2.0.0-rc.14 → 2.0.0-rc.15
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 +248 -189
- package/dist/dev.attribution.js +124 -41
- package/dist/dev.js +553 -322
- package/dist/observe/affects.js +34 -31
- package/dist/observe/boundaries.js +166 -132
- package/dist/observe/core/async.js +90 -83
- package/dist/observe/core/attribution-queries.js +1 -1
- package/dist/observe/core/attribution.js +281 -197
- package/dist/observe/core/constants.js +7 -2
- package/dist/observe/core/context.js +3 -3
- package/dist/observe/core/core.js +277 -245
- package/dist/observe/core/dev.js +3 -3
- package/dist/observe/core/effect.js +21 -21
- package/dist/observe/core/external.js +2 -2
- package/dist/observe/core/graph.js +25 -25
- package/dist/observe/core/heap.js +9 -9
- package/dist/observe/core/lanes.js +206 -160
- package/dist/observe/core/owner.js +27 -27
- package/dist/observe/core/scheduler.js +92 -86
- package/dist/observe/core/verdict.js +66 -54
- package/dist/observe/map.js +35 -35
- package/dist/observe/reveal.js +24 -24
- package/dist/observe/signals.js +38 -28
- package/dist/observe/store/affects.js +75 -68
- package/dist/observe/store/hooks.js +13 -0
- package/dist/observe/store/index.js +0 -2
- package/dist/observe/store/optimistic.js +67 -34
- package/dist/observe/store/projection.js +37 -31
- package/dist/observe/store/reconcile.js +32 -30
- package/dist/observe/store/store.js +128 -85
- package/dist/prod/affects.js +34 -31
- package/dist/prod/boundaries.js +143 -109
- package/dist/prod/core/async.js +64 -67
- package/dist/prod/core/constants.js +7 -2
- package/dist/prod/core/context.js +3 -3
- package/dist/prod/core/core.js +302 -270
- package/dist/prod/core/effect.js +38 -38
- package/dist/prod/core/external.js +4 -4
- package/dist/prod/core/graph.js +27 -27
- package/dist/prod/core/heap.js +30 -30
- package/dist/prod/core/lanes.js +257 -211
- package/dist/prod/core/owner.js +39 -39
- package/dist/prod/core/scheduler.js +125 -119
- package/dist/prod/core/verdict.js +70 -58
- package/dist/prod/map.js +73 -73
- package/dist/prod/reveal.js +38 -38
- package/dist/prod/signals.js +30 -20
- package/dist/prod/store/affects.js +75 -68
- package/dist/prod/store/hooks.js +13 -0
- package/dist/prod/store/index.js +0 -2
- package/dist/prod/store/optimistic.js +114 -81
- package/dist/prod/store/projection.js +30 -24
- package/dist/prod/store/reconcile.js +32 -30
- package/dist/prod/store/store.js +136 -93
- package/dist/types/boundaries.d.ts +4 -0
- package/dist/types/core/attribution.d.ts +6 -2
- package/dist/types/core/constants.d.ts +5 -0
- package/dist/types/core/core.d.ts +33 -3
- package/dist/types/core/lanes.d.ts +10 -0
- package/dist/types/core/scheduler.d.ts +12 -6
- package/dist/types/core/types.d.ts +0 -6
- package/dist/types/store/affects.d.ts +11 -0
- package/dist/types/store/hooks.d.ts +48 -0
- package/dist/types/store/index.d.ts +0 -1
- package/dist/types/store/store.d.ts +3 -38
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EFFECT_RENDER, EFFECT_USER, CONFIG_HELD, CONFIG_OVERRIDE, NOT_PENDING, STATUS_UNINITIALIZED, STATUS_PENDING, CONFIG_STAGED, REACTIVE_DISPOSED, REACTIVE_ZOMBIE, CONFIG_VERDICT, CONFIG_HAS_SNAPSHOT, REACTIVE_RECOMPUTING_DEPS, REACTIVE_MISSED_WAKE, CONFIG_IN_SNAPSHOT_SCOPE, REACTIVE_SNAPSHOT_STALE, REACTIVE_LANE_DIRTY, CONFIG_INPUTS_PUBLISHED, EFFECT_TRACKED, REACTIVE_MANUAL_WRITE, CONFIG_GUESS, REACTIVE_FRAME_READ, CONFIG_REDERIVE } from "./constants.js";
|
|
1
|
+
import { EFFECT_RENDER, EFFECT_USER, CONFIG_HELD, CONFIG_OVERRIDE, NOT_PENDING, STATUS_UNINITIALIZED, STATUS_PENDING, CONFIG_STAGED, REACTIVE_DISPOSED, REACTIVE_ZOMBIE, CONFIG_VERDICT, CONFIG_HAS_SNAPSHOT, REACTIVE_RECOMPUTING_DEPS, REACTIVE_MISSED_WAKE, CONFIG_IN_SNAPSHOT_SCOPE, REACTIVE_SNAPSHOT_STALE, CONFIG_HELD_FRAME, REACTIVE_LANE_DIRTY, CONFIG_INPUTS_PUBLISHED, EFFECT_TRACKED, REACTIVE_MANUAL_WRITE, CONFIG_GUESS, REACTIVE_FRAME_READ, CONFIG_REDERIVE, STATUS_ERROR } from "./constants.js";
|
|
2
2
|
|
|
3
3
|
import { attrHooks } from "./attribution-hooks.js";
|
|
4
4
|
|
|
@@ -19,18 +19,18 @@ import { devCheckQuiescent } from "./invariants.js";
|
|
|
19
19
|
|
|
20
20
|
/** A transaction (listed here) or a lane (lanes.ts lists its own). */ function newTransaction(e, n = null) {
|
|
21
21
|
const t = {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
ln: [],
|
|
23
|
+
rn: [ [], [] ],
|
|
24
|
+
fn: [],
|
|
25
25
|
dn: null,
|
|
26
26
|
_parent: n,
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
B: e,
|
|
28
|
+
xe: null,
|
|
29
29
|
tt: 0,
|
|
30
30
|
nt: false,
|
|
31
31
|
nn: null,
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
Ze: false,
|
|
33
|
+
sn: false,
|
|
34
34
|
T: null
|
|
35
35
|
};
|
|
36
36
|
if (!e) transactions.push(t);
|
|
@@ -78,14 +78,14 @@ function setPassLane(e) {
|
|
|
78
78
|
* set it. */ let passTx = null;
|
|
79
79
|
|
|
80
80
|
function joinPassTx(e, n) {
|
|
81
|
-
if (globalQueue.
|
|
81
|
+
if (globalQueue.X && !n || flushTransaction !== null) return joinFuture(e);
|
|
82
82
|
if (passTx === null) passTx = resolveTx(e); else merge(resolveTx(e), passTx);
|
|
83
83
|
schedule();
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
/** The transaction holding a node (CONFIG_HELD set), resolved through
|
|
87
87
|
* merges; the path is compressed. */ function txOf(e) {
|
|
88
|
-
return e.m.
|
|
88
|
+
return e.m.He = resolveTx(e.m.He);
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
/** `t` resolved through merges while it is still parked or open; null once
|
|
@@ -106,7 +106,7 @@ function resolveTx(e) {
|
|
|
106
106
|
if (n === null) {
|
|
107
107
|
flushTransaction = e === null ? newTransaction(false) : resolveTx(e);
|
|
108
108
|
} else if (e !== null) merge(resolveTx(e), n);
|
|
109
|
-
if (!globalQueue.
|
|
109
|
+
if (!globalQueue.X) schedule();
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
/** `t` merges into `f`: the work that touched both entangles them (A15).
|
|
@@ -116,14 +116,14 @@ function resolveTx(e) {
|
|
|
116
116
|
// into the other would close a cycle `resolveTx` never leaves.
|
|
117
117
|
e = resolveTx(e);
|
|
118
118
|
n = resolveTx(n);
|
|
119
|
-
if (e === n || e.
|
|
119
|
+
if (e === n || e.B || n.B) return;
|
|
120
120
|
e.dn = n;
|
|
121
121
|
n.tt += e.tt;
|
|
122
122
|
n.nt ||= e.nt;
|
|
123
|
-
append(n.
|
|
124
|
-
append(n.
|
|
125
|
-
append(n.
|
|
126
|
-
append(n.
|
|
123
|
+
append(n.ln, e.ln);
|
|
124
|
+
append(n.rn[0], e.rn[0]);
|
|
125
|
+
append(n.rn[1], e.rn[1]);
|
|
126
|
+
append(n.fn, e.fn);
|
|
127
127
|
if (e.T !== null) append(n.T ??= [], e.T);
|
|
128
128
|
transactions.splice(transactions.indexOf(e), 1);
|
|
129
129
|
}
|
|
@@ -150,7 +150,7 @@ function resolveTx(e) {
|
|
|
150
150
|
judge ??= e;
|
|
151
151
|
// An action still running in it holds it open (action.ts). Its lanes
|
|
152
152
|
// (lanes.ts): a lane under `t` blocks it while blocked itself.
|
|
153
|
-
const t = e.tt !== 0 || blockedBy(e.
|
|
153
|
+
const t = e.tt !== 0 || blockedBy(e.ln, e) || !!GlobalQueue.On && GlobalQueue.On(e);
|
|
154
154
|
judge = n;
|
|
155
155
|
return t;
|
|
156
156
|
}
|
|
@@ -162,7 +162,7 @@ let judge = null;
|
|
|
162
162
|
* not (lanes.ts, the body-end corollary). A pending reader counts only if
|
|
163
163
|
* one of the sources it waits on is `t`'s (a render effect marked pending
|
|
164
164
|
* by a lane flight, never re-run, is the lane's wait, not the frame's). */ function ownFlights(e) {
|
|
165
|
-
return blockedBy(e.
|
|
165
|
+
return blockedBy(e.ln, e, true);
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
/** A source the pending node waits on that is `t`'s (held by it, or by no
|
|
@@ -171,7 +171,7 @@ let judge = null;
|
|
|
171
171
|
const t = e.m?.Se;
|
|
172
172
|
if (t === undefined) return true;
|
|
173
173
|
for (const e of t) {
|
|
174
|
-
const t = e.m?.
|
|
174
|
+
const t = e.m?.He;
|
|
175
175
|
if (t == null || resolveTx(t) === n) return true;
|
|
176
176
|
}
|
|
177
177
|
return false;
|
|
@@ -180,15 +180,15 @@ let judge = null;
|
|
|
180
180
|
function blockedBy(e, n, t = false) {
|
|
181
181
|
for (let l = 0; l < e.length; l++) {
|
|
182
182
|
const o = e[l];
|
|
183
|
-
if (!(o.
|
|
183
|
+
if (!(o.S & STATUS_PENDING) || o.R & REACTIVE_DISPOSED || o.i & CONFIG_GUESS || o.m.He !== null && txOf(o) !== n || t && !ownSource(o, n)) continue;
|
|
184
184
|
// The frame's own pending observer (off screen: this one holds nothing;
|
|
185
185
|
// the next listed node may).
|
|
186
|
-
if (o.
|
|
186
|
+
if (o.u === EFFECT_RENDER) {
|
|
187
187
|
if (onScreen(o, judge ?? n)) return true;
|
|
188
188
|
continue;
|
|
189
189
|
}
|
|
190
|
-
for (let e = o.t; e !== null; e = e.
|
|
191
|
-
const t = e.
|
|
190
|
+
for (let e = o.t; e !== null; e = e.o) {
|
|
191
|
+
const t = e.l;
|
|
192
192
|
// A frame reader: a render effect, or a stale reader of the flight
|
|
193
193
|
// (REACTIVE_FRAME_READ — a verdict reader, `observeFlight`; lane work,
|
|
194
194
|
// `frameRead`): served committed instead of going pending, it derives
|
|
@@ -200,7 +200,7 @@ function blockedBy(e, n, t = false) {
|
|
|
200
200
|
// lane-membership ruling, 2026-10-05). An output that never committed
|
|
201
201
|
// shows nothing — a mount the frame has not revealed — and holds
|
|
202
202
|
// nothing.
|
|
203
|
-
if ((t.
|
|
203
|
+
if ((t.u === EFFECT_RENDER && t.S & STATUS_PENDING || t.R & REACTIVE_FRAME_READ || t.i & CONFIG_REDERIVE && t.S === STATUS_PENDING) && (e.Pe === t.pe || t.m?.F != null) && !(t.R & REACTIVE_DISPOSED) && onScreen(t, judge ?? n)) return true;
|
|
204
204
|
}
|
|
205
205
|
}
|
|
206
206
|
return false;
|
|
@@ -214,7 +214,7 @@ function blockedBy(e, n, t = false) {
|
|
|
214
214
|
* makes its say moot), and not behind a fallback (A33: a boundary showing its fallback is the display of
|
|
215
215
|
* everything under it, so a reader there is not on screen and holds
|
|
216
216
|
* nothing — boundaries.ts). */ function onScreen(e, n) {
|
|
217
|
-
return !(e.R & REACTIVE_ZOMBIE && removalStagedBy(e, n)) && !(GlobalQueue.
|
|
217
|
+
return !(e.R & REACTIVE_ZOMBIE && removalStagedBy(e, n)) && !(GlobalQueue.ce && GlobalQueue.ce(e));
|
|
218
218
|
}
|
|
219
219
|
|
|
220
220
|
/** A15 (#3463): a reader whose removal is staged — a zombie, parked by a
|
|
@@ -225,7 +225,7 @@ function blockedBy(e, n, t = false) {
|
|
|
225
225
|
* the nearest non-zombie ancestor. */ function removalStagedBy(e, n) {
|
|
226
226
|
let t = e;
|
|
227
227
|
while (t !== null && t.R & REACTIVE_ZOMBIE) t = t._parent;
|
|
228
|
-
return t !== null && (t.
|
|
228
|
+
return t !== null && (t.i & CONFIG_HELD) !== 0 && t.m?.He != null && txOf(t) === resolveTx(n);
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
/** Unchanged passes this flush whose stale dependency tail awaits the
|
|
@@ -252,7 +252,7 @@ function deferZombie(e) {
|
|
|
252
252
|
|
|
253
253
|
/** Put a staged node in a transaction: flagged, stamped, on its landing
|
|
254
254
|
* list. */ function holdNode(e, n) {
|
|
255
|
-
e.
|
|
255
|
+
e.i |= CONFIG_HELD;
|
|
256
256
|
list(e, n);
|
|
257
257
|
}
|
|
258
258
|
|
|
@@ -260,9 +260,9 @@ function deferZombie(e) {
|
|
|
260
260
|
* transaction whose `_nodes` it is on (every site that sets one does the
|
|
261
261
|
* other), so a re-pass, a re-landing or a re-staging adds nothing. */ function list(e, n) {
|
|
262
262
|
const t = ext(e);
|
|
263
|
-
if (t.
|
|
264
|
-
t.
|
|
265
|
-
n.
|
|
263
|
+
if (t.He === n) return;
|
|
264
|
+
t.He = n;
|
|
265
|
+
n.ln.push(e);
|
|
266
266
|
}
|
|
267
267
|
|
|
268
268
|
/** Hold the (uncommitted) frame under a held pass: every computed in the
|
|
@@ -278,18 +278,21 @@ function deferZombie(e) {
|
|
|
278
278
|
* next read of it trips over (`laneRead` → `txOf`). Its frame is the lane's
|
|
279
279
|
* too (ruling A: a lane pass's children are the lane's), revealed and
|
|
280
280
|
* retired at the lane's seam. */ function holdFrame(e, n) {
|
|
281
|
-
for (let t = e.
|
|
282
|
-
if (t.m?.
|
|
283
|
-
if (t.
|
|
281
|
+
for (let t = e.Ve; t !== null; t = t.ge) {
|
|
282
|
+
if (t.m?.He?.B) continue;
|
|
283
|
+
if (t.ee !== undefined) {
|
|
284
|
+
if (!(t.i & CONFIG_HELD)) holdNode(t, n);
|
|
285
|
+
t.i |= CONFIG_HELD_FRAME;
|
|
286
|
+
}
|
|
284
287
|
holdFrame(t, n);
|
|
285
288
|
}
|
|
286
289
|
}
|
|
287
290
|
|
|
288
291
|
const dirtyQueue = {
|
|
289
292
|
Ht: new Array(2e3).fill(undefined),
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
+
N: false,
|
|
294
|
+
Ue: 0,
|
|
295
|
+
ft: 0
|
|
293
296
|
};
|
|
294
297
|
|
|
295
298
|
let clock = 0;
|
|
@@ -352,7 +355,7 @@ function schedule() {
|
|
|
352
355
|
}
|
|
353
356
|
if (scheduled) return;
|
|
354
357
|
scheduled = true;
|
|
355
|
-
if (!syncDepth && !globalQueue.
|
|
358
|
+
if (!syncDepth && !globalQueue.X) {
|
|
356
359
|
queueMicrotask(flush);
|
|
357
360
|
}
|
|
358
361
|
}
|
|
@@ -401,11 +404,11 @@ function notifyHalted() {
|
|
|
401
404
|
}
|
|
402
405
|
|
|
403
406
|
class GlobalQueue {
|
|
404
|
-
|
|
405
|
-
|
|
407
|
+
rn=[ [], [] ];
|
|
408
|
+
X=false;
|
|
406
409
|
flush() {
|
|
407
|
-
if (this.
|
|
408
|
-
this.
|
|
410
|
+
if (this.X) return;
|
|
411
|
+
this.X = true;
|
|
409
412
|
// A28: companions of nodes written since the last flush mirror the
|
|
410
413
|
// flushed world — re-synced inside the running window (the write is
|
|
411
414
|
// "flushed" from here on).
|
|
@@ -423,14 +426,14 @@ class GlobalQueue {
|
|
|
423
426
|
clock++;
|
|
424
427
|
// A write the commit sweep staged with no subscriber to dirty is work
|
|
425
428
|
// too — the next round commits it.
|
|
426
|
-
scheduled = dirtyQueue.
|
|
429
|
+
scheduled = dirtyQueue.ft >= dirtyQueue.Ue || pendingNodes.length !== 0;
|
|
427
430
|
this.run(EFFECT_RENDER);
|
|
428
431
|
this.run(EFFECT_USER);
|
|
429
432
|
if (false && holding) ;
|
|
430
433
|
if (false && !scheduled) ;
|
|
431
434
|
if (false) ;
|
|
432
435
|
} finally {
|
|
433
|
-
this.
|
|
436
|
+
this.X = false;
|
|
434
437
|
passTx = null;
|
|
435
438
|
}
|
|
436
439
|
}
|
|
@@ -443,7 +446,7 @@ class GlobalQueue {
|
|
|
443
446
|
// Lanes: an ended action body judges its guesses now that their passes
|
|
444
447
|
// have run (lanes.ts); a correction's re-derivations run in one more
|
|
445
448
|
// pure round — the parent's flights before the parent is judged below.
|
|
446
|
-
if (GlobalQueue.
|
|
449
|
+
if (GlobalQueue.cn?.()) runHeap(dirtyQueue, GlobalQueue.Ce);
|
|
447
450
|
const e = flushTransaction;
|
|
448
451
|
flushTransaction = null;
|
|
449
452
|
// The frame's verdict is in: the guesses written since the last seam
|
|
@@ -451,11 +454,11 @@ class GlobalQueue {
|
|
|
451
454
|
// parents a nested lane to it), or are dropped — the frame commits, and
|
|
452
455
|
// there is nothing to be optimistic over. Their passes run next round;
|
|
453
456
|
// a parent landing at this very seam ends the lane before they do.
|
|
454
|
-
GlobalQueue.
|
|
457
|
+
GlobalQueue.In?.(e);
|
|
455
458
|
// A lane's own flush (its flight landing, its reveal) parks nothing: the
|
|
456
459
|
// lane's work reached it per pass; the frame's plain writes commit.
|
|
457
460
|
// (Resolved: a guess over another transaction's guess merged them.)
|
|
458
|
-
const n = e !== null && !e.
|
|
461
|
+
const n = e !== null && !e.B ? resolveTx(e) : null;
|
|
459
462
|
if (n !== null) {
|
|
460
463
|
// `t._nodes` grows under the walk (holdFrame pushes); the bound is
|
|
461
464
|
// this flush's list, which does not.
|
|
@@ -465,7 +468,7 @@ class GlobalQueue {
|
|
|
465
468
|
// Already held: the pass that re-staged it joined its transaction,
|
|
466
469
|
// which is `t` (or merged into it). Lane work is the lane's (a
|
|
467
470
|
// pending mark propagated onto it queued it here too).
|
|
468
|
-
if (!(e.
|
|
471
|
+
if (!(e.i & (CONFIG_HELD | CONFIG_OVERRIDE)) && (e.m === null || e.m.He === null || !e.m.He.B)) {
|
|
469
472
|
// A loading source — pending with nothing staged and no committed
|
|
470
473
|
// value (A16, A19 exc. 1: uninitialized is loading, not pending)
|
|
471
474
|
// that is not an observer — is nobody's frame: not held. Its
|
|
@@ -474,15 +477,15 @@ class GlobalQueue {
|
|
|
474
477
|
// stale stamp was the bridge that entangled unrelated updates
|
|
475
478
|
// with a never-resolving flight). Its observers are held and
|
|
476
479
|
// wait on its flight (`blocked`).
|
|
477
|
-
if (e.
|
|
480
|
+
if (e.Oe === NOT_PENDING && e.S & STATUS_UNINITIALIZED && e.u !== EFFECT_RENDER) continue;
|
|
478
481
|
// A34 (2): a tick whose writes net to the committed value made no
|
|
479
482
|
// proposal (`setShow(false); setShow(true)` on a committed true) —
|
|
480
483
|
// not staged, not held, pends nothing; a later write to it is
|
|
481
484
|
// plain mainline. (A node already held stays held when written
|
|
482
485
|
// back: A34 (1).)
|
|
483
|
-
if (e.
|
|
484
|
-
e.
|
|
485
|
-
e.
|
|
486
|
+
if (e.Oe !== NOT_PENDING && !(e.S & (STATUS_PENDING | STATUS_UNINITIALIZED)) && e.ye && e.ye(e.Ae, e.Oe) && e.m?.Qe == null && e.m?.ve == null) {
|
|
487
|
+
e.Oe = NOT_PENDING;
|
|
488
|
+
e.i &= ~CONFIG_STAGED;
|
|
486
489
|
continue;
|
|
487
490
|
}
|
|
488
491
|
holdNode(e, n);
|
|
@@ -490,7 +493,7 @@ class GlobalQueue {
|
|
|
490
493
|
// A held pass's frame is held with it (ruling A, O2): the children it
|
|
491
494
|
// created are the transaction's — they compute, and a pass over them
|
|
492
495
|
// or a write that reaches them joins; they effect at the landing.
|
|
493
|
-
if (e.
|
|
496
|
+
if (e.i & CONFIG_STAGED) holdFrame(e, n);
|
|
494
497
|
}
|
|
495
498
|
pendingNodes.length = 0;
|
|
496
499
|
GlobalQueue.Tn?.(n);
|
|
@@ -503,8 +506,8 @@ class GlobalQueue {
|
|
|
503
506
|
// through like the same read outside): it re-runs now, as the
|
|
504
507
|
// holder's verdict lane's work — before the lane seams, which reveal
|
|
505
508
|
// it (nothing commits this seam that could dispose it).
|
|
506
|
-
const t = deferredZombies.splice(0).sort((e, n) => e.
|
|
507
|
-
for (let e = 0; e < t.length; e++) if ((t[e].R & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE)) === REACTIVE_ZOMBIE && t[e].
|
|
509
|
+
const t = deferredZombies.splice(0).sort((e, n) => e.me - n.me);
|
|
510
|
+
for (let e = 0; e < t.length; e++) if ((t[e].R & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE)) === REACTIVE_ZOMBIE && t[e].i & CONFIG_VERDICT) recompute(t[e]);
|
|
508
511
|
// The unchanged passes' tails stay linked (A30, #3469): their inputs
|
|
509
512
|
// are held, and the committed frame still derives from them — no trim.
|
|
510
513
|
} else {
|
|
@@ -516,7 +519,7 @@ class GlobalQueue {
|
|
|
516
519
|
// `recompute`'s own gates.
|
|
517
520
|
for (let e = 0; e < heldTrims.length; e++) {
|
|
518
521
|
const n = heldTrims[e];
|
|
519
|
-
if (n.m?.
|
|
522
|
+
if (n.m?.F == null && !n.Ge) trimStaleDeps(n);
|
|
520
523
|
}
|
|
521
524
|
}
|
|
522
525
|
// The list is the flush's either way. Reset only when there is something
|
|
@@ -530,19 +533,19 @@ class GlobalQueue {
|
|
|
530
533
|
// removes its entry.
|
|
531
534
|
// This flush's own runs, the lanes' reveals, and the landings' held runs
|
|
532
535
|
// are collected apart and ordered below.
|
|
533
|
-
const t = this.
|
|
534
|
-
const l = this.
|
|
536
|
+
const t = this.rn;
|
|
537
|
+
const l = this.rn = [ [], [] ];
|
|
535
538
|
// The passes that read this frame's stagings as the screen read held
|
|
536
539
|
// writes if it parked: the lane seam re-derives them on the committed
|
|
537
540
|
// world (a lane's runs wait this round).
|
|
538
|
-
GlobalQueue.
|
|
541
|
+
GlobalQueue._n?.(n !== null ? stagedReaders : null);
|
|
539
542
|
if (stagedReaders.length) stagedReaders.length = 0;
|
|
540
|
-
this.
|
|
543
|
+
this.rn = [ [], [] ];
|
|
541
544
|
for (let e = transactions.length - 1; e >= 0; e--) {
|
|
542
545
|
const n = transactions[e];
|
|
543
546
|
if (blocked(n)) continue;
|
|
544
547
|
transactions.splice(e, 1);
|
|
545
|
-
GlobalQueue.
|
|
548
|
+
GlobalQueue.Gn?.(n);
|
|
546
549
|
land(n);
|
|
547
550
|
// A landing's commits can dispose a zombie a transaction judged above
|
|
548
551
|
// was blocked on (#3463: live "until the commit that disposes it") —
|
|
@@ -551,12 +554,13 @@ class GlobalQueue {
|
|
|
551
554
|
}
|
|
552
555
|
// The store folds the pending backings whose container nodes committed
|
|
553
556
|
// — this flush's, or a landing's (store/store.ts installs it).
|
|
554
|
-
GlobalQueue.
|
|
557
|
+
GlobalQueue.Sn?.();
|
|
555
558
|
// Marks declared outside a transaction: the flush that carried them
|
|
556
559
|
// parked (something async below) — the window is the frame's, the marks
|
|
557
560
|
// are its transaction's; else they release at the seam, verdict-only,
|
|
558
561
|
// nothing to show (affects.ts).
|
|
559
|
-
|
|
562
|
+
const o = n && liveTx(n);
|
|
563
|
+
GlobalQueue.A?.(o);
|
|
560
564
|
// The effect phase: lane work first (displayed ahead of the frame), then
|
|
561
565
|
// this flush's runs, then the runs the landings held from earlier
|
|
562
566
|
// flushes — as one pass would have queued them (#3540: a shell
|
|
@@ -564,32 +568,31 @@ class GlobalQueue {
|
|
|
564
568
|
// write; #3528: a display-ahead swap before the count it is shown
|
|
565
569
|
// beside). A parked frame's runs wait with its transaction instead: the
|
|
566
570
|
// effect phase applies a committed frame.
|
|
567
|
-
const
|
|
568
|
-
const u = o !== null;
|
|
571
|
+
const u = o !== null;
|
|
569
572
|
for (let e = 0; e < 2; e++) {
|
|
570
|
-
if (u) append(o.
|
|
573
|
+
if (u) append(o.rn[e], t[e]);
|
|
571
574
|
// The plain flush — no reveal, no landing, not parked — has nothing to
|
|
572
575
|
// order around its own runs: they are the queue as they stand (the
|
|
573
576
|
// same array, not a copy; `run` takes it whole). Every other seam
|
|
574
577
|
// builds the ordered queue above.
|
|
575
|
-
this.
|
|
578
|
+
this.rn[e] = u || l[e].length || this.rn[e].length ? l[e].concat(u ? [] : t[e], this.rn[e]) : t[e];
|
|
576
579
|
}
|
|
577
580
|
if (deferredZombies.length !== 0) {
|
|
578
581
|
// Survivors are the displayed frame of a held pass: they rerun for the
|
|
579
582
|
// live write, parents before children.
|
|
580
|
-
const e = deferredZombies.splice(0).sort((e, n) => e.
|
|
583
|
+
const e = deferredZombies.splice(0).sort((e, n) => e.me - n.me);
|
|
581
584
|
for (let n = 0; n < e.length; n++) {
|
|
582
585
|
if (!(e[n].R & REACTIVE_DISPOSED)) recompute(e[n]);
|
|
583
586
|
}
|
|
584
587
|
}
|
|
585
588
|
// Boundaries: readers that settled, landed or died this flush are
|
|
586
589
|
// dropped; a fallback with none left reveals next round.
|
|
587
|
-
GlobalQueue.
|
|
590
|
+
GlobalQueue.se?.();
|
|
588
591
|
}
|
|
589
592
|
run(e) {
|
|
590
|
-
const n = this.
|
|
593
|
+
const n = this.rn[e - 1];
|
|
591
594
|
if (n.length) {
|
|
592
|
-
this.
|
|
595
|
+
this.rn[e - 1] = [];
|
|
593
596
|
runQueue(n, e);
|
|
594
597
|
}
|
|
595
598
|
}
|
|
@@ -598,7 +601,7 @@ class GlobalQueue {
|
|
|
598
601
|
// the frame (which may park). A lane pass that read the frame's own
|
|
599
602
|
// staging is the frame's (`recompute` passes null): its run waits with
|
|
600
603
|
// the frame.
|
|
601
|
-
if (e) (t !== null ? t : this).
|
|
604
|
+
if (e) (t !== null ? t : this).rn[e - 1].push(n);
|
|
602
605
|
schedule();
|
|
603
606
|
}
|
|
604
607
|
/** Status reaching the root: pending is absorbed (the root mount defers —
|
|
@@ -615,7 +618,7 @@ class GlobalQueue {
|
|
|
615
618
|
* A boundary between the observer and the root (boundaries.ts) is asked
|
|
616
619
|
* first: a fallback catches what is not ready under it, and the root
|
|
617
620
|
* never hears of it. */ notify(e, n, t, l) {
|
|
618
|
-
if (GlobalQueue.
|
|
621
|
+
if (GlobalQueue.ie && GlobalQueue.ie(e, t & n, l)) return true;
|
|
619
622
|
if (n & STATUS_PENDING) {
|
|
620
623
|
if (t & STATUS_PENDING) {
|
|
621
624
|
// A pass that went pending holds its frame: the async's own
|
|
@@ -649,32 +652,35 @@ class GlobalQueue {
|
|
|
649
652
|
* for it is void (#3322: an effect has one value slot, and the stale pass
|
|
650
653
|
* overwrote the value that run was for) — the re-derivation's run replaces
|
|
651
654
|
* it. */ function reruns(e) {
|
|
652
|
-
for (let n = 0; n < e.
|
|
653
|
-
const t = e.
|
|
654
|
-
|
|
655
|
-
|
|
655
|
+
for (let n = 0; n < e.fn.length; n++) {
|
|
656
|
+
const t = e.fn[n];
|
|
657
|
+
// An errored reader's display is its error (D-39): the landing gives it
|
|
658
|
+
// nothing to re-derive, and a re-run would be a read that retries the
|
|
659
|
+
// errored source (#3945).
|
|
660
|
+
if (t.R & REACTIVE_DISPOSED || t.S & STATUS_ERROR) continue;
|
|
661
|
+
if (t.R & REACTIVE_FRAME_READ || t.i & CONFIG_VERDICT) {
|
|
656
662
|
t.R &= ~REACTIVE_FRAME_READ;
|
|
657
|
-
t.
|
|
663
|
+
t.Ge = false;
|
|
658
664
|
// Its frame is replaced by the re-derivation: a run one of its
|
|
659
665
|
// children queued this flush (re-run by the same landing) is for the
|
|
660
666
|
// frame being replaced — void too (#3404).
|
|
661
|
-
for (let e = t.
|
|
667
|
+
for (let e = t.Ve; e !== null; e = e.ge) if (e.u) e.Ge = false;
|
|
662
668
|
enqueueSub(t);
|
|
663
669
|
}
|
|
664
670
|
}
|
|
665
|
-
e.
|
|
671
|
+
e.fn.length = 0;
|
|
666
672
|
}
|
|
667
673
|
|
|
668
674
|
/** The landing: the held frame becomes the frame. */ function land(e) {
|
|
669
675
|
reruns(e);
|
|
670
|
-
if (e.T !== null) GlobalQueue.
|
|
676
|
+
if (e.T !== null) GlobalQueue.k(e.T);
|
|
671
677
|
// Old children die in the commits (cleanups first), then the stashed
|
|
672
678
|
// effects run ahead of this flush's own. A node a lane took over since
|
|
673
679
|
// (a guess written over the staged truth) is the lane's to land.
|
|
674
|
-
for (let n = 0; n < e.
|
|
675
|
-
const t = e.
|
|
676
|
-
if (t.m.
|
|
677
|
-
t.m.
|
|
680
|
+
for (let n = 0; n < e.ln.length; n++) {
|
|
681
|
+
const t = e.ln[n];
|
|
682
|
+
if (t.m.He === null || txOf(t) !== e) continue;
|
|
683
|
+
t.m.He = null;
|
|
678
684
|
commitPendingNode(t);
|
|
679
685
|
}
|
|
680
686
|
releaseQueues(e);
|
|
@@ -685,8 +691,8 @@ class GlobalQueue {
|
|
|
685
691
|
* pass would have queued them — a shell re-derived at the landing before a
|
|
686
692
|
* boundary swap held since the write (#3540's log order). */ function releaseQueues(e) {
|
|
687
693
|
for (let n = 0; n < 2; n++) {
|
|
688
|
-
append(globalQueue.
|
|
689
|
-
e.
|
|
694
|
+
append(globalQueue.rn[n], e.rn[n]);
|
|
695
|
+
e.rn[n] = [];
|
|
690
696
|
}
|
|
691
697
|
}
|
|
692
698
|
|
|
@@ -706,13 +712,13 @@ class GlobalQueue {
|
|
|
706
712
|
* after `t`'s landing (`_reruns`). Once per pass. */ function staleReader(e, n) {
|
|
707
713
|
if (!(e.R & REACTIVE_FRAME_READ)) {
|
|
708
714
|
e.R |= REACTIVE_FRAME_READ;
|
|
709
|
-
n.
|
|
715
|
+
n.fn.push(e);
|
|
710
716
|
}
|
|
711
717
|
}
|
|
712
718
|
|
|
713
719
|
function queuePendingNode(e) {
|
|
714
720
|
pendingNodes.push(e);
|
|
715
|
-
if (!globalQueue.
|
|
721
|
+
if (!globalQueue.X) markUnflushedStaged();
|
|
716
722
|
// A28
|
|
717
723
|
}
|
|
718
724
|
|
|
@@ -728,14 +734,14 @@ function bumpNotifyEpoch() {
|
|
|
728
734
|
function insertSubs(e) {
|
|
729
735
|
// §12d: stamp before walking — setSignal's staged-rewrite fast path skips
|
|
730
736
|
// the next walk for this node while the epoch holds (marking is idempotent).
|
|
731
|
-
e.
|
|
737
|
+
e.qe = notifyEpoch;
|
|
732
738
|
// Presence bits gate the optional-slot probes (see constants.ts): one
|
|
733
739
|
// masked read of the always-present _config instead of missing-property
|
|
734
740
|
// lookups in the hottest notify loop.
|
|
735
|
-
const n = e.
|
|
736
|
-
const t = (n & CONFIG_HAS_SNAPSHOT) !== 0 && e.m?.
|
|
737
|
-
for (let n = e.t; n !== null; n = n.
|
|
738
|
-
const e = n.
|
|
741
|
+
const n = e.i;
|
|
742
|
+
const t = (n & CONFIG_HAS_SNAPSHOT) !== 0 && e.m?.Le !== undefined;
|
|
743
|
+
for (let n = e.t; n !== null; n = n.o) {
|
|
744
|
+
const e = n.l;
|
|
739
745
|
// Missed-wake latch (#3037): this write is landing while the subscriber
|
|
740
746
|
// is mid-recompute (a nested pull committing beneath its reads), and the
|
|
741
747
|
// heap refuses RECOMPUTING nodes. A gen-current link means the pass
|
|
@@ -744,8 +750,8 @@ function insertSubs(e) {
|
|
|
744
750
|
// pass either re-reads them fresh or trims them), and neither does the
|
|
745
751
|
// tail link: it is the read IN FLIGHT — read() links before it pulls, so
|
|
746
752
|
// this very commit is what that read returns.
|
|
747
|
-
if (e.R & REACTIVE_RECOMPUTING_DEPS && n.
|
|
748
|
-
if (t && e.
|
|
753
|
+
if (e.R & REACTIVE_RECOMPUTING_DEPS && n.Pe === e.pe && n !== e.G) e.R |= REACTIVE_MISSED_WAKE;
|
|
754
|
+
if (t && e.i & CONFIG_IN_SNAPSHOT_SCOPE) {
|
|
749
755
|
e.R |= REACTIVE_SNAPSHOT_STALE;
|
|
750
756
|
continue;
|
|
751
757
|
}
|
|
@@ -761,9 +767,9 @@ function insertSubs(e) {
|
|
|
761
767
|
* is a live write if the effect becomes a zombie later this flush — an
|
|
762
768
|
* on-screen reader whose removal an adopted write stages follows the lane
|
|
763
769
|
* (the lane-membership ruling, 2026-10-05; `_update`). */ function laneDirty(e, n) {
|
|
764
|
-
for (let t = e.t; t !== null; t = t.
|
|
765
|
-
const e = t.
|
|
766
|
-
if (e?.
|
|
770
|
+
for (let t = e.t; t !== null; t = t.o) {
|
|
771
|
+
const e = t.l.m?.He;
|
|
772
|
+
if (e?.B ? sameLane(e, n) : t.l.u) t.l.R |= REACTIVE_LANE_DIRTY;
|
|
767
773
|
}
|
|
768
774
|
}
|
|
769
775
|
|
|
@@ -778,40 +784,40 @@ function commitPendingNode(e) {
|
|
|
778
784
|
// staged) stays uncommitted: a re-pass disposes its children on the spot,
|
|
779
785
|
// and the frame it parked stays the frame until a pass of this node
|
|
780
786
|
// commits a value.
|
|
781
|
-
if (!n.
|
|
782
|
-
e.
|
|
783
|
-
if (e.
|
|
784
|
-
e.
|
|
785
|
-
e.
|
|
787
|
+
if (!n.ee) {
|
|
788
|
+
e.i &= -385;
|
|
789
|
+
if (e.Oe !== NOT_PENDING) {
|
|
790
|
+
e.Ae = e.Oe;
|
|
791
|
+
e.Oe = NOT_PENDING;
|
|
786
792
|
}
|
|
787
793
|
return;
|
|
788
794
|
}
|
|
789
795
|
// Computeds only from here (`_statusFlags` is not a signal field). A
|
|
790
796
|
// commit beneath a flight publishes its inputs (A15 reveal corollary,
|
|
791
797
|
// #3305): a reveal of it must observe, not show the pre-flight value.
|
|
792
|
-
const t = e.
|
|
793
|
-
e.
|
|
794
|
-
if (e.
|
|
795
|
-
e.
|
|
796
|
-
e.
|
|
798
|
+
const t = e.Oe === NOT_PENDING && (n.S & STATUS_PENDING) !== 0;
|
|
799
|
+
e.i = t ? e.i & -1048833 | CONFIG_INPUTS_PUBLISHED : e.i & -1049985;
|
|
800
|
+
if (e.Oe !== NOT_PENDING) {
|
|
801
|
+
e.Ae = e.Oe;
|
|
802
|
+
e.Oe = NOT_PENDING;
|
|
797
803
|
// A node born staged (recompute) initializes at this commit.
|
|
798
|
-
n.
|
|
804
|
+
n.S &= ~STATUS_UNINITIALIZED;
|
|
799
805
|
// A quiet re-ask's classification survives its landing and dies with
|
|
800
806
|
// the landed value's commit (A19 exc. 2, #3178): verdict-quiet through
|
|
801
807
|
// the reveal. (The sweep of a node still in flight is not a landing.)
|
|
802
808
|
// The A28 stash goes with it.
|
|
803
|
-
if (n.m != null) n.m.
|
|
809
|
+
if (n.m != null) n.m.ke = false, n.m.ze = NOT_PENDING;
|
|
804
810
|
// An effect with a staged value was born held (L2): its first run is
|
|
805
811
|
// this commit's, not its creation's (A29) — queue it now.
|
|
806
|
-
if (e.
|
|
807
|
-
e.
|
|
808
|
-
globalQueue.enqueue(e.
|
|
812
|
+
if (e.u && e.u !== EFFECT_TRACKED) {
|
|
813
|
+
e.Ge = true;
|
|
814
|
+
globalQueue.enqueue(e.u, e.Y ??= GlobalQueue.q.bind(null, n));
|
|
809
815
|
}
|
|
810
816
|
}
|
|
811
817
|
// The committed value is the first observable answer for a loading-window
|
|
812
818
|
// node — the window closes here, not at compute time (#2990). Unconditional
|
|
813
819
|
// store to an always-present computed slot.
|
|
814
|
-
n.
|
|
820
|
+
n.De = false;
|
|
815
821
|
n.R &= ~REACTIVE_MANUAL_WRITE;
|
|
816
822
|
// The dependencies of the pass that produced the value are the frame's now:
|
|
817
823
|
// the previous frame's tail goes (A30, #3410; `recompute` left it for a
|
|
@@ -819,11 +825,11 @@ function commitPendingNode(e) {
|
|
|
819
825
|
// pass or by the node's own landing (whose pass was clean), so a set
|
|
820
826
|
// `_error` means the last pass threw, kept its full list, and `_depsTail`
|
|
821
827
|
// marks where it stopped.
|
|
822
|
-
if (n.m?.
|
|
823
|
-
if (!(n.
|
|
828
|
+
if (n.m?.F == null) trimStaleDeps(n);
|
|
829
|
+
if (!(n.S & STATUS_PENDING)) n.S &= ~STATUS_UNINITIALIZED;
|
|
824
830
|
// L2: the children this commit publishes are the frame's now — the frame
|
|
825
831
|
// they replace, parked by the pass (`recompute`), goes.
|
|
826
|
-
if (!t && n.m != null && (n.m.
|
|
832
|
+
if (!t && n.m != null && (n.m.Qe !== null || n.m.ve !== null)) GlobalQueue.Fe(n, false, true);
|
|
827
833
|
}
|
|
828
834
|
|
|
829
835
|
function commitPendingNodes() {
|
|
@@ -832,7 +838,7 @@ function commitPendingNodes() {
|
|
|
832
838
|
// A node a lane pass re-staged this round is the lane's to reveal; one
|
|
833
839
|
// a transaction holds (born held by a mainline pass, `passTx`) is that
|
|
834
840
|
// transaction's to commit, at its landing.
|
|
835
|
-
if (!(n.
|
|
841
|
+
if (!(n.i & (CONFIG_OVERRIDE | CONFIG_HELD))) commitPendingNode(n);
|
|
836
842
|
}
|
|
837
843
|
pendingNodes.length = 0;
|
|
838
844
|
}
|
|
@@ -857,7 +863,7 @@ function flush(e) {
|
|
|
857
863
|
}
|
|
858
864
|
}
|
|
859
865
|
}
|
|
860
|
-
if (globalQueue.
|
|
866
|
+
if (globalQueue.X) {
|
|
861
867
|
return;
|
|
862
868
|
}
|
|
863
869
|
if (halted) return;
|