@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
|
@@ -18,11 +18,11 @@ import { schedule, dirtyQueue } from "./scheduler.js";
|
|
|
18
18
|
// key off it, and the heap defers a zombie's recompute past the seam (no
|
|
19
19
|
// second heap — the deferral list in scheduler.ts).
|
|
20
20
|
function markDisposal(e) {
|
|
21
|
-
let n = e.
|
|
21
|
+
let n = e.nn;
|
|
22
22
|
while (n) {
|
|
23
23
|
n.R |= REACTIVE_ZOMBIE;
|
|
24
24
|
markDisposal(n);
|
|
25
|
-
n = n.
|
|
25
|
+
n = n.tn;
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -33,7 +33,7 @@ function dispose(e) {
|
|
|
33
33
|
// left queued would be recomputed and resurrected by the next flush (#2983)
|
|
34
34
|
// — dep unlinking, child disposal) is exactly unobserved()'s body; only
|
|
35
35
|
// this flag distinguishes death from dormancy.
|
|
36
|
-
e.
|
|
36
|
+
e.i &= ~CONFIG_AUTO_DISPOSE;
|
|
37
37
|
unobserved(e);
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -50,16 +50,16 @@ function disposeChildren(e, n = false, t) {
|
|
|
50
50
|
e.R = i | REACTIVE_DISPOSED;
|
|
51
51
|
if (e._parent === null && e.Ht) unregisterRoot(e);
|
|
52
52
|
}
|
|
53
|
-
if (n && e.
|
|
54
|
-
if (e.m !== null) e.m.
|
|
53
|
+
if (n && e.ee) {
|
|
54
|
+
if (e.m !== null) e.m.Te = null;
|
|
55
55
|
// A death that can unblock the future (L2, `futureBlocked`): a held
|
|
56
56
|
// flight's — a transaction's or a lane's (its `_x._transaction`) — or a
|
|
57
57
|
// stale reader's (REACTIVE_FRAME_READ — a frame that derived from one).
|
|
58
58
|
// Nothing else re-examines a dead node, so make sure a seam comes.
|
|
59
|
-
if (e.
|
|
59
|
+
if (e.S & STATUS_PENDING && e.m?._t != null || i & REACTIVE_FRAME_READ) schedule();
|
|
60
60
|
}
|
|
61
|
-
let r = t ? e.m.St : e.
|
|
62
|
-
if (t) e.m.St = null; else e.
|
|
61
|
+
let r = t ? e.m.St : e.nn;
|
|
62
|
+
if (t) e.m.St = null; else e.nn = null;
|
|
63
63
|
while (r) {
|
|
64
64
|
const e = r;
|
|
65
65
|
// Owner teardown is death regardless of the child's own lifecycle
|
|
@@ -69,7 +69,7 @@ function disposeChildren(e, n = false, t) {
|
|
|
69
69
|
// disposeChildren call early-returns on REACTIVE_DISPOSED) die too.
|
|
70
70
|
// Only unobserved()'s own node keeps its dormancy — it is never in
|
|
71
71
|
// this loop; its children are rebuilt fresh on reawaken.
|
|
72
|
-
e.
|
|
72
|
+
e.i &= ~CONFIG_AUTO_DISPOSE;
|
|
73
73
|
// Heap removal must not be gated on `_deps`: a dependency-free
|
|
74
74
|
// computation queued by refresh() has a null dep list but still sits in
|
|
75
75
|
// the dirty heap, and left there the post-disposal flush recomputes it —
|
|
@@ -89,9 +89,9 @@ function disposeChildren(e, n = false, t) {
|
|
|
89
89
|
// itself out of the detached chain, and a cleanup may then have linked
|
|
90
90
|
// it at the fresh head, which rewrote the `_nextSibling` a pre-read
|
|
91
91
|
// would still be holding.
|
|
92
|
-
r = r.
|
|
92
|
+
r = r.tn;
|
|
93
93
|
}
|
|
94
|
-
if (!t) e.
|
|
94
|
+
if (!t) e.dt = 0;
|
|
95
95
|
// O(1) splice out of parent's chain on individual dispose. Skipped during
|
|
96
96
|
// batch dispose (parent already disposed) and for a zombie (it sits on the
|
|
97
97
|
// parent's parked frame, drained whole by the commit). We leave
|
|
@@ -99,8 +99,8 @@ function disposeChildren(e, n = false, t) {
|
|
|
99
99
|
// still reach later siblings.
|
|
100
100
|
if (n && !t && !(i & REACTIVE_ZOMBIE) && e._parent !== null && !(e._parent.R & REACTIVE_DISPOSED)) {
|
|
101
101
|
const n = e.Gt;
|
|
102
|
-
const t = e.
|
|
103
|
-
if (n !== null) n.
|
|
102
|
+
const t = e.tn;
|
|
103
|
+
if (n !== null) n.tn = t; else e._parent.nn = t;
|
|
104
104
|
if (t !== null) t.Gt = n;
|
|
105
105
|
e.Gt = null;
|
|
106
106
|
}
|
|
@@ -115,11 +115,11 @@ function disposeChildren(e, n = false, t) {
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
function linkChild(e, n) {
|
|
118
|
-
const t = e.
|
|
118
|
+
const t = e.nn;
|
|
119
119
|
n.Gt = null;
|
|
120
|
-
n.
|
|
120
|
+
n.tn = t;
|
|
121
121
|
if (t !== null) t.Gt = n;
|
|
122
|
-
e.
|
|
122
|
+
e.nn = n;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
function runDisposal(e, n) {
|
|
@@ -129,9 +129,9 @@ function runDisposal(e, n) {
|
|
|
129
129
|
// walk ran every entry a second time. Detached, the re-entrant drain finds
|
|
130
130
|
// nothing. The same shape latched a throwing cleanup: the list survived the
|
|
131
131
|
// throw and every later drain re-ran and re-threw it.
|
|
132
|
-
const t = n ? e.m.ct : e.
|
|
132
|
+
const t = n ? e.m.ct : e.Re;
|
|
133
133
|
if (!t) return;
|
|
134
|
-
if (n) e.m.ct = null; else e.
|
|
134
|
+
if (n) e.m.ct = null; else e.Re = null;
|
|
135
135
|
if (Array.isArray(t)) {
|
|
136
136
|
// Unwind order (#3572, restores 1.x #1562): later registrations run
|
|
137
137
|
// before earlier ones. Children have already been disposed by the caller,
|
|
@@ -148,8 +148,8 @@ function runDisposal(e, n) {
|
|
|
148
148
|
|
|
149
149
|
function childId(e, n) {
|
|
150
150
|
let t = e;
|
|
151
|
-
while (t.
|
|
152
|
-
if (t.id != null) return formatId(t.id, n ? t.
|
|
151
|
+
while (t.i & CONFIG_TRANSPARENT && t._parent) t = t._parent;
|
|
152
|
+
if (t.id != null) return formatId(t.id, n ? t.dt++ : t.dt);
|
|
153
153
|
throw new Error("");
|
|
154
154
|
}
|
|
155
155
|
|
|
@@ -229,7 +229,7 @@ function formatId(e, n) {
|
|
|
229
229
|
* checks. `cleanup()` is the unchecked primitive used by internals.
|
|
230
230
|
*/ function cleanup(e) {
|
|
231
231
|
if (!context) return e;
|
|
232
|
-
if (!context.
|
|
232
|
+
if (!context.Re) context.Re = e; else if (Array.isArray(context.Re)) context.Re.push(e); else context.Re = [ context.Re, e ];
|
|
233
233
|
return e;
|
|
234
234
|
}
|
|
235
235
|
|
|
@@ -272,15 +272,15 @@ function disposeRootSelf(e = true) {
|
|
|
272
272
|
// is a plain store rather than a shape fork between labelled and plain roots.
|
|
273
273
|
const i = {
|
|
274
274
|
id: inheritId(e, t, n),
|
|
275
|
-
|
|
275
|
+
i: t ? CONFIG_TRANSPARENT : 0,
|
|
276
276
|
Ht: true,
|
|
277
277
|
gt: n?.Ht ? n.gt : n,
|
|
278
|
-
|
|
279
|
-
|
|
278
|
+
nn: null,
|
|
279
|
+
tn: null,
|
|
280
280
|
Gt: null,
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
281
|
+
Re: null,
|
|
282
|
+
D: n?.D || defaultContext,
|
|
283
|
+
dt: 0,
|
|
284
284
|
m: null,
|
|
285
285
|
_parent: n,
|
|
286
286
|
dispose: disposeRootSelf,
|
|
@@ -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,12 +19,12 @@ 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
|
-
|
|
22
|
+
Je: [],
|
|
23
23
|
En: [ [], [] ],
|
|
24
24
|
an: [],
|
|
25
|
-
|
|
25
|
+
Sn: null,
|
|
26
26
|
_parent: n,
|
|
27
|
-
|
|
27
|
+
B: e,
|
|
28
28
|
rt: null,
|
|
29
29
|
tt: 0,
|
|
30
30
|
nt: false,
|
|
@@ -78,7 +78,7 @@ 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
|
}
|
|
@@ -95,7 +95,7 @@ function joinPassTx(e, n) {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
function resolveTx(e) {
|
|
98
|
-
while (e.
|
|
98
|
+
while (e.Sn !== null) e = e.Sn;
|
|
99
99
|
return e;
|
|
100
100
|
}
|
|
101
101
|
|
|
@@ -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,12 +116,12 @@ 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
|
if (attrHooks !== null) attrHooks.transitionMerged(n, e);
|
|
121
|
-
e.
|
|
121
|
+
e.Sn = n;
|
|
122
122
|
n.tt += e.tt;
|
|
123
123
|
n.nt ||= e.nt;
|
|
124
|
-
append(n.
|
|
124
|
+
append(n.Je, e.Je);
|
|
125
125
|
append(n.En[0], e.En[0]);
|
|
126
126
|
append(n.En[1], e.En[1]);
|
|
127
127
|
append(n.an, e.an);
|
|
@@ -151,7 +151,7 @@ function resolveTx(e) {
|
|
|
151
151
|
judge ??= e;
|
|
152
152
|
// An action still running in it holds it open (action.ts). Its lanes
|
|
153
153
|
// (lanes.ts): a lane under `t` blocks it while blocked itself.
|
|
154
|
-
const t = e.tt !== 0 || blockedBy(e.
|
|
154
|
+
const t = e.tt !== 0 || blockedBy(e.Je, e) || !!GlobalQueue.dn && GlobalQueue.dn(e);
|
|
155
155
|
judge = n;
|
|
156
156
|
return t;
|
|
157
157
|
}
|
|
@@ -162,9 +162,9 @@ let judge = null;
|
|
|
162
162
|
* frame reader observes (`blockedBy`'s predicate, collected; the readers
|
|
163
163
|
* themselves are not sources). */ function blockersOf(e) {
|
|
164
164
|
const n = [];
|
|
165
|
-
for (let t = 0; t < e.
|
|
166
|
-
const l = e.
|
|
167
|
-
if (!l.
|
|
165
|
+
for (let t = 0; t < e.Je.length; t++) {
|
|
166
|
+
const l = e.Je[t];
|
|
167
|
+
if (!l.u && l.S & STATUS_PENDING && !(l.R & REACTIVE_DISPOSED) && (l.m._t === null || txOf(l) === e) && blockedBy([ l ], e)) n.push(l);
|
|
168
168
|
}
|
|
169
169
|
return n;
|
|
170
170
|
}
|
|
@@ -174,7 +174,7 @@ let judge = null;
|
|
|
174
174
|
* not (lanes.ts, the body-end corollary). A pending reader counts only if
|
|
175
175
|
* one of the sources it waits on is `t`'s (a render effect marked pending
|
|
176
176
|
* by a lane flight, never re-run, is the lane's wait, not the frame's). */ function ownFlights(e) {
|
|
177
|
-
return blockedBy(e.
|
|
177
|
+
return blockedBy(e.Je, e, true);
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
/** A source the pending node waits on that is `t`'s (held by it, or by no
|
|
@@ -192,15 +192,15 @@ let judge = null;
|
|
|
192
192
|
function blockedBy(e, n, t = false) {
|
|
193
193
|
for (let l = 0; l < e.length; l++) {
|
|
194
194
|
const o = e[l];
|
|
195
|
-
if (!(o.
|
|
195
|
+
if (!(o.S & STATUS_PENDING) || o.R & REACTIVE_DISPOSED || o.i & CONFIG_GUESS || o.m._t !== null && txOf(o) !== n || t && !ownSource(o, n)) continue;
|
|
196
196
|
// The frame's own pending observer (off screen: this one holds nothing;
|
|
197
197
|
// the next listed node may).
|
|
198
|
-
if (o.
|
|
198
|
+
if (o.u === EFFECT_RENDER) {
|
|
199
199
|
if (onScreen(o, judge ?? n)) return true;
|
|
200
200
|
continue;
|
|
201
201
|
}
|
|
202
|
-
for (let e = o.t; e !== null; e = e.
|
|
203
|
-
const t = e.
|
|
202
|
+
for (let e = o.t; e !== null; e = e.o) {
|
|
203
|
+
const t = e.l;
|
|
204
204
|
// A frame reader: a render effect, or a stale reader of the flight
|
|
205
205
|
// (REACTIVE_FRAME_READ — a verdict reader, `observeFlight`; lane work,
|
|
206
206
|
// `frameRead`): served committed instead of going pending, it derives
|
|
@@ -212,7 +212,7 @@ function blockedBy(e, n, t = false) {
|
|
|
212
212
|
// lane-membership ruling, 2026-10-05). An output that never committed
|
|
213
213
|
// shows nothing — a mount the frame has not revealed — and holds
|
|
214
214
|
// nothing.
|
|
215
|
-
if ((t.
|
|
215
|
+
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;
|
|
216
216
|
}
|
|
217
217
|
}
|
|
218
218
|
return false;
|
|
@@ -237,7 +237,7 @@ function blockedBy(e, n, t = false) {
|
|
|
237
237
|
* the nearest non-zombie ancestor. */ function removalStagedBy(e, n) {
|
|
238
238
|
let t = e;
|
|
239
239
|
while (t !== null && t.R & REACTIVE_ZOMBIE) t = t._parent;
|
|
240
|
-
return t !== null && (t.
|
|
240
|
+
return t !== null && (t.i & CONFIG_HELD) !== 0 && t.m?._t != null && txOf(t) === resolveTx(n);
|
|
241
241
|
}
|
|
242
242
|
|
|
243
243
|
/** Unchanged passes this flush whose stale dependency tail awaits the
|
|
@@ -264,7 +264,7 @@ function deferZombie(e) {
|
|
|
264
264
|
|
|
265
265
|
/** Put a staged node in a transaction: flagged, stamped, on its landing
|
|
266
266
|
* list. */ function holdNode(e, n) {
|
|
267
|
-
e.
|
|
267
|
+
e.i |= CONFIG_HELD;
|
|
268
268
|
list(e, n);
|
|
269
269
|
}
|
|
270
270
|
|
|
@@ -274,7 +274,7 @@ function deferZombie(e) {
|
|
|
274
274
|
const t = ext(e);
|
|
275
275
|
if (t._t === n) return;
|
|
276
276
|
t._t = n;
|
|
277
|
-
n.
|
|
277
|
+
n.Je.push(e);
|
|
278
278
|
}
|
|
279
279
|
|
|
280
280
|
/** Hold the (uncommitted) frame under a held pass: every computed in the
|
|
@@ -290,16 +290,19 @@ function deferZombie(e) {
|
|
|
290
290
|
* next read of it trips over (`laneRead` → `txOf`). Its frame is the lane's
|
|
291
291
|
* too (ruling A: a lane pass's children are the lane's), revealed and
|
|
292
292
|
* retired at the lane's seam. */ function holdFrame(e, n) {
|
|
293
|
-
for (let t = e.
|
|
294
|
-
if (t.m?._t?.
|
|
295
|
-
if (t.
|
|
293
|
+
for (let t = e.nn; t !== null; t = t.tn) {
|
|
294
|
+
if (t.m?._t?.B) continue;
|
|
295
|
+
if (t.ee !== undefined) {
|
|
296
|
+
if (!(t.i & CONFIG_HELD)) holdNode(t, n);
|
|
297
|
+
t.i |= CONFIG_HELD_FRAME;
|
|
298
|
+
}
|
|
296
299
|
holdFrame(t, n);
|
|
297
300
|
}
|
|
298
301
|
}
|
|
299
302
|
|
|
300
303
|
const dirtyQueue = {
|
|
301
304
|
jt: new Array(2e3).fill(undefined),
|
|
302
|
-
|
|
305
|
+
N: false,
|
|
303
306
|
ut: 0,
|
|
304
307
|
Kt: 0
|
|
305
308
|
};
|
|
@@ -364,7 +367,7 @@ function schedule() {
|
|
|
364
367
|
}
|
|
365
368
|
if (scheduled) return;
|
|
366
369
|
scheduled = true;
|
|
367
|
-
if (!syncDepth && !globalQueue.
|
|
370
|
+
if (!syncDepth && !globalQueue.X) {
|
|
368
371
|
queueMicrotask(flush);
|
|
369
372
|
}
|
|
370
373
|
}
|
|
@@ -414,10 +417,10 @@ function notifyHalted() {
|
|
|
414
417
|
|
|
415
418
|
class GlobalQueue {
|
|
416
419
|
En=[ [], [] ];
|
|
417
|
-
|
|
420
|
+
X=false;
|
|
418
421
|
flush() {
|
|
419
|
-
if (this.
|
|
420
|
-
this.
|
|
422
|
+
if (this.X) return;
|
|
423
|
+
this.X = true;
|
|
421
424
|
// A28: companions of nodes written since the last flush mirror the
|
|
422
425
|
// flushed world — re-synced inside the running window (the write is
|
|
423
426
|
// "flushed" from here on).
|
|
@@ -445,7 +448,7 @@ class GlobalQueue {
|
|
|
445
448
|
if (false && !scheduled) ;
|
|
446
449
|
if (false) ;
|
|
447
450
|
} finally {
|
|
448
|
-
this.
|
|
451
|
+
this.X = false;
|
|
449
452
|
passTx = null;
|
|
450
453
|
}
|
|
451
454
|
}
|
|
@@ -458,7 +461,7 @@ class GlobalQueue {
|
|
|
458
461
|
// Lanes: an ended action body judges its guesses now that their passes
|
|
459
462
|
// have run (lanes.ts); a correction's re-derivations run in one more
|
|
460
463
|
// pure round — the parent's flights before the parent is judged below.
|
|
461
|
-
if (GlobalQueue.
|
|
464
|
+
if (GlobalQueue.On?.()) runHeap(dirtyQueue, GlobalQueue.et);
|
|
462
465
|
const e = flushTransaction;
|
|
463
466
|
flushTransaction = null;
|
|
464
467
|
// The frame's verdict is in: the guesses written since the last seam
|
|
@@ -466,11 +469,11 @@ class GlobalQueue {
|
|
|
466
469
|
// parents a nested lane to it), or are dropped — the frame commits, and
|
|
467
470
|
// there is nothing to be optimistic over. Their passes run next round;
|
|
468
471
|
// a parent landing at this very seam ends the lane before they do.
|
|
469
|
-
GlobalQueue.
|
|
472
|
+
GlobalQueue.Gn?.(e);
|
|
470
473
|
// A lane's own flush (its flight landing, its reveal) parks nothing: the
|
|
471
474
|
// lane's work reached it per pass; the frame's plain writes commit.
|
|
472
475
|
// (Resolved: a guess over another transaction's guess merged them.)
|
|
473
|
-
const n = e !== null && !e.
|
|
476
|
+
const n = e !== null && !e.B ? resolveTx(e) : null;
|
|
474
477
|
if (n !== null) {
|
|
475
478
|
// `t._nodes` grows under the walk (holdFrame pushes); the bound is
|
|
476
479
|
// this flush's list, which does not.
|
|
@@ -480,7 +483,7 @@ class GlobalQueue {
|
|
|
480
483
|
// Already held: the pass that re-staged it joined its transaction,
|
|
481
484
|
// which is `t` (or merged into it). Lane work is the lane's (a
|
|
482
485
|
// pending mark propagated onto it queued it here too).
|
|
483
|
-
if (!(e.
|
|
486
|
+
if (!(e.i & (CONFIG_HELD | CONFIG_OVERRIDE)) && (e.m === null || e.m._t === null || !e.m._t.B)) {
|
|
484
487
|
// A loading source — pending with nothing staged and no committed
|
|
485
488
|
// value (A16, A19 exc. 1: uninitialized is loading, not pending)
|
|
486
489
|
// that is not an observer — is nobody's frame: not held. Its
|
|
@@ -489,15 +492,15 @@ class GlobalQueue {
|
|
|
489
492
|
// stale stamp was the bridge that entangled unrelated updates
|
|
490
493
|
// with a never-resolving flight). Its observers are held and
|
|
491
494
|
// wait on its flight (`blocked`).
|
|
492
|
-
if (e.
|
|
495
|
+
if (e.Oe === NOT_PENDING && e.S & STATUS_UNINITIALIZED && e.u !== EFFECT_RENDER) continue;
|
|
493
496
|
// A34 (2): a tick whose writes net to the committed value made no
|
|
494
497
|
// proposal (`setShow(false); setShow(true)` on a committed true) —
|
|
495
498
|
// not staged, not held, pends nothing; a later write to it is
|
|
496
499
|
// plain mainline. (A node already held stays held when written
|
|
497
500
|
// back: A34 (1).)
|
|
498
|
-
if (e.
|
|
499
|
-
e.
|
|
500
|
-
e.
|
|
501
|
+
if (e.Oe !== NOT_PENDING && !(e.S & (STATUS_PENDING | STATUS_UNINITIALIZED)) && e.en && e.en(e.Ae, e.Oe) && e.m?.St == null && e.m?.ct == null) {
|
|
502
|
+
e.Oe = NOT_PENDING;
|
|
503
|
+
e.i &= ~CONFIG_STAGED;
|
|
501
504
|
continue;
|
|
502
505
|
}
|
|
503
506
|
holdNode(e, n);
|
|
@@ -505,10 +508,10 @@ class GlobalQueue {
|
|
|
505
508
|
// A held pass's frame is held with it (ruling A, O2): the children it
|
|
506
509
|
// created are the transaction's — they compute, and a pass over them
|
|
507
510
|
// or a write that reaches them joins; they effect at the landing.
|
|
508
|
-
if (e.
|
|
511
|
+
if (e.i & CONFIG_STAGED) holdFrame(e, n);
|
|
509
512
|
}
|
|
510
513
|
pendingNodes.length = 0;
|
|
511
|
-
GlobalQueue.
|
|
514
|
+
GlobalQueue.An?.(n);
|
|
512
515
|
// (This flush's runs are stashed with `t` below — after the landings,
|
|
513
516
|
// so a `t` that lands at this very seam runs them first, ahead of
|
|
514
517
|
// the runs it held from earlier flushes.)
|
|
@@ -519,7 +522,7 @@ class GlobalQueue {
|
|
|
519
522
|
// holder's verdict lane's work — before the lane seams, which reveal
|
|
520
523
|
// it (nothing commits this seam that could dispose it).
|
|
521
524
|
const t = deferredZombies.splice(0).sort((e, n) => e.it - n.it);
|
|
522
|
-
for (let e = 0; e < t.length; e++) if ((t[e].R & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE)) === REACTIVE_ZOMBIE && t[e].
|
|
525
|
+
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]);
|
|
523
526
|
// The unchanged passes' tails stay linked (A30, #3469): their inputs
|
|
524
527
|
// are held, and the committed frame still derives from them — no trim.
|
|
525
528
|
} else {
|
|
@@ -531,7 +534,7 @@ class GlobalQueue {
|
|
|
531
534
|
// `recompute`'s own gates.
|
|
532
535
|
for (let e = 0; e < heldTrims.length; e++) {
|
|
533
536
|
const n = heldTrims[e];
|
|
534
|
-
if (n.m?.
|
|
537
|
+
if (n.m?.F == null && !n.lt) trimStaleDeps(n);
|
|
535
538
|
}
|
|
536
539
|
}
|
|
537
540
|
// The list is the flush's either way. Reset only when there is something
|
|
@@ -550,14 +553,14 @@ class GlobalQueue {
|
|
|
550
553
|
// The passes that read this frame's stagings as the screen read held
|
|
551
554
|
// writes if it parked: the lane seam re-derives them on the committed
|
|
552
555
|
// world (a lane's runs wait this round).
|
|
553
|
-
GlobalQueue.
|
|
556
|
+
GlobalQueue.cn?.(n !== null ? stagedReaders : null);
|
|
554
557
|
if (stagedReaders.length) stagedReaders.length = 0;
|
|
555
558
|
this.En = [ [], [] ];
|
|
556
559
|
for (let e = transactions.length - 1; e >= 0; e--) {
|
|
557
560
|
const n = transactions[e];
|
|
558
561
|
if (blocked(n)) continue;
|
|
559
562
|
transactions.splice(e, 1);
|
|
560
|
-
GlobalQueue.
|
|
563
|
+
GlobalQueue.Tn?.(n);
|
|
561
564
|
land(n);
|
|
562
565
|
// A landing's commits can dispose a zombie a transaction judged above
|
|
563
566
|
// was blocked on (#3463: live "until the commit that disposes it") —
|
|
@@ -566,12 +569,13 @@ class GlobalQueue {
|
|
|
566
569
|
}
|
|
567
570
|
// The store folds the pending backings whose container nodes committed
|
|
568
571
|
// — this flush's, or a landing's (store/store.ts installs it).
|
|
569
|
-
GlobalQueue.
|
|
572
|
+
GlobalQueue.Rn?.();
|
|
570
573
|
// Marks declared outside a transaction: the flush that carried them
|
|
571
574
|
// parked (something async below) — the window is the frame's, the marks
|
|
572
575
|
// are its transaction's; else they release at the seam, verdict-only,
|
|
573
576
|
// nothing to show (affects.ts).
|
|
574
|
-
|
|
577
|
+
const o = n && liveTx(n);
|
|
578
|
+
GlobalQueue.A?.(o);
|
|
575
579
|
// The effect phase: lane work first (displayed ahead of the frame), then
|
|
576
580
|
// this flush's runs, then the runs the landings held from earlier
|
|
577
581
|
// flushes — as one pass would have queued them (#3540: a shell
|
|
@@ -579,8 +583,7 @@ class GlobalQueue {
|
|
|
579
583
|
// write; #3528: a display-ahead swap before the count it is shown
|
|
580
584
|
// beside). A parked frame's runs wait with its transaction instead: the
|
|
581
585
|
// effect phase applies a committed frame.
|
|
582
|
-
const
|
|
583
|
-
const u = o !== null;
|
|
586
|
+
const u = o !== null;
|
|
584
587
|
// Attribution hook: this flush found its transaction incomplete — its
|
|
585
588
|
// writes stay staged, its runs are stashed below. Before the effect
|
|
586
589
|
// phase (the lanes' display-ahead runs are the visible acknowledgers);
|
|
@@ -607,7 +610,7 @@ class GlobalQueue {
|
|
|
607
610
|
}
|
|
608
611
|
// Boundaries: readers that settled, landed or died this flush are
|
|
609
612
|
// dropped; a fallback with none left reveals next round.
|
|
610
|
-
GlobalQueue.
|
|
613
|
+
GlobalQueue.ce?.();
|
|
611
614
|
}
|
|
612
615
|
run(e) {
|
|
613
616
|
const n = this.En[e - 1];
|
|
@@ -638,7 +641,7 @@ class GlobalQueue {
|
|
|
638
641
|
* A boundary between the observer and the root (boundaries.ts) is asked
|
|
639
642
|
* first: a fallback catches what is not ready under it, and the root
|
|
640
643
|
* never hears of it. */ notify(e, n, t, l) {
|
|
641
|
-
if (GlobalQueue.
|
|
644
|
+
if (GlobalQueue.ie && GlobalQueue.ie(e, t & n, l)) return true;
|
|
642
645
|
if (n & STATUS_PENDING) {
|
|
643
646
|
if (t & STATUS_PENDING) {
|
|
644
647
|
// A pass that went pending holds its frame: the async's own
|
|
@@ -674,14 +677,17 @@ class GlobalQueue {
|
|
|
674
677
|
* it. */ function reruns(e) {
|
|
675
678
|
for (let n = 0; n < e.an.length; n++) {
|
|
676
679
|
const t = e.an[n];
|
|
677
|
-
|
|
678
|
-
|
|
680
|
+
// An errored reader's display is its error (D-39): the landing gives it
|
|
681
|
+
// nothing to re-derive, and a re-run would be a read that retries the
|
|
682
|
+
// errored source (#3945).
|
|
683
|
+
if (t.R & REACTIVE_DISPOSED || t.S & STATUS_ERROR) continue;
|
|
684
|
+
if (t.R & REACTIVE_FRAME_READ || t.i & CONFIG_VERDICT) {
|
|
679
685
|
t.R &= ~REACTIVE_FRAME_READ;
|
|
680
686
|
t.lt = false;
|
|
681
687
|
// Its frame is replaced by the re-derivation: a run one of its
|
|
682
688
|
// children queued this flush (re-run by the same landing) is for the
|
|
683
689
|
// frame being replaced — void too (#3404).
|
|
684
|
-
for (let e = t.
|
|
690
|
+
for (let e = t.nn; e !== null; e = e.tn) if (e.u) e.lt = false;
|
|
685
691
|
enqueueSub(t);
|
|
686
692
|
}
|
|
687
693
|
}
|
|
@@ -693,12 +699,12 @@ class GlobalQueue {
|
|
|
693
699
|
// (`_nodes` still lists them).
|
|
694
700
|
if (attrHooks !== null) attrHooks.transitionSettled(e);
|
|
695
701
|
reruns(e);
|
|
696
|
-
if (e.T !== null) GlobalQueue.
|
|
702
|
+
if (e.T !== null) GlobalQueue.k(e.T);
|
|
697
703
|
// Old children die in the commits (cleanups first), then the stashed
|
|
698
704
|
// effects run ahead of this flush's own. A node a lane took over since
|
|
699
705
|
// (a guess written over the staged truth) is the lane's to land.
|
|
700
|
-
for (let n = 0; n < e.
|
|
701
|
-
const t = e.
|
|
706
|
+
for (let n = 0; n < e.Je.length; n++) {
|
|
707
|
+
const t = e.Je[n];
|
|
702
708
|
if (t.m._t === null || txOf(t) !== e) continue;
|
|
703
709
|
t.m._t = null;
|
|
704
710
|
commitPendingNode(t);
|
|
@@ -738,7 +744,7 @@ class GlobalQueue {
|
|
|
738
744
|
|
|
739
745
|
function queuePendingNode(e) {
|
|
740
746
|
pendingNodes.push(e);
|
|
741
|
-
if (!globalQueue.
|
|
747
|
+
if (!globalQueue.X) markUnflushedStaged();
|
|
742
748
|
// A28
|
|
743
749
|
}
|
|
744
750
|
|
|
@@ -758,13 +764,13 @@ function insertSubs(e) {
|
|
|
758
764
|
// Presence bits gate the optional-slot probes (see constants.ts): one
|
|
759
765
|
// masked read of the always-present _config instead of missing-property
|
|
760
766
|
// lookups in the hottest notify loop.
|
|
761
|
-
const n = e.
|
|
767
|
+
const n = e.i;
|
|
762
768
|
const t = (n & CONFIG_HAS_SNAPSHOT) !== 0 && e.m?.ot !== undefined;
|
|
763
769
|
// Observe-tier fan-out: this walk visits every subscriber edge anyway, so
|
|
764
770
|
// the graph-size count is one local increment here and no field anywhere.
|
|
765
771
|
let l = 0;
|
|
766
|
-
for (let n = e.t; n !== null; n = n.
|
|
767
|
-
const e = n.
|
|
772
|
+
for (let n = e.t; n !== null; n = n.o) {
|
|
773
|
+
const e = n.l;
|
|
768
774
|
l++;
|
|
769
775
|
// Missed-wake latch (#3037): this write is landing while the subscriber
|
|
770
776
|
// is mid-recompute (a nested pull committing beneath its reads), and the
|
|
@@ -774,8 +780,8 @@ function insertSubs(e) {
|
|
|
774
780
|
// pass either re-reads them fresh or trims them), and neither does the
|
|
775
781
|
// tail link: it is the read IN FLIGHT — read() links before it pulls, so
|
|
776
782
|
// this very commit is what that read returns.
|
|
777
|
-
if (e.R & REACTIVE_RECOMPUTING_DEPS && n.
|
|
778
|
-
if (t && e.
|
|
783
|
+
if (e.R & REACTIVE_RECOMPUTING_DEPS && n.Pe === e.pe && n !== e.p) e.R |= REACTIVE_MISSED_WAKE;
|
|
784
|
+
if (t && e.i & CONFIG_IN_SNAPSHOT_SCOPE) {
|
|
779
785
|
e.R |= REACTIVE_SNAPSHOT_STALE;
|
|
780
786
|
continue;
|
|
781
787
|
}
|
|
@@ -792,9 +798,9 @@ function insertSubs(e) {
|
|
|
792
798
|
* is a live write if the effect becomes a zombie later this flush — an
|
|
793
799
|
* on-screen reader whose removal an adopted write stages follows the lane
|
|
794
800
|
* (the lane-membership ruling, 2026-10-05; `_update`). */ function laneDirty(e, n) {
|
|
795
|
-
for (let t = e.t; t !== null; t = t.
|
|
796
|
-
const e = t.
|
|
797
|
-
if (e?.
|
|
801
|
+
for (let t = e.t; t !== null; t = t.o) {
|
|
802
|
+
const e = t.l.m?._t;
|
|
803
|
+
if (e?.B ? sameLane(e, n) : t.l.u) t.l.R |= REACTIVE_LANE_DIRTY;
|
|
798
804
|
}
|
|
799
805
|
}
|
|
800
806
|
|
|
@@ -809,24 +815,24 @@ function commitPendingNode(e) {
|
|
|
809
815
|
// staged) stays uncommitted: a re-pass disposes its children on the spot,
|
|
810
816
|
// and the frame it parked stays the frame until a pass of this node
|
|
811
817
|
// commits a value.
|
|
812
|
-
if (!n.
|
|
813
|
-
e.
|
|
814
|
-
if (e.
|
|
815
|
-
e.
|
|
816
|
-
e.
|
|
818
|
+
if (!n.ee) {
|
|
819
|
+
e.i &= -385;
|
|
820
|
+
if (e.Oe !== NOT_PENDING) {
|
|
821
|
+
e.Ae = e.Oe;
|
|
822
|
+
e.Oe = NOT_PENDING;
|
|
817
823
|
}
|
|
818
824
|
return;
|
|
819
825
|
}
|
|
820
826
|
// Computeds only from here (`_statusFlags` is not a signal field). A
|
|
821
827
|
// commit beneath a flight publishes its inputs (A15 reveal corollary,
|
|
822
828
|
// #3305): a reveal of it must observe, not show the pre-flight value.
|
|
823
|
-
const t = e.
|
|
824
|
-
e.
|
|
825
|
-
if (e.
|
|
826
|
-
e.
|
|
827
|
-
e.
|
|
829
|
+
const t = e.Oe === NOT_PENDING && (n.S & STATUS_PENDING) !== 0;
|
|
830
|
+
e.i = t ? e.i & -1048833 | CONFIG_INPUTS_PUBLISHED : e.i & -1049985;
|
|
831
|
+
if (e.Oe !== NOT_PENDING) {
|
|
832
|
+
e.Ae = e.Oe;
|
|
833
|
+
e.Oe = NOT_PENDING;
|
|
828
834
|
// A node born staged (recompute) initializes at this commit.
|
|
829
|
-
n.
|
|
835
|
+
n.S &= ~STATUS_UNINITIALIZED;
|
|
830
836
|
// A quiet re-ask's classification survives its landing and dies with
|
|
831
837
|
// the landed value's commit (A19 exc. 2, #3178): verdict-quiet through
|
|
832
838
|
// the reveal. (The sweep of a node still in flight is not a landing.)
|
|
@@ -834,15 +840,15 @@ function commitPendingNode(e) {
|
|
|
834
840
|
if (n.m != null) n.m.It = false, n.m.ht = NOT_PENDING;
|
|
835
841
|
// An effect with a staged value was born held (L2): its first run is
|
|
836
842
|
// this commit's, not its creation's (A29) — queue it now.
|
|
837
|
-
if (e.
|
|
843
|
+
if (e.u && e.u !== EFFECT_TRACKED) {
|
|
838
844
|
e.lt = true;
|
|
839
|
-
globalQueue.enqueue(e.
|
|
845
|
+
globalQueue.enqueue(e.u, e.Y ??= GlobalQueue.q.bind(null, n));
|
|
840
846
|
}
|
|
841
847
|
}
|
|
842
848
|
// The committed value is the first observable answer for a loading-window
|
|
843
849
|
// node — the window closes here, not at compute time (#2990). Unconditional
|
|
844
850
|
// store to an always-present computed slot.
|
|
845
|
-
n.
|
|
851
|
+
n.De = false;
|
|
846
852
|
n.R &= ~REACTIVE_MANUAL_WRITE;
|
|
847
853
|
// The dependencies of the pass that produced the value are the frame's now:
|
|
848
854
|
// the previous frame's tail goes (A30, #3410; `recompute` left it for a
|
|
@@ -850,11 +856,11 @@ function commitPendingNode(e) {
|
|
|
850
856
|
// pass or by the node's own landing (whose pass was clean), so a set
|
|
851
857
|
// `_error` means the last pass threw, kept its full list, and `_depsTail`
|
|
852
858
|
// marks where it stopped.
|
|
853
|
-
if (n.m?.
|
|
854
|
-
if (!(n.
|
|
859
|
+
if (n.m?.F == null) trimStaleDeps(n);
|
|
860
|
+
if (!(n.S & STATUS_PENDING)) n.S &= ~STATUS_UNINITIALIZED;
|
|
855
861
|
// L2: the children this commit publishes are the frame's now — the frame
|
|
856
862
|
// they replace, parked by the pass (`recompute`), goes.
|
|
857
|
-
if (!t && n.m != null && (n.m.St !== null || n.m.ct !== null)) GlobalQueue.
|
|
863
|
+
if (!t && n.m != null && (n.m.St !== null || n.m.ct !== null)) GlobalQueue.Et(n, false, true);
|
|
858
864
|
}
|
|
859
865
|
|
|
860
866
|
function commitPendingNodes() {
|
|
@@ -863,7 +869,7 @@ function commitPendingNodes() {
|
|
|
863
869
|
// A node a lane pass re-staged this round is the lane's to reveal; one
|
|
864
870
|
// a transaction holds (born held by a mainline pass, `passTx`) is that
|
|
865
871
|
// transaction's to commit, at its landing.
|
|
866
|
-
if (!(n.
|
|
872
|
+
if (!(n.i & (CONFIG_OVERRIDE | CONFIG_HELD))) commitPendingNode(n);
|
|
867
873
|
}
|
|
868
874
|
pendingNodes.length = 0;
|
|
869
875
|
}
|
|
@@ -888,7 +894,7 @@ function flush(e) {
|
|
|
888
894
|
}
|
|
889
895
|
}
|
|
890
896
|
}
|
|
891
|
-
if (globalQueue.
|
|
897
|
+
if (globalQueue.X) {
|
|
892
898
|
return;
|
|
893
899
|
}
|
|
894
900
|
if (halted) return;
|