@solidjs/signals 2.0.0-rc.14 → 2.0.0-rc.16
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 +882 -405
- package/dist/dev.attribution.js +172 -66
- package/dist/dev.js +1529 -745
- package/dist/observe/affects-marks.js +86 -0
- package/dist/observe/affects.js +32 -80
- package/dist/observe/boundaries.js +202 -166
- package/dist/observe/core/async.js +192 -141
- package/dist/observe/core/attribution-queries.js +1 -1
- package/dist/observe/core/attribution.js +315 -214
- package/dist/observe/core/constants.js +23 -4
- package/dist/observe/core/context.js +3 -3
- package/dist/observe/core/core.js +532 -360
- package/dist/observe/core/dev.js +63 -35
- package/dist/observe/core/effect.js +47 -47
- package/dist/observe/core/error.js +5 -3
- package/dist/observe/core/external.js +5 -6
- package/dist/observe/core/graph.js +56 -42
- package/dist/observe/core/heap.js +33 -33
- package/dist/observe/core/identities.js +62 -0
- package/dist/observe/core/lanes.js +242 -229
- package/dist/observe/core/owner.js +62 -50
- package/dist/observe/core/scheduler.js +322 -168
- package/dist/observe/core/truth.js +45 -0
- package/dist/observe/core/verdict.js +114 -64
- package/dist/observe/deferred.js +184 -0
- package/dist/observe/index.js +7 -1
- package/dist/observe/map.js +339 -254
- package/dist/observe/reveal.js +77 -77
- package/dist/observe/signals.js +54 -33
- package/dist/observe/store/affects.js +76 -69
- package/dist/observe/store/edges.js +39 -0
- package/dist/observe/store/hooks.js +13 -0
- package/dist/observe/store/index.js +0 -2
- package/dist/observe/store/optimistic.js +82 -44
- package/dist/observe/store/projection.js +83 -64
- package/dist/observe/store/reconcile.js +32 -30
- package/dist/observe/store/store.js +219 -123
- package/dist/prod/affects-marks.js +86 -0
- package/dist/prod/affects.js +33 -79
- package/dist/prod/boundaries.js +185 -149
- package/dist/prod/core/async.js +176 -135
- package/dist/prod/core/constants.js +23 -4
- package/dist/prod/core/context.js +3 -3
- package/dist/prod/core/core.js +521 -353
- package/dist/prod/core/effect.js +66 -66
- package/dist/prod/core/error.js +5 -3
- package/dist/prod/core/external.js +5 -6
- package/dist/prod/core/graph.js +56 -42
- package/dist/prod/core/heap.js +38 -38
- package/dist/prod/core/identities.js +62 -0
- package/dist/prod/core/lanes.js +237 -224
- package/dist/prod/core/owner.js +62 -51
- package/dist/prod/core/scheduler.js +312 -158
- package/dist/prod/core/truth.js +45 -0
- package/dist/prod/core/verdict.js +113 -63
- package/dist/prod/deferred.js +182 -0
- package/dist/prod/index.js +7 -1
- package/dist/prod/map.js +366 -281
- package/dist/prod/reveal.js +77 -77
- package/dist/prod/signals.js +59 -38
- package/dist/prod/store/affects.js +76 -69
- package/dist/prod/store/edges.js +39 -0
- package/dist/prod/store/hooks.js +13 -0
- package/dist/prod/store/index.js +0 -2
- package/dist/prod/store/optimistic.js +121 -84
- package/dist/prod/store/projection.js +46 -34
- package/dist/prod/store/reconcile.js +32 -30
- package/dist/prod/store/store.js +205 -116
- package/dist/types/affects-marks.d.ts +14 -0
- package/dist/types/boundaries.d.ts +5 -1
- package/dist/types/core/async.d.ts +10 -0
- package/dist/types/core/attribution.d.ts +6 -2
- package/dist/types/core/constants.d.ts +18 -1
- package/dist/types/core/core.d.ts +77 -5
- package/dist/types/core/dev.d.ts +43 -13
- package/dist/types/core/graph.d.ts +11 -0
- package/dist/types/core/identities.d.ts +17 -0
- package/dist/types/core/index.d.ts +2 -1
- package/dist/types/core/lanes.d.ts +2 -4
- package/dist/types/core/owner.d.ts +5 -3
- package/dist/types/core/scheduler.d.ts +59 -34
- package/dist/types/core/truth.d.ts +16 -0
- package/dist/types/core/types.d.ts +21 -6
- package/dist/types/core/verdict.d.ts +32 -0
- package/dist/types/deferred.d.ts +28 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/map.d.ts +2 -2
- package/dist/types/signals.d.ts +28 -7
- package/dist/types/store/affects.d.ts +11 -0
- package/dist/types/store/edges.d.ts +9 -0
- package/dist/types/store/hooks.d.ts +48 -0
- package/dist/types/store/index.d.ts +10 -4
- package/dist/types/store/optimistic.d.ts +3 -2
- package/dist/types/store/projection.d.ts +4 -3
- package/dist/types/store/store.d.ts +4 -39
- package/dist/types/store/target.d.ts +16 -8
- package/dist/types/store/types.d.ts +14 -0
- package/package.json +1 -1
|
@@ -1,8 +1,10 @@
|
|
|
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_STAGED_ERROR, CONFIG_INPUTS_PUBLISHED, STATUS_ERROR, EFFECT_TRACKED, REACTIVE_MANUAL_WRITE, CONFIG_GUESS, REACTIVE_FRAME_READ, CONFIG_REDERIVE } from "./constants.js";
|
|
2
2
|
|
|
3
3
|
import { attrHooks } from "./attribution-hooks.js";
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { notifyStatus } from "./async.js";
|
|
6
|
+
|
|
7
|
+
import { resyncUnflushedCompanions, isSame, recompute, markUnflushedStaged, ext } from "./core.js";
|
|
6
8
|
|
|
7
9
|
import { DEV, GRAPH_SIZE_WARN_AT, noteFanOut } from "./dev.js";
|
|
8
10
|
|
|
@@ -12,29 +14,136 @@ import { runHeap, enqueueSub, deleteFromHeap } from "./heap.js";
|
|
|
12
14
|
|
|
13
15
|
import { devCheckQuiescent } from "./invariants.js";
|
|
14
16
|
|
|
17
|
+
// Optional-module hooks: module bindings, not `GlobalQueue` statics, so a
|
|
18
|
+
// bundle that never imports the installing module never assigns them — its
|
|
19
|
+
// minifier folds every guarded call site (and the branch behind it) away.
|
|
20
|
+
// Each module installs its own set at evaluation (pay-for-use).
|
|
21
|
+
// Lanes (lanes.ts; installed when `createOptimistic` — or verdict.ts — is
|
|
22
|
+
// imported; every call site is behind a bit or a `passLane` nothing else sets).
|
|
23
|
+
let laneReadHook = null;
|
|
24
|
+
|
|
25
|
+
let laneStageHook = null;
|
|
26
|
+
|
|
27
|
+
let laneOutcomeHook = null;
|
|
28
|
+
|
|
29
|
+
let laneWriteHook = null;
|
|
30
|
+
|
|
31
|
+
let applyGuessesHook = null;
|
|
32
|
+
|
|
33
|
+
let laneSeamsHook = null;
|
|
34
|
+
|
|
35
|
+
let laneCorrectionsHook = null;
|
|
36
|
+
|
|
37
|
+
let endLanesHook = null;
|
|
38
|
+
|
|
39
|
+
let lanesBlockedHook = null;
|
|
40
|
+
|
|
41
|
+
function installLaneHooks(e, n, o, t, l, s, u, i, r) {
|
|
42
|
+
laneReadHook = e;
|
|
43
|
+
laneStageHook = n;
|
|
44
|
+
laneOutcomeHook = o;
|
|
45
|
+
laneWriteHook = t;
|
|
46
|
+
applyGuessesHook = l;
|
|
47
|
+
laneSeamsHook = s;
|
|
48
|
+
laneCorrectionsHook = u;
|
|
49
|
+
endLanesHook = i;
|
|
50
|
+
lanesBlockedHook = r;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Boundaries (boundaries.ts): the display consumers between an observer
|
|
54
|
+
// and the root. `catch` — status from a frame reader, nearest boundary
|
|
55
|
+
// first (true: caught, the root never hears of it; a clear — flags 0 —
|
|
56
|
+
// settles the reader there); `fresh` — a pass that read a hold (true: a
|
|
57
|
+
// first pass a loading boundary that has not shown content caught,
|
|
58
|
+
// `joinPass`; `held`: the flush's hold owns it, nothing catches it);
|
|
59
|
+
// `hidden` — a frame reader behind a fallback (the boundary showing it)
|
|
60
|
+
// is not on screen and holds nothing; `boundarySeam` — the seam's sweep (readers
|
|
61
|
+
// gone or settled without a pass reveal; an `on` re-arm resolves);
|
|
62
|
+
// `heldRun` — a queued run under a fallback-showing boundary waits for
|
|
63
|
+
// the reveal (true: held; the boundary re-queues it by type). The
|
|
64
|
+
// synchronous first render on creation builds the subtree, attached or
|
|
65
|
+
// not; its updates and the user effects wait.
|
|
66
|
+
let catchHook = null;
|
|
67
|
+
|
|
68
|
+
let freshHook = null;
|
|
69
|
+
|
|
70
|
+
let hiddenHook = null;
|
|
71
|
+
|
|
72
|
+
let boundarySeamHook = null;
|
|
73
|
+
|
|
74
|
+
let heldRunHook = null;
|
|
75
|
+
|
|
76
|
+
function installBoundaryHooks(e, n, o, t, l) {
|
|
77
|
+
catchHook = e;
|
|
78
|
+
freshHook = n;
|
|
79
|
+
hiddenHook = o;
|
|
80
|
+
boundarySeamHook = t;
|
|
81
|
+
heldRunHook = l;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Store (store/store.ts): `park` — the flush parked into `t`: the store
|
|
85
|
+
* materializes and holds the container node of every staging that had none
|
|
86
|
+
* (a staging needs the node as its home only when something can read the
|
|
87
|
+
* frame through it; a hold can); `commit` — fold the pending backings whose
|
|
88
|
+
* container nodes this flush committed (the owned-raw model's one mutation
|
|
89
|
+
* point). */ let storeParkHook = null;
|
|
90
|
+
|
|
91
|
+
let storeCommitHook = null;
|
|
92
|
+
|
|
93
|
+
function installStoreHooks(e, n) {
|
|
94
|
+
storeParkHook = e;
|
|
95
|
+
storeCommitHook = n;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** `affects()` marks (affects.ts): the releases at a landing and at the seam
|
|
99
|
+
* (ambient marks). */ let releaseMarksHook = null;
|
|
100
|
+
|
|
101
|
+
let releaseAmbientMarksHook = null;
|
|
102
|
+
|
|
103
|
+
function installMarkHooks(e, n) {
|
|
104
|
+
releaseMarksHook = e;
|
|
105
|
+
releaseAmbientMarksHook = n;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Verdicts (verdict.ts). */ let observeFlightHook = null;
|
|
109
|
+
|
|
110
|
+
function installVerdictHooks(e) {
|
|
111
|
+
observeFlightHook = e;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** External-source bridge (wired by enableExternalSource(); undefined while
|
|
115
|
+
* no config is active — including after _resetExternalSourceConfig()). */ let wireExternalSourceHook = undefined;
|
|
116
|
+
|
|
117
|
+
let externalUntrackHook = undefined;
|
|
118
|
+
|
|
119
|
+
function setExternalHooks(e, n) {
|
|
120
|
+
wireExternalSourceHook = e;
|
|
121
|
+
externalUntrackHook = n;
|
|
122
|
+
}
|
|
123
|
+
|
|
15
124
|
/** Live transactions — opened, unmerged, not landed. Scanned at every seam
|
|
16
125
|
* while non-empty; nothing on the plain path. */ const transactions = [];
|
|
17
126
|
|
|
18
127
|
/** Observe tier: this flush parked its transaction (`holdStart` fired). */ let holding = false;
|
|
19
128
|
|
|
20
129
|
/** A transaction (listed here) or a lane (lanes.ts lists its own). */ function newTransaction(e, n = null) {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
130
|
+
const o = {
|
|
131
|
+
je: [],
|
|
132
|
+
un: [ [], [] ],
|
|
133
|
+
En: [],
|
|
134
|
+
cn: null,
|
|
26
135
|
_parent: n,
|
|
27
|
-
|
|
28
|
-
|
|
136
|
+
V: e,
|
|
137
|
+
it: null,
|
|
29
138
|
tt: 0,
|
|
30
139
|
nt: false,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
140
|
+
nn: null,
|
|
141
|
+
It: false,
|
|
142
|
+
ln: false,
|
|
143
|
+
I: null
|
|
35
144
|
};
|
|
36
|
-
if (!e) transactions.push(
|
|
37
|
-
return
|
|
145
|
+
if (!e) transactions.push(o);
|
|
146
|
+
return o;
|
|
38
147
|
}
|
|
39
148
|
|
|
40
149
|
// Lanes (plan sec. 28, 2026-10-02; lanes.ts). A lane is "a new base of a transition":
|
|
@@ -78,14 +187,14 @@ function setPassLane(e) {
|
|
|
78
187
|
* set it. */ let passTx = null;
|
|
79
188
|
|
|
80
189
|
function joinPassTx(e, n) {
|
|
81
|
-
if (globalQueue.
|
|
190
|
+
if (globalQueue.q && !n || flushTransaction !== null) return joinFuture(e);
|
|
82
191
|
if (passTx === null) passTx = resolveTx(e); else merge(resolveTx(e), passTx);
|
|
83
192
|
schedule();
|
|
84
193
|
}
|
|
85
194
|
|
|
86
195
|
/** The transaction holding a node (CONFIG_HELD set), resolved through
|
|
87
196
|
* merges; the path is compressed. */ function txOf(e) {
|
|
88
|
-
return e.
|
|
197
|
+
return e.T.Je = resolveTx(e.T.Je);
|
|
89
198
|
}
|
|
90
199
|
|
|
91
200
|
/** `t` resolved through merges while it is still parked or open; null once
|
|
@@ -95,7 +204,7 @@ function joinPassTx(e, n) {
|
|
|
95
204
|
}
|
|
96
205
|
|
|
97
206
|
function resolveTx(e) {
|
|
98
|
-
while (e.
|
|
207
|
+
while (e.cn !== null) e = e.cn;
|
|
99
208
|
return e;
|
|
100
209
|
}
|
|
101
210
|
|
|
@@ -106,7 +215,7 @@ function resolveTx(e) {
|
|
|
106
215
|
if (n === null) {
|
|
107
216
|
flushTransaction = e === null ? newTransaction(false) : resolveTx(e);
|
|
108
217
|
} else if (e !== null) merge(resolveTx(e), n);
|
|
109
|
-
if (!globalQueue.
|
|
218
|
+
if (!globalQueue.q) schedule();
|
|
110
219
|
}
|
|
111
220
|
|
|
112
221
|
/** `t` merges into `f`: the work that touched both entangles them (A15).
|
|
@@ -116,16 +225,16 @@ function resolveTx(e) {
|
|
|
116
225
|
// into the other would close a cycle `resolveTx` never leaves.
|
|
117
226
|
e = resolveTx(e);
|
|
118
227
|
n = resolveTx(n);
|
|
119
|
-
if (e === n || e.
|
|
228
|
+
if (e === n || e.V || n.V) return;
|
|
120
229
|
if (attrHooks !== null) attrHooks.transitionMerged(n, e);
|
|
121
|
-
e.
|
|
230
|
+
e.cn = n;
|
|
122
231
|
n.tt += e.tt;
|
|
123
232
|
n.nt ||= e.nt;
|
|
124
|
-
append(n.
|
|
125
|
-
append(n.
|
|
126
|
-
append(n.
|
|
127
|
-
append(n.
|
|
128
|
-
if (e.
|
|
233
|
+
append(n.je, e.je);
|
|
234
|
+
append(n.un[0], e.un[0]);
|
|
235
|
+
append(n.un[1], e.un[1]);
|
|
236
|
+
append(n.En, e.En);
|
|
237
|
+
if (e.I !== null) append(n.I ??= [], e.I);
|
|
129
238
|
transactions.splice(transactions.indexOf(e), 1);
|
|
130
239
|
}
|
|
131
240
|
|
|
@@ -151,9 +260,9 @@ function resolveTx(e) {
|
|
|
151
260
|
judge ??= e;
|
|
152
261
|
// An action still running in it holds it open (action.ts). Its lanes
|
|
153
262
|
// (lanes.ts): a lane under `t` blocks it while blocked itself.
|
|
154
|
-
const
|
|
263
|
+
const o = e.tt !== 0 || blockedBy(e.je, e) || !!lanesBlockedHook?.(e);
|
|
155
264
|
judge = n;
|
|
156
|
-
return
|
|
265
|
+
return o;
|
|
157
266
|
}
|
|
158
267
|
|
|
159
268
|
let judge = null;
|
|
@@ -162,9 +271,9 @@ let judge = null;
|
|
|
162
271
|
* frame reader observes (`blockedBy`'s predicate, collected; the readers
|
|
163
272
|
* themselves are not sources). */ function blockersOf(e) {
|
|
164
273
|
const n = [];
|
|
165
|
-
for (let
|
|
166
|
-
const
|
|
167
|
-
if (!
|
|
274
|
+
for (let o = 0; o < e.je.length; o++) {
|
|
275
|
+
const t = e.je[o];
|
|
276
|
+
if (!t.i && t.m & STATUS_PENDING && !(t.R & REACTIVE_DISPOSED) && (t.T.Je === null || txOf(t) === e) && blockedBy([ t ], e)) n.push(t);
|
|
168
277
|
}
|
|
169
278
|
return n;
|
|
170
279
|
}
|
|
@@ -174,33 +283,33 @@ let judge = null;
|
|
|
174
283
|
* not (lanes.ts, the body-end corollary). A pending reader counts only if
|
|
175
284
|
* one of the sources it waits on is `t`'s (a render effect marked pending
|
|
176
285
|
* by a lane flight, never re-run, is the lane's wait, not the frame's). */ function ownFlights(e) {
|
|
177
|
-
return blockedBy(e.
|
|
286
|
+
return blockedBy(e.je, e, true);
|
|
178
287
|
}
|
|
179
288
|
|
|
180
289
|
/** A source the pending node waits on that is `t`'s (held by it, or by no
|
|
181
290
|
* one) — not another transaction's or lane's work. Itself, when it is the
|
|
182
291
|
* source. */ function ownSource(e, n) {
|
|
183
|
-
const
|
|
184
|
-
if (
|
|
185
|
-
for (const e of
|
|
186
|
-
const
|
|
187
|
-
if (
|
|
292
|
+
const o = e.T?.fe;
|
|
293
|
+
if (o === undefined) return true;
|
|
294
|
+
for (const e of o) {
|
|
295
|
+
const o = e.T?.Je;
|
|
296
|
+
if (o == null || resolveTx(o) === n) return true;
|
|
188
297
|
}
|
|
189
298
|
return false;
|
|
190
299
|
}
|
|
191
300
|
|
|
192
|
-
function blockedBy(e, n,
|
|
193
|
-
for (let
|
|
194
|
-
const
|
|
195
|
-
if (!(
|
|
301
|
+
function blockedBy(e, n, o = false) {
|
|
302
|
+
for (let t = 0; t < e.length; t++) {
|
|
303
|
+
const l = e[t];
|
|
304
|
+
if (!(l.m & STATUS_PENDING) || l.R & REACTIVE_DISPOSED || l.l & CONFIG_GUESS || l.T.Je !== null && txOf(l) !== n || o && !ownSource(l, n)) continue;
|
|
196
305
|
// The frame's own pending observer (off screen: this one holds nothing;
|
|
197
306
|
// the next listed node may).
|
|
198
|
-
if (
|
|
199
|
-
if (onScreen(
|
|
307
|
+
if (l.i === EFFECT_RENDER) {
|
|
308
|
+
if (onScreen(l, judge ?? n)) return true;
|
|
200
309
|
continue;
|
|
201
310
|
}
|
|
202
|
-
for (let e = o
|
|
203
|
-
const
|
|
311
|
+
for (let e = l.o; e !== null; e = e.t) {
|
|
312
|
+
const o = e.u;
|
|
204
313
|
// A frame reader: a render effect, or a stale reader of the flight
|
|
205
314
|
// (REACTIVE_FRAME_READ — a verdict reader, `observeFlight`; lane work,
|
|
206
315
|
// `frameRead`): served committed instead of going pending, it derives
|
|
@@ -212,7 +321,7 @@ function blockedBy(e, n, t = false) {
|
|
|
212
321
|
// lane-membership ruling, 2026-10-05). An output that never committed
|
|
213
322
|
// shows nothing — a mount the frame has not revealed — and holds
|
|
214
323
|
// nothing.
|
|
215
|
-
if ((
|
|
324
|
+
if ((o.i === EFFECT_RENDER && o.m & STATUS_PENDING || o.R & REACTIVE_FRAME_READ || o.l & CONFIG_REDERIVE && o.m === STATUS_PENDING) && (e.Te === o.Ne || o.T?.F != null) && !(o.R & REACTIVE_DISPOSED) && onScreen(o, judge ?? n)) return true;
|
|
216
325
|
}
|
|
217
326
|
}
|
|
218
327
|
return false;
|
|
@@ -226,7 +335,7 @@ function blockedBy(e, n, t = false) {
|
|
|
226
335
|
* makes its say moot), and not behind a fallback (A33: a boundary showing its fallback is the display of
|
|
227
336
|
* everything under it, so a reader there is not on screen and holds
|
|
228
337
|
* nothing — boundaries.ts). */ function onScreen(e, n) {
|
|
229
|
-
return !(e.R & REACTIVE_ZOMBIE && removalStagedBy(e, n)) && !(
|
|
338
|
+
return !(e.R & REACTIVE_ZOMBIE && removalStagedBy(e, n)) && !hiddenHook?.(e);
|
|
230
339
|
}
|
|
231
340
|
|
|
232
341
|
/** A15 (#3463): a reader whose removal is staged — a zombie, parked by a
|
|
@@ -235,9 +344,9 @@ function blockedBy(e, n, t = false) {
|
|
|
235
344
|
* (done, and the commit disposes it; not done, and it stays parked). The
|
|
236
345
|
* staging transaction is the one holding the pass that parked the frame:
|
|
237
346
|
* the nearest non-zombie ancestor. */ function removalStagedBy(e, n) {
|
|
238
|
-
let
|
|
239
|
-
while (
|
|
240
|
-
return
|
|
347
|
+
let o = e;
|
|
348
|
+
while (o?.R & REACTIVE_ZOMBIE) o = o._parent;
|
|
349
|
+
return o?.l & CONFIG_HELD && o.T?.Je != null && txOf(o) === resolveTx(n);
|
|
241
350
|
}
|
|
242
351
|
|
|
243
352
|
/** Unchanged passes this flush whose stale dependency tail awaits the
|
|
@@ -264,17 +373,17 @@ function deferZombie(e) {
|
|
|
264
373
|
|
|
265
374
|
/** Put a staged node in a transaction: flagged, stamped, on its landing
|
|
266
375
|
* list. */ function holdNode(e, n) {
|
|
267
|
-
e.
|
|
376
|
+
e.l |= CONFIG_HELD;
|
|
268
377
|
list(e, n);
|
|
269
378
|
}
|
|
270
379
|
|
|
271
380
|
/** Put a node on a transaction's list, once: a node's `_transaction` is the
|
|
272
381
|
* transaction whose `_nodes` it is on (every site that sets one does the
|
|
273
382
|
* other), so a re-pass, a re-landing or a re-staging adds nothing. */ function list(e, n) {
|
|
274
|
-
const
|
|
275
|
-
if (
|
|
276
|
-
|
|
277
|
-
n.
|
|
383
|
+
const o = ext(e);
|
|
384
|
+
if (o.Je === n) return;
|
|
385
|
+
o.Je = n;
|
|
386
|
+
n.je.push(e);
|
|
278
387
|
}
|
|
279
388
|
|
|
280
389
|
/** Hold the (uncommitted) frame under a held pass: every computed in the
|
|
@@ -290,18 +399,34 @@ function deferZombie(e) {
|
|
|
290
399
|
* next read of it trips over (`laneRead` → `txOf`). Its frame is the lane's
|
|
291
400
|
* too (ruling A: a lane pass's children are the lane's), revealed and
|
|
292
401
|
* retired at the lane's seam. */ function holdFrame(e, n) {
|
|
293
|
-
for (let
|
|
294
|
-
if (
|
|
295
|
-
if (
|
|
296
|
-
|
|
402
|
+
for (let o = e.ze; o !== null; o = o.Xe) {
|
|
403
|
+
if (o.T?.Je?.V) continue;
|
|
404
|
+
if (o.X !== undefined) {
|
|
405
|
+
if (!(o.l & CONFIG_HELD)) holdNode(o, n);
|
|
406
|
+
o.l |= CONFIG_HELD_FRAME;
|
|
407
|
+
}
|
|
408
|
+
holdFrame(o, n);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/** The nodes under `owner` a transaction other than `t` holds (boundaries.ts:
|
|
413
|
+
* what an earlier hold staged under a re-armed boundary). */ function heldUnder(e, n, o) {
|
|
414
|
+
for (let t = e.ze; t !== null; t = t.Xe) {
|
|
415
|
+
if ((t.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && txOf(t) !== n) o(t);
|
|
416
|
+
heldUnder(t, n, o);
|
|
297
417
|
}
|
|
298
418
|
}
|
|
299
419
|
|
|
420
|
+
/** `n` leaves the hold it was staged in: its next pass publishes mainline. */ function unhold(e) {
|
|
421
|
+
e.T.Je = null;
|
|
422
|
+
e.l &= ~CONFIG_HELD;
|
|
423
|
+
}
|
|
424
|
+
|
|
300
425
|
const dirtyQueue = {
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
426
|
+
gt: new Array(2e3).fill(undefined),
|
|
427
|
+
p: false,
|
|
428
|
+
st: 0,
|
|
429
|
+
jt: 0
|
|
305
430
|
};
|
|
306
431
|
|
|
307
432
|
let clock = 0;
|
|
@@ -364,7 +489,7 @@ function schedule() {
|
|
|
364
489
|
}
|
|
365
490
|
if (scheduled) return;
|
|
366
491
|
scheduled = true;
|
|
367
|
-
if (!syncDepth && !globalQueue.
|
|
492
|
+
if (!syncDepth && !globalQueue.q) {
|
|
368
493
|
queueMicrotask(flush);
|
|
369
494
|
}
|
|
370
495
|
}
|
|
@@ -395,9 +520,9 @@ function haltReactivity(e) {
|
|
|
395
520
|
// nothing an app or its telemetry can act on (#3338 — an uncaught throw
|
|
396
521
|
// during the hydration render). The rethrow may reach the top as well in
|
|
397
522
|
// the non-swallowed cases; a duplicate report beats a silent one.
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
|
|
523
|
+
const o = e !== undefined && globalThis.reportError;
|
|
524
|
+
o || e === undefined ? console.error(n) : console.error(n, e);
|
|
525
|
+
o && o(e);
|
|
401
526
|
}
|
|
402
527
|
|
|
403
528
|
// Logs on the first write after a halt so a frozen interaction is traceable.
|
|
@@ -413,11 +538,11 @@ function notifyHalted() {
|
|
|
413
538
|
}
|
|
414
539
|
|
|
415
540
|
class GlobalQueue {
|
|
416
|
-
|
|
417
|
-
|
|
541
|
+
un=[ [], [] ];
|
|
542
|
+
q=false;
|
|
418
543
|
flush() {
|
|
419
|
-
if (this.
|
|
420
|
-
this.
|
|
544
|
+
if (this.q) return;
|
|
545
|
+
this.q = true;
|
|
421
546
|
// A28: companions of nodes written since the last flush mirror the
|
|
422
547
|
// flushed world — re-synced inside the running window (the write is
|
|
423
548
|
// "flushed" from here on).
|
|
@@ -430,12 +555,12 @@ class GlobalQueue {
|
|
|
430
555
|
// recomputed; late subscribers (an effect reading a swept memo this
|
|
431
556
|
// flush) revive it, which is the pay-for-use contract.
|
|
432
557
|
sweepDormant();
|
|
433
|
-
runHeap(dirtyQueue, GlobalQueue.
|
|
558
|
+
runHeap(dirtyQueue, GlobalQueue.Ze);
|
|
434
559
|
this.settle();
|
|
435
560
|
clock++;
|
|
436
561
|
// A write the commit sweep staged with no subscriber to dirty is work
|
|
437
562
|
// too — the next round commits it.
|
|
438
|
-
scheduled = dirtyQueue.
|
|
563
|
+
scheduled = dirtyQueue.jt >= dirtyQueue.st || pendingNodes.length !== 0;
|
|
439
564
|
this.run(EFFECT_RENDER);
|
|
440
565
|
this.run(EFFECT_USER);
|
|
441
566
|
if (true && holding) {
|
|
@@ -445,7 +570,7 @@ class GlobalQueue {
|
|
|
445
570
|
if (false && !scheduled) ;
|
|
446
571
|
if (false) ;
|
|
447
572
|
} finally {
|
|
448
|
-
this.
|
|
573
|
+
this.q = false;
|
|
449
574
|
passTx = null;
|
|
450
575
|
}
|
|
451
576
|
}
|
|
@@ -458,7 +583,7 @@ class GlobalQueue {
|
|
|
458
583
|
// Lanes: an ended action body judges its guesses now that their passes
|
|
459
584
|
// have run (lanes.ts); a correction's re-derivations run in one more
|
|
460
585
|
// pure round — the parent's flights before the parent is judged below.
|
|
461
|
-
if (
|
|
586
|
+
if (laneCorrectionsHook?.()) runHeap(dirtyQueue, GlobalQueue.Ze);
|
|
462
587
|
const e = flushTransaction;
|
|
463
588
|
flushTransaction = null;
|
|
464
589
|
// The frame's verdict is in: the guesses written since the last seam
|
|
@@ -466,21 +591,21 @@ class GlobalQueue {
|
|
|
466
591
|
// parents a nested lane to it), or are dropped — the frame commits, and
|
|
467
592
|
// there is nothing to be optimistic over. Their passes run next round;
|
|
468
593
|
// a parent landing at this very seam ends the lane before they do.
|
|
469
|
-
|
|
594
|
+
applyGuessesHook?.(e);
|
|
470
595
|
// A lane's own flush (its flight landing, its reveal) parks nothing: the
|
|
471
596
|
// lane's work reached it per pass; the frame's plain writes commit.
|
|
472
597
|
// (Resolved: a guess over another transaction's guess merged them.)
|
|
473
|
-
const n = e !== null && !e.
|
|
598
|
+
const n = e !== null && !e.V ? resolveTx(e) : null;
|
|
474
599
|
if (n !== null) {
|
|
475
600
|
// `t._nodes` grows under the walk (holdFrame pushes); the bound is
|
|
476
601
|
// this flush's list, which does not.
|
|
477
602
|
const e = pendingNodes.length;
|
|
478
|
-
for (let
|
|
479
|
-
const e = pendingNodes[
|
|
603
|
+
for (let o = 0; o < e; o++) {
|
|
604
|
+
const e = pendingNodes[o];
|
|
480
605
|
// Already held: the pass that re-staged it joined its transaction,
|
|
481
606
|
// which is `t` (or merged into it). Lane work is the lane's (a
|
|
482
607
|
// pending mark propagated onto it queued it here too).
|
|
483
|
-
if (!(e.
|
|
608
|
+
if (!(e.l & (CONFIG_HELD | CONFIG_OVERRIDE)) && (e.T === null || e.T.Je === null || !e.T.Je.V)) {
|
|
484
609
|
// A loading source — pending with nothing staged and no committed
|
|
485
610
|
// value (A16, A19 exc. 1: uninitialized is loading, not pending)
|
|
486
611
|
// that is not an observer — is nobody's frame: not held. Its
|
|
@@ -489,15 +614,19 @@ class GlobalQueue {
|
|
|
489
614
|
// stale stamp was the bridge that entangled unrelated updates
|
|
490
615
|
// with a never-resolving flight). Its observers are held and
|
|
491
616
|
// wait on its flight (`blocked`).
|
|
492
|
-
if (e.
|
|
617
|
+
if (e.ue === NOT_PENDING && e.m & STATUS_UNINITIALIZED && e.i !== EFFECT_RENDER) continue;
|
|
493
618
|
// A34 (2): a tick whose writes net to the committed value made no
|
|
494
619
|
// proposal (`setShow(false); setShow(true)` on a committed true) —
|
|
495
620
|
// not staged, not held, pends nothing; a later write to it is
|
|
496
621
|
// plain mainline. (A node already held stays held when written
|
|
497
622
|
// back: A34 (1).)
|
|
498
|
-
if (e.
|
|
499
|
-
|
|
500
|
-
|
|
623
|
+
if (e.ue !== NOT_PENDING && !(e.m & (STATUS_PENDING | STATUS_UNINITIALIZED)) && e.T?.Nt == null && e.T?.ft == null &&
|
|
624
|
+
// The comparator last: a pass that created children (an object
|
|
625
|
+
// owning a fresh async memo, #4111) must not have it read them.
|
|
626
|
+
// A NotReady read there is a change (`isSame`).
|
|
627
|
+
isSame(e, e.Ie, e.ue)) {
|
|
628
|
+
e.ue = NOT_PENDING;
|
|
629
|
+
e.l &= ~CONFIG_STAGED;
|
|
501
630
|
continue;
|
|
502
631
|
}
|
|
503
632
|
holdNode(e, n);
|
|
@@ -505,10 +634,10 @@ class GlobalQueue {
|
|
|
505
634
|
// A held pass's frame is held with it (ruling A, O2): the children it
|
|
506
635
|
// created are the transaction's — they compute, and a pass over them
|
|
507
636
|
// or a write that reaches them joins; they effect at the landing.
|
|
508
|
-
if (e.
|
|
637
|
+
if (e.l & CONFIG_STAGED) holdFrame(e, n);
|
|
509
638
|
}
|
|
510
639
|
pendingNodes.length = 0;
|
|
511
|
-
|
|
640
|
+
storeParkHook?.(n);
|
|
512
641
|
// (This flush's runs are stashed with `t` below — after the landings,
|
|
513
642
|
// so a `t` that lands at this very seam runs them first, ahead of
|
|
514
643
|
// the runs it held from earlier flushes.)
|
|
@@ -518,8 +647,12 @@ class GlobalQueue {
|
|
|
518
647
|
// through like the same read outside): it re-runs now, as the
|
|
519
648
|
// holder's verdict lane's work — before the lane seams, which reveal
|
|
520
649
|
// it (nothing commits this seam that could dispose it).
|
|
521
|
-
|
|
522
|
-
|
|
650
|
+
// (Verdict readers exist only with the verdict engine installed.)
|
|
651
|
+
const o = deferredZombies.splice(0);
|
|
652
|
+
if (observeFlightHook) {
|
|
653
|
+
o.sort((e, n) => e.Et - n.Et);
|
|
654
|
+
for (let e = 0; e < o.length; e++) if ((o[e].R & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE)) === REACTIVE_ZOMBIE && o[e].l & CONFIG_VERDICT) recompute(o[e]);
|
|
655
|
+
}
|
|
523
656
|
// The unchanged passes' tails stay linked (A30, #3469): their inputs
|
|
524
657
|
// are held, and the committed frame still derives from them — no trim.
|
|
525
658
|
} else {
|
|
@@ -531,7 +664,7 @@ class GlobalQueue {
|
|
|
531
664
|
// `recompute`'s own gates.
|
|
532
665
|
for (let e = 0; e < heldTrims.length; e++) {
|
|
533
666
|
const n = heldTrims[e];
|
|
534
|
-
if (n.
|
|
667
|
+
if (n.T?.F == null && !n.Qe) trimStaleDeps(n);
|
|
535
668
|
}
|
|
536
669
|
}
|
|
537
670
|
// The list is the flush's either way. Reset only when there is something
|
|
@@ -545,19 +678,21 @@ class GlobalQueue {
|
|
|
545
678
|
// removes its entry.
|
|
546
679
|
// This flush's own runs, the lanes' reveals, and the landings' held runs
|
|
547
680
|
// are collected apart and ordered below.
|
|
548
|
-
const
|
|
549
|
-
const
|
|
681
|
+
const o = this.un;
|
|
682
|
+
const t = this.un = [ [], [] ];
|
|
550
683
|
// The passes that read this frame's stagings as the screen read held
|
|
551
684
|
// writes if it parked: the lane seam re-derives them on the committed
|
|
552
685
|
// world (a lane's runs wait this round).
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
686
|
+
if (laneSeamsHook) {
|
|
687
|
+
laneSeamsHook(n !== null ? stagedReaders : null);
|
|
688
|
+
if (stagedReaders.length) stagedReaders.length = 0;
|
|
689
|
+
}
|
|
690
|
+
this.un = [ [], [] ];
|
|
556
691
|
for (let e = transactions.length - 1; e >= 0; e--) {
|
|
557
692
|
const n = transactions[e];
|
|
558
693
|
if (blocked(n)) continue;
|
|
559
694
|
transactions.splice(e, 1);
|
|
560
|
-
|
|
695
|
+
endLanesHook?.(n);
|
|
561
696
|
land(n);
|
|
562
697
|
// A landing's commits can dispose a zombie a transaction judged above
|
|
563
698
|
// was blocked on (#3463: live "until the commit that disposes it") —
|
|
@@ -566,12 +701,13 @@ class GlobalQueue {
|
|
|
566
701
|
}
|
|
567
702
|
// The store folds the pending backings whose container nodes committed
|
|
568
703
|
// — this flush's, or a landing's (store/store.ts installs it).
|
|
569
|
-
|
|
704
|
+
storeCommitHook?.();
|
|
570
705
|
// Marks declared outside a transaction: the flush that carried them
|
|
571
706
|
// parked (something async below) — the window is the frame's, the marks
|
|
572
707
|
// are its transaction's; else they release at the seam, verdict-only,
|
|
573
708
|
// nothing to show (affects.ts).
|
|
574
|
-
|
|
709
|
+
const l = n && liveTx(n);
|
|
710
|
+
releaseAmbientMarksHook?.(l);
|
|
575
711
|
// The effect phase: lane work first (displayed ahead of the frame), then
|
|
576
712
|
// this flush's runs, then the runs the landings held from earlier
|
|
577
713
|
// flushes — as one pass would have queued them (#3540: a shell
|
|
@@ -579,49 +715,48 @@ class GlobalQueue {
|
|
|
579
715
|
// write; #3528: a display-ahead swap before the count it is shown
|
|
580
716
|
// beside). A parked frame's runs wait with its transaction instead: the
|
|
581
717
|
// effect phase applies a committed frame.
|
|
582
|
-
const
|
|
583
|
-
const u = o !== null;
|
|
718
|
+
const s = l !== null;
|
|
584
719
|
// Attribution hook: this flush found its transaction incomplete — its
|
|
585
720
|
// writes stay staged, its runs are stashed below. Before the effect
|
|
586
721
|
// phase (the lanes' display-ahead runs are the visible acknowledgers);
|
|
587
722
|
// `holdEnd` fires after it (`flush`).
|
|
588
|
-
if (
|
|
589
|
-
attrHooks.holdStart(
|
|
723
|
+
if (s && attrHooks !== null) {
|
|
724
|
+
attrHooks.holdStart(l);
|
|
590
725
|
holding = true;
|
|
591
726
|
}
|
|
592
727
|
for (let e = 0; e < 2; e++) {
|
|
593
|
-
if (
|
|
728
|
+
if (s) append(l.un[e], o[e]);
|
|
594
729
|
// The plain flush — no reveal, no landing, not parked — has nothing to
|
|
595
730
|
// order around its own runs: they are the queue as they stand (the
|
|
596
731
|
// same array, not a copy; `run` takes it whole). Every other seam
|
|
597
732
|
// builds the ordered queue above.
|
|
598
|
-
this.
|
|
733
|
+
this.un[e] = s || t[e].length || this.un[e].length ? t[e].concat(s ? [] : o[e], this.un[e]) : o[e];
|
|
599
734
|
}
|
|
600
735
|
if (deferredZombies.length !== 0) {
|
|
601
736
|
// Survivors are the displayed frame of a held pass: they rerun for the
|
|
602
737
|
// live write, parents before children.
|
|
603
|
-
const e = deferredZombies.splice(0).sort((e, n) => e.
|
|
738
|
+
const e = deferredZombies.splice(0).sort((e, n) => e.Et - n.Et);
|
|
604
739
|
for (let n = 0; n < e.length; n++) {
|
|
605
740
|
if (!(e[n].R & REACTIVE_DISPOSED)) recompute(e[n]);
|
|
606
741
|
}
|
|
607
742
|
}
|
|
608
743
|
// Boundaries: readers that settled, landed or died this flush are
|
|
609
744
|
// dropped; a fallback with none left reveals next round.
|
|
610
|
-
|
|
745
|
+
boundarySeamHook?.();
|
|
611
746
|
}
|
|
612
747
|
run(e) {
|
|
613
|
-
const n = this.
|
|
748
|
+
const n = this.un[e - 1];
|
|
614
749
|
if (n.length) {
|
|
615
|
-
this.
|
|
750
|
+
this.un[e - 1] = [];
|
|
616
751
|
runQueue(n, e);
|
|
617
752
|
}
|
|
618
753
|
}
|
|
619
|
-
enqueue(e, n,
|
|
754
|
+
enqueue(e, n, o = passLane) {
|
|
620
755
|
// Lane work's runs are the lane's: they run at its reveal, not with
|
|
621
756
|
// the frame (which may park). A lane pass that read the frame's own
|
|
622
757
|
// staging is the frame's (`recompute` passes null): its run waits with
|
|
623
758
|
// the frame.
|
|
624
|
-
if (e) (
|
|
759
|
+
if (e) (o !== null ? o : this).un[e - 1].push(n);
|
|
625
760
|
schedule();
|
|
626
761
|
}
|
|
627
762
|
/** Status reaching the root: pending is absorbed (the root mount defers —
|
|
@@ -637,10 +772,10 @@ class GlobalQueue {
|
|
|
637
772
|
*
|
|
638
773
|
* A boundary between the observer and the root (boundaries.ts) is asked
|
|
639
774
|
* first: a fallback catches what is not ready under it, and the root
|
|
640
|
-
* never hears of it. */ notify(e, n,
|
|
641
|
-
if (
|
|
775
|
+
* never hears of it. */ notify(e, n, o, t) {
|
|
776
|
+
if (catchHook?.(e, o & n, t)) return true;
|
|
642
777
|
if (n & STATUS_PENDING) {
|
|
643
|
-
if (
|
|
778
|
+
if (o & STATUS_PENDING) {
|
|
644
779
|
// A pass that went pending holds its frame: the async's own
|
|
645
780
|
// synchronous frame is not ready (A15 reveal corollary: a reveal
|
|
646
781
|
// that discovers a flight whose inputs are visible holds and joins
|
|
@@ -672,20 +807,23 @@ class GlobalQueue {
|
|
|
672
807
|
* for it is void (#3322: an effect has one value slot, and the stale pass
|
|
673
808
|
* overwrote the value that run was for) — the re-derivation's run replaces
|
|
674
809
|
* it. */ function reruns(e) {
|
|
675
|
-
for (let n = 0; n < e.
|
|
676
|
-
const
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
810
|
+
for (let n = 0; n < e.En.length; n++) {
|
|
811
|
+
const o = e.En[n];
|
|
812
|
+
// An errored reader's display is its error (D-39): the landing gives it
|
|
813
|
+
// nothing to re-derive, and a re-run would be a read that retries the
|
|
814
|
+
// errored source (#3945).
|
|
815
|
+
if (o.R & REACTIVE_DISPOSED || o.m & STATUS_ERROR) continue;
|
|
816
|
+
if (o.R & REACTIVE_FRAME_READ || o.l & CONFIG_VERDICT) {
|
|
817
|
+
o.R &= ~REACTIVE_FRAME_READ;
|
|
818
|
+
o.Qe = false;
|
|
681
819
|
// Its frame is replaced by the re-derivation: a run one of its
|
|
682
820
|
// children queued this flush (re-run by the same landing) is for the
|
|
683
821
|
// frame being replaced — void too (#3404).
|
|
684
|
-
for (let e =
|
|
685
|
-
enqueueSub(
|
|
822
|
+
for (let e = o.ze; e !== null; e = e.Xe) if (e.i) e.Qe = false;
|
|
823
|
+
enqueueSub(o);
|
|
686
824
|
}
|
|
687
825
|
}
|
|
688
|
-
e.
|
|
826
|
+
e.En.length = 0;
|
|
689
827
|
}
|
|
690
828
|
|
|
691
829
|
/** The landing: the held frame becomes the frame. */ function land(e) {
|
|
@@ -693,16 +831,20 @@ class GlobalQueue {
|
|
|
693
831
|
// (`_nodes` still lists them).
|
|
694
832
|
if (attrHooks !== null) attrHooks.transitionSettled(e);
|
|
695
833
|
reruns(e);
|
|
696
|
-
if (e.
|
|
834
|
+
if (e.I !== null) releaseMarksHook(e.I);
|
|
697
835
|
// Old children die in the commits (cleanups first), then the stashed
|
|
698
836
|
// effects run ahead of this flush's own. A node a lane took over since
|
|
699
837
|
// (a guess written over the staged truth) is the lane's to land.
|
|
700
|
-
for (let n = 0; n < e.
|
|
701
|
-
const
|
|
702
|
-
if (
|
|
703
|
-
|
|
704
|
-
commitPendingNode(
|
|
838
|
+
for (let n = 0; n < e.je.length; n++) {
|
|
839
|
+
const o = e.je[n];
|
|
840
|
+
if (o.T.Je === null || txOf(o) !== e) continue;
|
|
841
|
+
o.T.Je = null;
|
|
842
|
+
commitPendingNode(o);
|
|
705
843
|
}
|
|
844
|
+
// A cleanup those commits ran that wrote a node `u` still held joined
|
|
845
|
+
// `u`, which has left `transactions`: the write is this landing's, and
|
|
846
|
+
// the next flush must not park in it (#4089).
|
|
847
|
+
if (flushTransaction === e) flushTransaction = null;
|
|
706
848
|
releaseQueues(e);
|
|
707
849
|
}
|
|
708
850
|
|
|
@@ -711,13 +853,13 @@ class GlobalQueue {
|
|
|
711
853
|
* pass would have queued them — a shell re-derived at the landing before a
|
|
712
854
|
* boundary swap held since the write (#3540's log order). */ function releaseQueues(e) {
|
|
713
855
|
for (let n = 0; n < 2; n++) {
|
|
714
|
-
append(globalQueue.
|
|
715
|
-
e.
|
|
856
|
+
append(globalQueue.un[n], e.un[n]);
|
|
857
|
+
e.un[n] = [];
|
|
716
858
|
}
|
|
717
859
|
}
|
|
718
860
|
|
|
719
861
|
/** `a.push(...b)` without the argument-count limit. */ function append(e, n) {
|
|
720
|
-
for (let
|
|
862
|
+
for (let o = 0; o < n.length; o++) e.push(n[o]);
|
|
721
863
|
}
|
|
722
864
|
|
|
723
865
|
/** Nodes staged this flush (`_pendingValue` set), committed at its end. */ const pendingNodes = [];
|
|
@@ -732,13 +874,13 @@ class GlobalQueue {
|
|
|
732
874
|
* after `t`'s landing (`_reruns`). Once per pass. */ function staleReader(e, n) {
|
|
733
875
|
if (!(e.R & REACTIVE_FRAME_READ)) {
|
|
734
876
|
e.R |= REACTIVE_FRAME_READ;
|
|
735
|
-
n.
|
|
877
|
+
n.En.push(e);
|
|
736
878
|
}
|
|
737
879
|
}
|
|
738
880
|
|
|
739
881
|
function queuePendingNode(e) {
|
|
740
882
|
pendingNodes.push(e);
|
|
741
|
-
if (!globalQueue.
|
|
883
|
+
if (!globalQueue.q) markUnflushedStaged();
|
|
742
884
|
// A28
|
|
743
885
|
}
|
|
744
886
|
|
|
@@ -754,18 +896,18 @@ function bumpNotifyEpoch() {
|
|
|
754
896
|
function insertSubs(e) {
|
|
755
897
|
// §12d: stamp before walking — setSignal's staged-rewrite fast path skips
|
|
756
898
|
// the next walk for this node while the epoch holds (marking is idempotent).
|
|
757
|
-
e.
|
|
899
|
+
e.Ot = notifyEpoch;
|
|
758
900
|
// Presence bits gate the optional-slot probes (see constants.ts): one
|
|
759
901
|
// masked read of the always-present _config instead of missing-property
|
|
760
902
|
// lookups in the hottest notify loop.
|
|
761
|
-
const n = e.
|
|
762
|
-
const
|
|
903
|
+
const n = e.l;
|
|
904
|
+
const o = (n & CONFIG_HAS_SNAPSHOT) !== 0 && e.T?.lt !== undefined;
|
|
763
905
|
// Observe-tier fan-out: this walk visits every subscriber edge anyway, so
|
|
764
906
|
// the graph-size count is one local increment here and no field anywhere.
|
|
765
|
-
let
|
|
766
|
-
for (let n = e.
|
|
767
|
-
const e = n.
|
|
768
|
-
|
|
907
|
+
let t = 0;
|
|
908
|
+
for (let n = e.o; n !== null; n = n.t) {
|
|
909
|
+
const e = n.u;
|
|
910
|
+
t++;
|
|
769
911
|
// Missed-wake latch (#3037): this write is landing while the subscriber
|
|
770
912
|
// is mid-recompute (a nested pull committing beneath its reads), and the
|
|
771
913
|
// heap refuses RECOMPUTING nodes. A gen-current link means the pass
|
|
@@ -774,14 +916,14 @@ function insertSubs(e) {
|
|
|
774
916
|
// pass either re-reads them fresh or trims them), and neither does the
|
|
775
917
|
// tail link: it is the read IN FLIGHT — read() links before it pulls, so
|
|
776
918
|
// this very commit is what that read returns.
|
|
777
|
-
if (e.R & REACTIVE_RECOMPUTING_DEPS && n.
|
|
778
|
-
if (
|
|
919
|
+
if (e.R & REACTIVE_RECOMPUTING_DEPS && n.Te === e.Ne && n !== e._) e.R |= REACTIVE_MISSED_WAKE;
|
|
920
|
+
if (o && e.l & CONFIG_IN_SNAPSHOT_SCOPE) {
|
|
779
921
|
e.R |= REACTIVE_SNAPSHOT_STALE;
|
|
780
922
|
continue;
|
|
781
923
|
}
|
|
782
924
|
enqueueSub(e);
|
|
783
925
|
}
|
|
784
|
-
if (
|
|
926
|
+
if (t >= GRAPH_SIZE_WARN_AT) noteFanOut(e, t);
|
|
785
927
|
}
|
|
786
928
|
|
|
787
929
|
/** Lane work re-staged (lanes.ts; `recompute`): the lane's members among
|
|
@@ -792,14 +934,14 @@ function insertSubs(e) {
|
|
|
792
934
|
* is a live write if the effect becomes a zombie later this flush — an
|
|
793
935
|
* on-screen reader whose removal an adopted write stages follows the lane
|
|
794
936
|
* (the lane-membership ruling, 2026-10-05; `_update`). */ function laneDirty(e, n) {
|
|
795
|
-
for (let
|
|
796
|
-
const e =
|
|
797
|
-
if (e?.
|
|
937
|
+
for (let o = e.o; o !== null; o = o.t) {
|
|
938
|
+
const e = o.u.T?.Je;
|
|
939
|
+
if (e?.V ? sameLane(e, n) : o.u.i) o.u.R |= REACTIVE_LANE_DIRTY;
|
|
798
940
|
}
|
|
799
941
|
}
|
|
800
942
|
|
|
801
943
|
/** One lane, or two of one link group (#3335): one reveal unit. */ function sameLane(e, n) {
|
|
802
|
-
return e === n || e.
|
|
944
|
+
return e === n || e.nn !== null && e.nn === n.nn;
|
|
803
945
|
}
|
|
804
946
|
|
|
805
947
|
function commitPendingNode(e) {
|
|
@@ -809,40 +951,52 @@ function commitPendingNode(e) {
|
|
|
809
951
|
// staged) stays uncommitted: a re-pass disposes its children on the spot,
|
|
810
952
|
// and the frame it parked stays the frame until a pass of this node
|
|
811
953
|
// commits a value.
|
|
812
|
-
if (!n.
|
|
813
|
-
e.
|
|
814
|
-
if (e.
|
|
815
|
-
e.
|
|
816
|
-
e.
|
|
954
|
+
if (!n.X) {
|
|
955
|
+
e.l &= -385;
|
|
956
|
+
if (e.ue !== NOT_PENDING) {
|
|
957
|
+
e.Ie = e.ue;
|
|
958
|
+
e.ue = NOT_PENDING;
|
|
817
959
|
}
|
|
818
960
|
return;
|
|
819
961
|
}
|
|
820
962
|
// Computeds only from here (`_statusFlags` is not a signal field). A
|
|
821
963
|
// commit beneath a flight publishes its inputs (A15 reveal corollary,
|
|
822
964
|
// #3305): a reveal of it must observe, not show the pre-flight value.
|
|
823
|
-
const
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
965
|
+
const o = e.ue === NOT_PENDING && (n.m & STATUS_PENDING) !== 0;
|
|
966
|
+
const t = e.l & CONFIG_STAGED_ERROR;
|
|
967
|
+
// An answer commits: the error it replaces leaves the display (D-41).
|
|
968
|
+
if (!o && n.T) n.T.de = undefined;
|
|
969
|
+
e.l = o ? e.l & -1048833 | CONFIG_INPUTS_PUBLISHED : e.l & -3147137;
|
|
970
|
+
// D-41: the staged answer is an error — published now, when a value would
|
|
971
|
+
// show, through the ordinary error path (`stageError`). Its readers first
|
|
972
|
+
// see it in the cycle after this seam: a tracked re-read there is not a
|
|
973
|
+
// retry (`read`), or a boundary re-derived by it would retry it forever.
|
|
974
|
+
if (t) {
|
|
975
|
+
const o = e.ue;
|
|
976
|
+
e.ue = NOT_PENDING;
|
|
977
|
+
e.ie = clock + 1;
|
|
978
|
+
notifyStatus(n, STATUS_ERROR, o);
|
|
979
|
+
} else if (e.ue !== NOT_PENDING) {
|
|
980
|
+
e.Ie = e.ue;
|
|
981
|
+
e.ue = NOT_PENDING;
|
|
828
982
|
// A node born staged (recompute) initializes at this commit.
|
|
829
|
-
n.
|
|
983
|
+
n.m &= ~STATUS_UNINITIALIZED;
|
|
830
984
|
// A quiet re-ask's classification survives its landing and dies with
|
|
831
985
|
// the landed value's commit (A19 exc. 2, #3178): verdict-quiet through
|
|
832
986
|
// the reveal. (The sweep of a node still in flight is not a landing.)
|
|
833
987
|
// The A28 stash goes with it.
|
|
834
|
-
if (n.
|
|
988
|
+
if (n.T != null) n.T.ut = false, n.T.Gt = NOT_PENDING;
|
|
835
989
|
// An effect with a staged value was born held (L2): its first run is
|
|
836
990
|
// this commit's, not its creation's (A29) — queue it now.
|
|
837
991
|
if (e.i && e.i !== EFFECT_TRACKED) {
|
|
838
|
-
e.
|
|
839
|
-
globalQueue.enqueue(e.i, e.
|
|
992
|
+
e.Qe = true;
|
|
993
|
+
globalQueue.enqueue(e.i, e.W ??= GlobalQueue.K.bind(null, n));
|
|
840
994
|
}
|
|
841
995
|
}
|
|
842
996
|
// The committed value is the first observable answer for a loading-window
|
|
843
997
|
// node — the window closes here, not at compute time (#2990). Unconditional
|
|
844
998
|
// store to an always-present computed slot.
|
|
845
|
-
n.
|
|
999
|
+
n.ce = false;
|
|
846
1000
|
n.R &= ~REACTIVE_MANUAL_WRITE;
|
|
847
1001
|
// The dependencies of the pass that produced the value are the frame's now:
|
|
848
1002
|
// the previous frame's tail goes (A30, #3410; `recompute` left it for a
|
|
@@ -850,11 +1004,11 @@ function commitPendingNode(e) {
|
|
|
850
1004
|
// pass or by the node's own landing (whose pass was clean), so a set
|
|
851
1005
|
// `_error` means the last pass threw, kept its full list, and `_depsTail`
|
|
852
1006
|
// marks where it stopped.
|
|
853
|
-
if (n.
|
|
854
|
-
if (!(n.
|
|
1007
|
+
if (n.T?.F == null) trimStaleDeps(n);
|
|
1008
|
+
if (!(n.m & STATUS_PENDING)) n.m &= ~STATUS_UNINITIALIZED;
|
|
855
1009
|
// L2: the children this commit publishes are the frame's now — the frame
|
|
856
1010
|
// they replace, parked by the pass (`recompute`), goes.
|
|
857
|
-
if (!
|
|
1011
|
+
if (!o && n.T != null && (n.T.Nt !== null || n.T.ft !== null)) GlobalQueue.et(n, false, true);
|
|
858
1012
|
}
|
|
859
1013
|
|
|
860
1014
|
function commitPendingNodes() {
|
|
@@ -863,7 +1017,7 @@ function commitPendingNodes() {
|
|
|
863
1017
|
// A node a lane pass re-staged this round is the lane's to reveal; one
|
|
864
1018
|
// a transaction holds (born held by a mainline pass, `passTx`) is that
|
|
865
1019
|
// transaction's to commit, at its landing.
|
|
866
|
-
if (!(n.
|
|
1020
|
+
if (!(n.l & (CONFIG_OVERRIDE | CONFIG_HELD))) commitPendingNode(n);
|
|
867
1021
|
}
|
|
868
1022
|
pendingNodes.length = 0;
|
|
869
1023
|
}
|
|
@@ -888,7 +1042,7 @@ function flush(e) {
|
|
|
888
1042
|
}
|
|
889
1043
|
}
|
|
890
1044
|
}
|
|
891
|
-
if (globalQueue.
|
|
1045
|
+
if (globalQueue.q) {
|
|
892
1046
|
return;
|
|
893
1047
|
}
|
|
894
1048
|
if (halted) return;
|
|
@@ -912,7 +1066,7 @@ function flush(e) {
|
|
|
912
1066
|
}
|
|
913
1067
|
|
|
914
1068
|
function runQueue(e, n) {
|
|
915
|
-
for (let
|
|
1069
|
+
for (let o = 0; o < e.length; o++) e[o](n);
|
|
916
1070
|
}
|
|
917
1071
|
|
|
918
|
-
export { GlobalQueue, MAINLINE_QUESTION, ROOT_ERROR_HOOK, actionDepth, blocked, blockersOf, bumpNotifyEpoch, clock, commitPendingNode, deferZombie, dirtyQueue, enforceLoadingBoundary, enterAction, exitAction, flush, flushTransaction, globalQueue, haltReactivity, heldTrim, holdNode, insertSubs, joinFuture, joinPassTx, laneDirty, list, liveTx, merge, newTransaction, nextQuestion, notifyEpoch, ownFlights, passLane, passTx, question, queuePendingNode, releaseQueues, reruns, resetErrorHalt, resolveTx, sameLane, schedule, setPassLane, setQuestion, stagedReaders, staleReader, txOf };
|
|
1072
|
+
export { GlobalQueue, MAINLINE_QUESTION, ROOT_ERROR_HOOK, actionDepth, applyGuessesHook, blocked, blockersOf, boundarySeamHook, bumpNotifyEpoch, catchHook, clock, commitPendingNode, deferZombie, dirtyQueue, endLanesHook, enforceLoadingBoundary, enterAction, exitAction, externalUntrackHook, flush, flushTransaction, freshHook, globalQueue, haltReactivity, heldRunHook, heldTrim, heldUnder, hiddenHook, holdNode, insertSubs, installBoundaryHooks, installLaneHooks, installMarkHooks, installStoreHooks, installVerdictHooks, joinFuture, joinPassTx, laneCorrectionsHook, laneDirty, laneOutcomeHook, laneReadHook, laneSeamsHook, laneStageHook, laneWriteHook, lanesBlockedHook, list, liveTx, merge, newTransaction, nextQuestion, notifyEpoch, observeFlightHook, ownFlights, passLane, passTx, question, queuePendingNode, releaseAmbientMarksHook, releaseMarksHook, releaseQueues, reruns, resetErrorHalt, resolveTx, sameLane, schedule, setExternalHooks, setPassLane, setQuestion, stagedReaders, staleReader, storeCommitHook, storeParkHook, txOf, unhold, wireExternalSourceHook };
|