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