@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 } 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, n, o, t, l, s, u, i, r) {
|
|
44
|
+
laneReadHook = e;
|
|
45
|
+
laneStageHook = n;
|
|
46
|
+
laneOutcomeHook = o;
|
|
47
|
+
laneWriteHook = t;
|
|
48
|
+
applyGuessesHook = l;
|
|
49
|
+
laneSeamsHook = s;
|
|
50
|
+
laneCorrectionsHook = u;
|
|
51
|
+
endLanesHook = i;
|
|
52
|
+
lanesBlockedHook = r;
|
|
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, n, o, t, l) {
|
|
79
|
+
catchHook = e;
|
|
80
|
+
freshHook = n;
|
|
81
|
+
hiddenHook = o;
|
|
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, n) {
|
|
96
|
+
storeParkHook = e;
|
|
97
|
+
storeCommitHook = n;
|
|
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, n) {
|
|
106
|
+
releaseMarksHook = e;
|
|
107
|
+
releaseAmbientMarksHook = n;
|
|
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, n) {
|
|
122
|
+
wireExternalSourceHook = e;
|
|
123
|
+
externalUntrackHook = n;
|
|
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
131
|
/** A transaction (listed here) or a lane (lanes.ts lists its own). */ function newTransaction(e, n = null) {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
132
|
+
const o = {
|
|
133
|
+
tn: [],
|
|
134
|
+
un: [ [], [] ],
|
|
135
|
+
En: [],
|
|
136
|
+
cn: null,
|
|
26
137
|
_parent: n,
|
|
27
|
-
|
|
28
|
-
|
|
138
|
+
v: e,
|
|
139
|
+
he: null,
|
|
29
140
|
tt: 0,
|
|
30
141
|
nt: false,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
142
|
+
en: null,
|
|
143
|
+
ke: false,
|
|
144
|
+
ln: false,
|
|
145
|
+
I: null
|
|
35
146
|
};
|
|
36
|
-
if (!e) transactions.push(
|
|
37
|
-
return
|
|
147
|
+
if (!e) transactions.push(o);
|
|
148
|
+
return o;
|
|
38
149
|
}
|
|
39
150
|
|
|
40
151
|
// Lanes (plan sec. 28, 2026-10-02; lanes.ts). A lane is "a new base of a transition":
|
|
@@ -78,14 +189,14 @@ function setPassLane(e) {
|
|
|
78
189
|
* set it. */ let passTx = null;
|
|
79
190
|
|
|
80
191
|
function joinPassTx(e, n) {
|
|
81
|
-
if (globalQueue.
|
|
192
|
+
if (globalQueue.q && !n || 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.Re = resolveTx(e.T.Re);
|
|
89
200
|
}
|
|
90
201
|
|
|
91
202
|
/** `t` resolved through merges while it is still parked or open; null once
|
|
@@ -95,7 +206,7 @@ function joinPassTx(e, n) {
|
|
|
95
206
|
}
|
|
96
207
|
|
|
97
208
|
function resolveTx(e) {
|
|
98
|
-
while (e.
|
|
209
|
+
while (e.cn !== null) e = e.cn;
|
|
99
210
|
return e;
|
|
100
211
|
}
|
|
101
212
|
|
|
@@ -106,7 +217,7 @@ function resolveTx(e) {
|
|
|
106
217
|
if (n === null) {
|
|
107
218
|
flushTransaction = e === null ? newTransaction(false) : resolveTx(e);
|
|
108
219
|
} else if (e !== null) merge(resolveTx(e), n);
|
|
109
|
-
if (!globalQueue.
|
|
220
|
+
if (!globalQueue.q) schedule();
|
|
110
221
|
}
|
|
111
222
|
|
|
112
223
|
/** `t` merges into `f`: the work that touched both entangles them (A15).
|
|
@@ -116,15 +227,15 @@ function resolveTx(e) {
|
|
|
116
227
|
// into the other would close a cycle `resolveTx` never leaves.
|
|
117
228
|
e = resolveTx(e);
|
|
118
229
|
n = resolveTx(n);
|
|
119
|
-
if (e === n || e.
|
|
120
|
-
e.
|
|
230
|
+
if (e === n || e.v || n.v) return;
|
|
231
|
+
e.cn = n;
|
|
121
232
|
n.tt += e.tt;
|
|
122
233
|
n.nt ||= e.nt;
|
|
123
|
-
append(n.
|
|
124
|
-
append(n.
|
|
125
|
-
append(n.
|
|
126
|
-
append(n.
|
|
127
|
-
if (e.
|
|
234
|
+
append(n.tn, e.tn);
|
|
235
|
+
append(n.un[0], e.un[0]);
|
|
236
|
+
append(n.un[1], e.un[1]);
|
|
237
|
+
append(n.En, e.En);
|
|
238
|
+
if (e.I !== null) append(n.I ??= [], e.I);
|
|
128
239
|
transactions.splice(transactions.indexOf(e), 1);
|
|
129
240
|
}
|
|
130
241
|
|
|
@@ -150,9 +261,9 @@ function resolveTx(e) {
|
|
|
150
261
|
judge ??= e;
|
|
151
262
|
// An action still running in it holds it open (action.ts). Its lanes
|
|
152
263
|
// (lanes.ts): a lane under `t` blocks it while blocked itself.
|
|
153
|
-
const
|
|
264
|
+
const o = e.tt !== 0 || blockedBy(e.tn, e) || !!lanesBlockedHook?.(e);
|
|
154
265
|
judge = n;
|
|
155
|
-
return
|
|
266
|
+
return o;
|
|
156
267
|
}
|
|
157
268
|
|
|
158
269
|
let judge = null;
|
|
@@ -162,33 +273,33 @@ let judge = null;
|
|
|
162
273
|
* not (lanes.ts, the body-end corollary). A pending reader counts only if
|
|
163
274
|
* one of the sources it waits on is `t`'s (a render effect marked pending
|
|
164
275
|
* by a lane flight, never re-run, is the lane's wait, not the frame's). */ function ownFlights(e) {
|
|
165
|
-
return blockedBy(e.
|
|
276
|
+
return blockedBy(e.tn, e, true);
|
|
166
277
|
}
|
|
167
278
|
|
|
168
279
|
/** A source the pending node waits on that is `t`'s (held by it, or by no
|
|
169
280
|
* one) — not another transaction's or lane's work. Itself, when it is the
|
|
170
281
|
* source. */ function ownSource(e, n) {
|
|
171
|
-
const
|
|
172
|
-
if (
|
|
173
|
-
for (const e of
|
|
174
|
-
const
|
|
175
|
-
if (
|
|
282
|
+
const o = e.T?.fe;
|
|
283
|
+
if (o === undefined) return true;
|
|
284
|
+
for (const e of o) {
|
|
285
|
+
const o = e.T?.Re;
|
|
286
|
+
if (o == null || resolveTx(o) === n) return true;
|
|
176
287
|
}
|
|
177
288
|
return false;
|
|
178
289
|
}
|
|
179
290
|
|
|
180
|
-
function blockedBy(e, n,
|
|
181
|
-
for (let
|
|
182
|
-
const
|
|
183
|
-
if (!(
|
|
291
|
+
function blockedBy(e, n, o = false) {
|
|
292
|
+
for (let t = 0; t < e.length; t++) {
|
|
293
|
+
const l = e[t];
|
|
294
|
+
if (!(l.m & STATUS_PENDING) || l.R & REACTIVE_DISPOSED || l.l & CONFIG_GUESS || l.T.Re !== null && txOf(l) !== n || o && !ownSource(l, n)) continue;
|
|
184
295
|
// The frame's own pending observer (off screen: this one holds nothing;
|
|
185
296
|
// the next listed node may).
|
|
186
|
-
if (
|
|
187
|
-
if (onScreen(
|
|
297
|
+
if (l.i === EFFECT_RENDER) {
|
|
298
|
+
if (onScreen(l, judge ?? n)) return true;
|
|
188
299
|
continue;
|
|
189
300
|
}
|
|
190
|
-
for (let e = o
|
|
191
|
-
const
|
|
301
|
+
for (let e = l.o; e !== null; e = e.t) {
|
|
302
|
+
const o = e.u;
|
|
192
303
|
// A frame reader: a render effect, or a stale reader of the flight
|
|
193
304
|
// (REACTIVE_FRAME_READ — a verdict reader, `observeFlight`; lane work,
|
|
194
305
|
// `frameRead`): served committed instead of going pending, it derives
|
|
@@ -200,7 +311,7 @@ function blockedBy(e, n, t = false) {
|
|
|
200
311
|
// lane-membership ruling, 2026-10-05). An output that never committed
|
|
201
312
|
// shows nothing — a mount the frame has not revealed — and holds
|
|
202
313
|
// nothing.
|
|
203
|
-
if ((
|
|
314
|
+
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;
|
|
204
315
|
}
|
|
205
316
|
}
|
|
206
317
|
return false;
|
|
@@ -214,7 +325,7 @@ function blockedBy(e, n, t = false) {
|
|
|
214
325
|
* makes its say moot), and not behind a fallback (A33: a boundary showing its fallback is the display of
|
|
215
326
|
* everything under it, so a reader there is not on screen and holds
|
|
216
327
|
* nothing — boundaries.ts). */ function onScreen(e, n) {
|
|
217
|
-
return !(e.R & REACTIVE_ZOMBIE && removalStagedBy(e, n)) && !(
|
|
328
|
+
return !(e.R & REACTIVE_ZOMBIE && removalStagedBy(e, n)) && !hiddenHook?.(e);
|
|
218
329
|
}
|
|
219
330
|
|
|
220
331
|
/** A15 (#3463): a reader whose removal is staged — a zombie, parked by a
|
|
@@ -223,9 +334,9 @@ function blockedBy(e, n, t = false) {
|
|
|
223
334
|
* (done, and the commit disposes it; not done, and it stays parked). The
|
|
224
335
|
* staging transaction is the one holding the pass that parked the frame:
|
|
225
336
|
* the nearest non-zombie ancestor. */ function removalStagedBy(e, n) {
|
|
226
|
-
let
|
|
227
|
-
while (
|
|
228
|
-
return
|
|
337
|
+
let o = e;
|
|
338
|
+
while (o?.R & REACTIVE_ZOMBIE) o = o._parent;
|
|
339
|
+
return o?.l & CONFIG_HELD && o.T?.Re != null && txOf(o) === resolveTx(n);
|
|
229
340
|
}
|
|
230
341
|
|
|
231
342
|
/** Unchanged passes this flush whose stale dependency tail awaits the
|
|
@@ -252,17 +363,17 @@ function deferZombie(e) {
|
|
|
252
363
|
|
|
253
364
|
/** Put a staged node in a transaction: flagged, stamped, on its landing
|
|
254
365
|
* list. */ function holdNode(e, n) {
|
|
255
|
-
e.
|
|
366
|
+
e.l |= CONFIG_HELD;
|
|
256
367
|
list(e, n);
|
|
257
368
|
}
|
|
258
369
|
|
|
259
370
|
/** Put a node on a transaction's list, once: a node's `_transaction` is the
|
|
260
371
|
* transaction whose `_nodes` it is on (every site that sets one does the
|
|
261
372
|
* other), so a re-pass, a re-landing or a re-staging adds nothing. */ function list(e, n) {
|
|
262
|
-
const
|
|
263
|
-
if (
|
|
264
|
-
|
|
265
|
-
n.
|
|
373
|
+
const o = ext(e);
|
|
374
|
+
if (o.Re === n) return;
|
|
375
|
+
o.Re = n;
|
|
376
|
+
n.tn.push(e);
|
|
266
377
|
}
|
|
267
378
|
|
|
268
379
|
/** Hold the (uncommitted) frame under a held pass: every computed in the
|
|
@@ -278,21 +389,34 @@ function deferZombie(e) {
|
|
|
278
389
|
* next read of it trips over (`laneRead` → `txOf`). Its frame is the lane's
|
|
279
390
|
* too (ruling A: a lane pass's children are the lane's), revealed and
|
|
280
391
|
* retired at the lane's seam. */ function holdFrame(e, n) {
|
|
281
|
-
for (let
|
|
282
|
-
if (
|
|
283
|
-
if (
|
|
284
|
-
if (!(
|
|
285
|
-
|
|
392
|
+
for (let o = e.De; o !== null; o = o.pe) {
|
|
393
|
+
if (o.T?.Re?.v) continue;
|
|
394
|
+
if (o.X !== undefined) {
|
|
395
|
+
if (!(o.l & CONFIG_HELD)) holdNode(o, n);
|
|
396
|
+
o.l |= CONFIG_HELD_FRAME;
|
|
286
397
|
}
|
|
287
|
-
holdFrame(
|
|
398
|
+
holdFrame(o, n);
|
|
288
399
|
}
|
|
289
400
|
}
|
|
290
401
|
|
|
402
|
+
/** The nodes under `owner` a transaction other than `t` holds (boundaries.ts:
|
|
403
|
+
* what an earlier hold staged under a re-armed boundary). */ function heldUnder(e, n, o) {
|
|
404
|
+
for (let t = e.De; t !== null; t = t.pe) {
|
|
405
|
+
if ((t.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && txOf(t) !== n) o(t);
|
|
406
|
+
heldUnder(t, n, o);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/** `n` leaves the hold it was staged in: its next pass publishes mainline. */ function unhold(e) {
|
|
411
|
+
e.T.Re = null;
|
|
412
|
+
e.l &= ~CONFIG_HELD;
|
|
413
|
+
}
|
|
414
|
+
|
|
291
415
|
const dirtyQueue = {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
416
|
+
$e: new Array(2e3).fill(undefined),
|
|
417
|
+
p: false,
|
|
418
|
+
Ge: 0,
|
|
419
|
+
et: 0
|
|
296
420
|
};
|
|
297
421
|
|
|
298
422
|
let clock = 0;
|
|
@@ -355,7 +479,7 @@ function schedule() {
|
|
|
355
479
|
}
|
|
356
480
|
if (scheduled) return;
|
|
357
481
|
scheduled = true;
|
|
358
|
-
if (!syncDepth && !globalQueue.
|
|
482
|
+
if (!syncDepth && !globalQueue.q) {
|
|
359
483
|
queueMicrotask(flush);
|
|
360
484
|
}
|
|
361
485
|
}
|
|
@@ -376,6 +500,8 @@ function schedule() {
|
|
|
376
500
|
function haltReactivity(e) {
|
|
377
501
|
if (halted) return;
|
|
378
502
|
halted = true;
|
|
503
|
+
// A cleanup error collected before the halt is dropped (owner.ts).
|
|
504
|
+
rethrowCleanupError(true);
|
|
379
505
|
let n = "[REACTIVITY_HALTED]";
|
|
380
506
|
// Surface the cause here too: callers rethrow it, but a creation-time throw
|
|
381
507
|
// unwinds through ancestor recomputes that convert it to status instead of
|
|
@@ -386,9 +512,18 @@ function haltReactivity(e) {
|
|
|
386
512
|
// nothing an app or its telemetry can act on (#3338 — an uncaught throw
|
|
387
513
|
// during the hydration render). The rethrow may reach the top as well in
|
|
388
514
|
// the non-swallowed cases; a duplicate report beats a silent one.
|
|
389
|
-
const
|
|
390
|
-
|
|
391
|
-
|
|
515
|
+
const o = e !== undefined && globalThis.reportError;
|
|
516
|
+
o || e === undefined ? console.error(n) : console.error(n, e);
|
|
517
|
+
o && o(e);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Dev: `haltReactivity`, located at `subject` — the computation the uncaught
|
|
522
|
+
* error escaped from — for the REACTIVITY_HALTED message's owner path. Call
|
|
523
|
+
* sites pick it in dev builds only (`haltReactivity(e)` in production), so
|
|
524
|
+
* production keeps the one-argument halt and its bytes unchanged.
|
|
525
|
+
*/ function haltReactivityAt(e, n) {
|
|
526
|
+
haltReactivity(e);
|
|
392
527
|
}
|
|
393
528
|
|
|
394
529
|
// Logs on the first write after a halt so a frozen interaction is traceable.
|
|
@@ -404,11 +539,11 @@ function notifyHalted() {
|
|
|
404
539
|
}
|
|
405
540
|
|
|
406
541
|
class GlobalQueue {
|
|
407
|
-
|
|
408
|
-
|
|
542
|
+
un=[ [], [] ];
|
|
543
|
+
q=false;
|
|
409
544
|
flush() {
|
|
410
|
-
if (this.
|
|
411
|
-
this.
|
|
545
|
+
if (this.q) return;
|
|
546
|
+
this.q = true;
|
|
412
547
|
// A28: companions of nodes written since the last flush mirror the
|
|
413
548
|
// flushed world — re-synced inside the running window (the write is
|
|
414
549
|
// "flushed" from here on).
|
|
@@ -421,19 +556,19 @@ class GlobalQueue {
|
|
|
421
556
|
// recomputed; late subscribers (an effect reading a swept memo this
|
|
422
557
|
// flush) revive it, which is the pay-for-use contract.
|
|
423
558
|
sweepDormant();
|
|
424
|
-
runHeap(dirtyQueue, GlobalQueue.
|
|
559
|
+
runHeap(dirtyQueue, GlobalQueue._e);
|
|
425
560
|
this.settle();
|
|
426
561
|
clock++;
|
|
427
562
|
// A write the commit sweep staged with no subscriber to dirty is work
|
|
428
563
|
// too — the next round commits it.
|
|
429
|
-
scheduled = dirtyQueue.
|
|
564
|
+
scheduled = dirtyQueue.et >= dirtyQueue.Ge || pendingNodes.length !== 0;
|
|
430
565
|
this.run(EFFECT_RENDER);
|
|
431
566
|
this.run(EFFECT_USER);
|
|
432
567
|
if (false && holding) ;
|
|
433
568
|
if (false && !scheduled) ;
|
|
434
569
|
if (false) ;
|
|
435
570
|
} finally {
|
|
436
|
-
this.
|
|
571
|
+
this.q = false;
|
|
437
572
|
passTx = null;
|
|
438
573
|
}
|
|
439
574
|
}
|
|
@@ -446,7 +581,7 @@ class GlobalQueue {
|
|
|
446
581
|
// Lanes: an ended action body judges its guesses now that their passes
|
|
447
582
|
// have run (lanes.ts); a correction's re-derivations run in one more
|
|
448
583
|
// pure round — the parent's flights before the parent is judged below.
|
|
449
|
-
if (
|
|
584
|
+
if (laneCorrectionsHook?.()) runHeap(dirtyQueue, GlobalQueue._e);
|
|
450
585
|
const e = flushTransaction;
|
|
451
586
|
flushTransaction = null;
|
|
452
587
|
// The frame's verdict is in: the guesses written since the last seam
|
|
@@ -454,21 +589,21 @@ class GlobalQueue {
|
|
|
454
589
|
// parents a nested lane to it), or are dropped — the frame commits, and
|
|
455
590
|
// there is nothing to be optimistic over. Their passes run next round;
|
|
456
591
|
// a parent landing at this very seam ends the lane before they do.
|
|
457
|
-
|
|
592
|
+
applyGuessesHook?.(e);
|
|
458
593
|
// A lane's own flush (its flight landing, its reveal) parks nothing: the
|
|
459
594
|
// lane's work reached it per pass; the frame's plain writes commit.
|
|
460
595
|
// (Resolved: a guess over another transaction's guess merged them.)
|
|
461
|
-
const n = e !== null && !e.
|
|
596
|
+
const n = e !== null && !e.v ? resolveTx(e) : null;
|
|
462
597
|
if (n !== null) {
|
|
463
598
|
// `t._nodes` grows under the walk (holdFrame pushes); the bound is
|
|
464
599
|
// this flush's list, which does not.
|
|
465
600
|
const e = pendingNodes.length;
|
|
466
|
-
for (let
|
|
467
|
-
const e = pendingNodes[
|
|
601
|
+
for (let o = 0; o < e; o++) {
|
|
602
|
+
const e = pendingNodes[o];
|
|
468
603
|
// Already held: the pass that re-staged it joined its transaction,
|
|
469
604
|
// which is `t` (or merged into it). Lane work is the lane's (a
|
|
470
605
|
// pending mark propagated onto it queued it here too).
|
|
471
|
-
if (!(e.
|
|
606
|
+
if (!(e.l & (CONFIG_HELD | CONFIG_OVERRIDE)) && (e.T === null || e.T.Re === null || !e.T.Re.v)) {
|
|
472
607
|
// A loading source — pending with nothing staged and no committed
|
|
473
608
|
// value (A16, A19 exc. 1: uninitialized is loading, not pending)
|
|
474
609
|
// that is not an observer — is nobody's frame: not held. Its
|
|
@@ -477,15 +612,19 @@ class GlobalQueue {
|
|
|
477
612
|
// stale stamp was the bridge that entangled unrelated updates
|
|
478
613
|
// with a never-resolving flight). Its observers are held and
|
|
479
614
|
// wait on its flight (`blocked`).
|
|
480
|
-
if (e.
|
|
615
|
+
if (e.se === NOT_PENDING && e.m & STATUS_UNINITIALIZED && e.i !== EFFECT_RENDER) continue;
|
|
481
616
|
// A34 (2): a tick whose writes net to the committed value made no
|
|
482
617
|
// proposal (`setShow(false); setShow(true)` on a committed true) —
|
|
483
618
|
// not staged, not held, pends nothing; a later write to it is
|
|
484
619
|
// plain mainline. (A node already held stays held when written
|
|
485
620
|
// back: A34 (1).)
|
|
486
|
-
if (e.
|
|
487
|
-
|
|
488
|
-
|
|
621
|
+
if (e.se !== NOT_PENDING && !(e.m & (STATUS_PENDING | STATUS_UNINITIALIZED)) && e.T?.Ue == null && e.T?.me == null &&
|
|
622
|
+
// The comparator last: a pass that created children (an object
|
|
623
|
+
// owning a fresh async memo, #4111) must not have it read them.
|
|
624
|
+
// A NotReady read there is a change (`isSame`).
|
|
625
|
+
isSame(e, e.Ie, e.se)) {
|
|
626
|
+
e.se = NOT_PENDING;
|
|
627
|
+
e.l &= ~CONFIG_STAGED;
|
|
489
628
|
continue;
|
|
490
629
|
}
|
|
491
630
|
holdNode(e, n);
|
|
@@ -493,10 +632,10 @@ class GlobalQueue {
|
|
|
493
632
|
// A held pass's frame is held with it (ruling A, O2): the children it
|
|
494
633
|
// created are the transaction's — they compute, and a pass over them
|
|
495
634
|
// or a write that reaches them joins; they effect at the landing.
|
|
496
|
-
if (e.
|
|
635
|
+
if (e.l & CONFIG_STAGED) holdFrame(e, n);
|
|
497
636
|
}
|
|
498
637
|
pendingNodes.length = 0;
|
|
499
|
-
|
|
638
|
+
storeParkHook?.(n);
|
|
500
639
|
// (This flush's runs are stashed with `t` below — after the landings,
|
|
501
640
|
// so a `t` that lands at this very seam runs them first, ahead of
|
|
502
641
|
// the runs it held from earlier flushes.)
|
|
@@ -506,8 +645,12 @@ class GlobalQueue {
|
|
|
506
645
|
// through like the same read outside): it re-runs now, as the
|
|
507
646
|
// holder's verdict lane's work — before the lane seams, which reveal
|
|
508
647
|
// it (nothing commits this seam that could dispose it).
|
|
509
|
-
|
|
510
|
-
|
|
648
|
+
// (Verdict readers exist only with the verdict engine installed.)
|
|
649
|
+
const o = deferredZombies.splice(0);
|
|
650
|
+
if (observeFlightHook) {
|
|
651
|
+
o.sort((e, n) => e.Fe - n.Fe);
|
|
652
|
+
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]);
|
|
653
|
+
}
|
|
511
654
|
// The unchanged passes' tails stay linked (A30, #3469): their inputs
|
|
512
655
|
// are held, and the committed frame still derives from them — no trim.
|
|
513
656
|
} else {
|
|
@@ -519,7 +662,7 @@ class GlobalQueue {
|
|
|
519
662
|
// `recompute`'s own gates.
|
|
520
663
|
for (let e = 0; e < heldTrims.length; e++) {
|
|
521
664
|
const n = heldTrims[e];
|
|
522
|
-
if (n.
|
|
665
|
+
if (n.T?.F == null && !n.Ae) trimStaleDeps(n);
|
|
523
666
|
}
|
|
524
667
|
}
|
|
525
668
|
// The list is the flush's either way. Reset only when there is something
|
|
@@ -533,19 +676,21 @@ class GlobalQueue {
|
|
|
533
676
|
// removes its entry.
|
|
534
677
|
// This flush's own runs, the lanes' reveals, and the landings' held runs
|
|
535
678
|
// are collected apart and ordered below.
|
|
536
|
-
const
|
|
537
|
-
const
|
|
679
|
+
const o = this.un;
|
|
680
|
+
const t = this.un = [ [], [] ];
|
|
538
681
|
// The passes that read this frame's stagings as the screen read held
|
|
539
682
|
// writes if it parked: the lane seam re-derives them on the committed
|
|
540
683
|
// world (a lane's runs wait this round).
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
684
|
+
if (laneSeamsHook) {
|
|
685
|
+
laneSeamsHook(n !== null ? stagedReaders : null);
|
|
686
|
+
if (stagedReaders.length) stagedReaders.length = 0;
|
|
687
|
+
}
|
|
688
|
+
this.un = [ [], [] ];
|
|
544
689
|
for (let e = transactions.length - 1; e >= 0; e--) {
|
|
545
690
|
const n = transactions[e];
|
|
546
691
|
if (blocked(n)) continue;
|
|
547
692
|
transactions.splice(e, 1);
|
|
548
|
-
|
|
693
|
+
endLanesHook?.(n);
|
|
549
694
|
land(n);
|
|
550
695
|
// A landing's commits can dispose a zombie a transaction judged above
|
|
551
696
|
// was blocked on (#3463: live "until the commit that disposes it") —
|
|
@@ -554,13 +699,13 @@ class GlobalQueue {
|
|
|
554
699
|
}
|
|
555
700
|
// The store folds the pending backings whose container nodes committed
|
|
556
701
|
// — this flush's, or a landing's (store/store.ts installs it).
|
|
557
|
-
|
|
702
|
+
storeCommitHook?.();
|
|
558
703
|
// Marks declared outside a transaction: the flush that carried them
|
|
559
704
|
// parked (something async below) — the window is the frame's, the marks
|
|
560
705
|
// are its transaction's; else they release at the seam, verdict-only,
|
|
561
706
|
// nothing to show (affects.ts).
|
|
562
|
-
const
|
|
563
|
-
|
|
707
|
+
const l = n && liveTx(n);
|
|
708
|
+
releaseAmbientMarksHook?.(l);
|
|
564
709
|
// The effect phase: lane work first (displayed ahead of the frame), then
|
|
565
710
|
// this flush's runs, then the runs the landings held from earlier
|
|
566
711
|
// flushes — as one pass would have queued them (#3540: a shell
|
|
@@ -568,40 +713,40 @@ class GlobalQueue {
|
|
|
568
713
|
// write; #3528: a display-ahead swap before the count it is shown
|
|
569
714
|
// beside). A parked frame's runs wait with its transaction instead: the
|
|
570
715
|
// effect phase applies a committed frame.
|
|
571
|
-
const
|
|
716
|
+
const s = l !== null;
|
|
572
717
|
for (let e = 0; e < 2; e++) {
|
|
573
|
-
if (
|
|
718
|
+
if (s) append(l.un[e], o[e]);
|
|
574
719
|
// The plain flush — no reveal, no landing, not parked — has nothing to
|
|
575
720
|
// order around its own runs: they are the queue as they stand (the
|
|
576
721
|
// same array, not a copy; `run` takes it whole). Every other seam
|
|
577
722
|
// builds the ordered queue above.
|
|
578
|
-
this.
|
|
723
|
+
this.un[e] = s || t[e].length || this.un[e].length ? t[e].concat(s ? [] : o[e], this.un[e]) : o[e];
|
|
579
724
|
}
|
|
580
725
|
if (deferredZombies.length !== 0) {
|
|
581
726
|
// Survivors are the displayed frame of a held pass: they rerun for the
|
|
582
727
|
// live write, parents before children.
|
|
583
|
-
const e = deferredZombies.splice(0).sort((e, n) => e.
|
|
728
|
+
const e = deferredZombies.splice(0).sort((e, n) => e.Fe - n.Fe);
|
|
584
729
|
for (let n = 0; n < e.length; n++) {
|
|
585
730
|
if (!(e[n].R & REACTIVE_DISPOSED)) recompute(e[n]);
|
|
586
731
|
}
|
|
587
732
|
}
|
|
588
733
|
// Boundaries: readers that settled, landed or died this flush are
|
|
589
734
|
// dropped; a fallback with none left reveals next round.
|
|
590
|
-
|
|
735
|
+
boundarySeamHook?.();
|
|
591
736
|
}
|
|
592
737
|
run(e) {
|
|
593
|
-
const n = this.
|
|
738
|
+
const n = this.un[e - 1];
|
|
594
739
|
if (n.length) {
|
|
595
|
-
this.
|
|
740
|
+
this.un[e - 1] = [];
|
|
596
741
|
runQueue(n, e);
|
|
597
742
|
}
|
|
598
743
|
}
|
|
599
|
-
enqueue(e, n,
|
|
744
|
+
enqueue(e, n, o = passLane) {
|
|
600
745
|
// Lane work's runs are the lane's: they run at its reveal, not with
|
|
601
746
|
// the frame (which may park). A lane pass that read the frame's own
|
|
602
747
|
// staging is the frame's (`recompute` passes null): its run waits with
|
|
603
748
|
// the frame.
|
|
604
|
-
if (e) (
|
|
749
|
+
if (e) (o !== null ? o : this).un[e - 1].push(n);
|
|
605
750
|
schedule();
|
|
606
751
|
}
|
|
607
752
|
/** Status reaching the root: pending is absorbed (the root mount defers —
|
|
@@ -617,10 +762,10 @@ class GlobalQueue {
|
|
|
617
762
|
*
|
|
618
763
|
* A boundary between the observer and the root (boundaries.ts) is asked
|
|
619
764
|
* first: a fallback catches what is not ready under it, and the root
|
|
620
|
-
* never hears of it. */ notify(e, n,
|
|
621
|
-
if (
|
|
765
|
+
* never hears of it. */ notify(e, n, o, t) {
|
|
766
|
+
if (catchHook?.(e, o & n, t)) return true;
|
|
622
767
|
if (n & STATUS_PENDING) {
|
|
623
|
-
if (
|
|
768
|
+
if (o & STATUS_PENDING) {
|
|
624
769
|
// A pass that went pending holds its frame: the async's own
|
|
625
770
|
// synchronous frame is not ready (A15 reveal corollary: a reveal
|
|
626
771
|
// that discovers a flight whose inputs are visible holds and joins
|
|
@@ -652,37 +797,41 @@ class GlobalQueue {
|
|
|
652
797
|
* for it is void (#3322: an effect has one value slot, and the stale pass
|
|
653
798
|
* overwrote the value that run was for) — the re-derivation's run replaces
|
|
654
799
|
* it. */ function reruns(e) {
|
|
655
|
-
for (let n = 0; n < e.
|
|
656
|
-
const
|
|
800
|
+
for (let n = 0; n < e.En.length; n++) {
|
|
801
|
+
const o = e.En[n];
|
|
657
802
|
// An errored reader's display is its error (D-39): the landing gives it
|
|
658
803
|
// nothing to re-derive, and a re-run would be a read that retries the
|
|
659
804
|
// errored source (#3945).
|
|
660
|
-
if (
|
|
661
|
-
if (
|
|
662
|
-
|
|
663
|
-
|
|
805
|
+
if (o.R & REACTIVE_DISPOSED || o.m & STATUS_ERROR) continue;
|
|
806
|
+
if (o.R & REACTIVE_FRAME_READ || o.l & CONFIG_VERDICT) {
|
|
807
|
+
o.R &= ~REACTIVE_FRAME_READ;
|
|
808
|
+
o.Ae = false;
|
|
664
809
|
// Its frame is replaced by the re-derivation: a run one of its
|
|
665
810
|
// children queued this flush (re-run by the same landing) is for the
|
|
666
811
|
// frame being replaced — void too (#3404).
|
|
667
|
-
for (let e =
|
|
668
|
-
enqueueSub(
|
|
812
|
+
for (let e = o.De; e !== null; e = e.pe) if (e.i) e.Ae = false;
|
|
813
|
+
enqueueSub(o);
|
|
669
814
|
}
|
|
670
815
|
}
|
|
671
|
-
e.
|
|
816
|
+
e.En.length = 0;
|
|
672
817
|
}
|
|
673
818
|
|
|
674
819
|
/** The landing: the held frame becomes the frame. */ function land(e) {
|
|
675
820
|
reruns(e);
|
|
676
|
-
if (e.
|
|
821
|
+
if (e.I !== null) releaseMarksHook(e.I);
|
|
677
822
|
// Old children die in the commits (cleanups first), then the stashed
|
|
678
823
|
// effects run ahead of this flush's own. A node a lane took over since
|
|
679
824
|
// (a guess written over the staged truth) is the lane's to land.
|
|
680
|
-
for (let n = 0; n < e.
|
|
681
|
-
const
|
|
682
|
-
if (
|
|
683
|
-
|
|
684
|
-
commitPendingNode(
|
|
825
|
+
for (let n = 0; n < e.tn.length; n++) {
|
|
826
|
+
const o = e.tn[n];
|
|
827
|
+
if (o.T.Re === null || txOf(o) !== e) continue;
|
|
828
|
+
o.T.Re = null;
|
|
829
|
+
commitPendingNode(o);
|
|
685
830
|
}
|
|
831
|
+
// A cleanup those commits ran that wrote a node `u` still held joined
|
|
832
|
+
// `u`, which has left `transactions`: the write is this landing's, and
|
|
833
|
+
// the next flush must not park in it (#4089).
|
|
834
|
+
if (flushTransaction === e) flushTransaction = null;
|
|
686
835
|
releaseQueues(e);
|
|
687
836
|
}
|
|
688
837
|
|
|
@@ -691,13 +840,13 @@ class GlobalQueue {
|
|
|
691
840
|
* pass would have queued them — a shell re-derived at the landing before a
|
|
692
841
|
* boundary swap held since the write (#3540's log order). */ function releaseQueues(e) {
|
|
693
842
|
for (let n = 0; n < 2; n++) {
|
|
694
|
-
append(globalQueue.
|
|
695
|
-
e.
|
|
843
|
+
append(globalQueue.un[n], e.un[n]);
|
|
844
|
+
e.un[n] = [];
|
|
696
845
|
}
|
|
697
846
|
}
|
|
698
847
|
|
|
699
848
|
/** `a.push(...b)` without the argument-count limit. */ function append(e, n) {
|
|
700
|
-
for (let
|
|
849
|
+
for (let o = 0; o < n.length; o++) e.push(n[o]);
|
|
701
850
|
}
|
|
702
851
|
|
|
703
852
|
/** Nodes staged this flush (`_pendingValue` set), committed at its end. */ const pendingNodes = [];
|
|
@@ -712,13 +861,13 @@ class GlobalQueue {
|
|
|
712
861
|
* after `t`'s landing (`_reruns`). Once per pass. */ function staleReader(e, n) {
|
|
713
862
|
if (!(e.R & REACTIVE_FRAME_READ)) {
|
|
714
863
|
e.R |= REACTIVE_FRAME_READ;
|
|
715
|
-
n.
|
|
864
|
+
n.En.push(e);
|
|
716
865
|
}
|
|
717
866
|
}
|
|
718
867
|
|
|
719
868
|
function queuePendingNode(e) {
|
|
720
869
|
pendingNodes.push(e);
|
|
721
|
-
if (!globalQueue.
|
|
870
|
+
if (!globalQueue.q) markUnflushedStaged();
|
|
722
871
|
// A28
|
|
723
872
|
}
|
|
724
873
|
|
|
@@ -734,14 +883,14 @@ function bumpNotifyEpoch() {
|
|
|
734
883
|
function insertSubs(e) {
|
|
735
884
|
// §12d: stamp before walking — setSignal's staged-rewrite fast path skips
|
|
736
885
|
// the next walk for this node while the epoch holds (marking is idempotent).
|
|
737
|
-
e.
|
|
886
|
+
e.Me = notifyEpoch;
|
|
738
887
|
// Presence bits gate the optional-slot probes (see constants.ts): one
|
|
739
888
|
// masked read of the always-present _config instead of missing-property
|
|
740
889
|
// lookups in the hottest notify loop.
|
|
741
|
-
const n = e.
|
|
742
|
-
const
|
|
743
|
-
for (let n = e.
|
|
744
|
-
const e = n.
|
|
890
|
+
const n = e.l;
|
|
891
|
+
const o = (n & CONFIG_HAS_SNAPSHOT) !== 0 && e.T?.Pe !== undefined;
|
|
892
|
+
for (let n = e.o; n !== null; n = n.t) {
|
|
893
|
+
const e = n.u;
|
|
745
894
|
// Missed-wake latch (#3037): this write is landing while the subscriber
|
|
746
895
|
// is mid-recompute (a nested pull committing beneath its reads), and the
|
|
747
896
|
// heap refuses RECOMPUTING nodes. A gen-current link means the pass
|
|
@@ -750,8 +899,8 @@ function insertSubs(e) {
|
|
|
750
899
|
// pass either re-reads them fresh or trims them), and neither does the
|
|
751
900
|
// tail link: it is the read IN FLIGHT — read() links before it pulls, so
|
|
752
901
|
// this very commit is what that read returns.
|
|
753
|
-
if (e.R & REACTIVE_RECOMPUTING_DEPS && n.
|
|
754
|
-
if (
|
|
902
|
+
if (e.R & REACTIVE_RECOMPUTING_DEPS && n.Te === e.Ne && n !== e._) e.R |= REACTIVE_MISSED_WAKE;
|
|
903
|
+
if (o && e.l & CONFIG_IN_SNAPSHOT_SCOPE) {
|
|
755
904
|
e.R |= REACTIVE_SNAPSHOT_STALE;
|
|
756
905
|
continue;
|
|
757
906
|
}
|
|
@@ -767,14 +916,14 @@ function insertSubs(e) {
|
|
|
767
916
|
* is a live write if the effect becomes a zombie later this flush — an
|
|
768
917
|
* on-screen reader whose removal an adopted write stages follows the lane
|
|
769
918
|
* (the lane-membership ruling, 2026-10-05; `_update`). */ function laneDirty(e, n) {
|
|
770
|
-
for (let
|
|
771
|
-
const e =
|
|
772
|
-
if (e?.
|
|
919
|
+
for (let o = e.o; o !== null; o = o.t) {
|
|
920
|
+
const e = o.u.T?.Re;
|
|
921
|
+
if (e?.v ? sameLane(e, n) : o.u.i) o.u.R |= REACTIVE_LANE_DIRTY;
|
|
773
922
|
}
|
|
774
923
|
}
|
|
775
924
|
|
|
776
925
|
/** One lane, or two of one link group (#3335): one reveal unit. */ function sameLane(e, n) {
|
|
777
|
-
return e === n || e.
|
|
926
|
+
return e === n || e.en !== null && e.en === n.en;
|
|
778
927
|
}
|
|
779
928
|
|
|
780
929
|
function commitPendingNode(e) {
|
|
@@ -784,40 +933,52 @@ function commitPendingNode(e) {
|
|
|
784
933
|
// staged) stays uncommitted: a re-pass disposes its children on the spot,
|
|
785
934
|
// and the frame it parked stays the frame until a pass of this node
|
|
786
935
|
// commits a value.
|
|
787
|
-
if (!n.
|
|
788
|
-
e.
|
|
789
|
-
if (e.
|
|
790
|
-
e.
|
|
791
|
-
e.
|
|
936
|
+
if (!n.X) {
|
|
937
|
+
e.l &= -385;
|
|
938
|
+
if (e.se !== NOT_PENDING) {
|
|
939
|
+
e.Ie = e.se;
|
|
940
|
+
e.se = NOT_PENDING;
|
|
792
941
|
}
|
|
793
942
|
return;
|
|
794
943
|
}
|
|
795
944
|
// Computeds only from here (`_statusFlags` is not a signal field). A
|
|
796
945
|
// commit beneath a flight publishes its inputs (A15 reveal corollary,
|
|
797
946
|
// #3305): a reveal of it must observe, not show the pre-flight value.
|
|
798
|
-
const
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
947
|
+
const o = e.se === NOT_PENDING && (n.m & STATUS_PENDING) !== 0;
|
|
948
|
+
const t = e.l & CONFIG_STAGED_ERROR;
|
|
949
|
+
// An answer commits: the error it replaces leaves the display (D-41).
|
|
950
|
+
if (!o && n.T) n.T.de = undefined;
|
|
951
|
+
e.l = o ? e.l & -1048833 | CONFIG_INPUTS_PUBLISHED : e.l & -3147137;
|
|
952
|
+
// D-41: the staged answer is an error — published now, when a value would
|
|
953
|
+
// show, through the ordinary error path (`stageError`). Its readers first
|
|
954
|
+
// see it in the cycle after this seam: a tracked re-read there is not a
|
|
955
|
+
// retry (`read`), or a boundary re-derived by it would retry it forever.
|
|
956
|
+
if (t) {
|
|
957
|
+
const o = e.se;
|
|
958
|
+
e.se = NOT_PENDING;
|
|
959
|
+
e.ie = clock + 1;
|
|
960
|
+
notifyStatus(n, STATUS_ERROR, o);
|
|
961
|
+
} else if (e.se !== NOT_PENDING) {
|
|
962
|
+
e.Ie = e.se;
|
|
963
|
+
e.se = NOT_PENDING;
|
|
803
964
|
// A node born staged (recompute) initializes at this commit.
|
|
804
|
-
n.
|
|
965
|
+
n.m &= ~STATUS_UNINITIALIZED;
|
|
805
966
|
// A quiet re-ask's classification survives its landing and dies with
|
|
806
967
|
// the landed value's commit (A19 exc. 2, #3178): verdict-quiet through
|
|
807
968
|
// the reveal. (The sweep of a node still in flight is not a landing.)
|
|
808
969
|
// The A28 stash goes with it.
|
|
809
|
-
if (n.
|
|
970
|
+
if (n.T != null) n.T.Ve = false, n.T.je = NOT_PENDING;
|
|
810
971
|
// An effect with a staged value was born held (L2): its first run is
|
|
811
972
|
// this commit's, not its creation's (A29) — queue it now.
|
|
812
|
-
if (e.
|
|
813
|
-
e.
|
|
814
|
-
globalQueue.enqueue(e.
|
|
973
|
+
if (e.i && e.i !== EFFECT_TRACKED) {
|
|
974
|
+
e.Ae = true;
|
|
975
|
+
globalQueue.enqueue(e.i, e.W ??= GlobalQueue.K.bind(null, n));
|
|
815
976
|
}
|
|
816
977
|
}
|
|
817
978
|
// The committed value is the first observable answer for a loading-window
|
|
818
979
|
// node — the window closes here, not at compute time (#2990). Unconditional
|
|
819
980
|
// store to an always-present computed slot.
|
|
820
|
-
n.
|
|
981
|
+
n.ce = false;
|
|
821
982
|
n.R &= ~REACTIVE_MANUAL_WRITE;
|
|
822
983
|
// The dependencies of the pass that produced the value are the frame's now:
|
|
823
984
|
// the previous frame's tail goes (A30, #3410; `recompute` left it for a
|
|
@@ -825,11 +986,11 @@ function commitPendingNode(e) {
|
|
|
825
986
|
// pass or by the node's own landing (whose pass was clean), so a set
|
|
826
987
|
// `_error` means the last pass threw, kept its full list, and `_depsTail`
|
|
827
988
|
// marks where it stopped.
|
|
828
|
-
if (n.
|
|
829
|
-
if (!(n.
|
|
989
|
+
if (n.T?.F == null) trimStaleDeps(n);
|
|
990
|
+
if (!(n.m & STATUS_PENDING)) n.m &= ~STATUS_UNINITIALIZED;
|
|
830
991
|
// L2: the children this commit publishes are the frame's now — the frame
|
|
831
992
|
// they replace, parked by the pass (`recompute`), goes.
|
|
832
|
-
if (!
|
|
993
|
+
if (!o && n.T != null && (n.T.Ue !== null || n.T.me !== null)) GlobalQueue.Ce(n, false, true);
|
|
833
994
|
}
|
|
834
995
|
|
|
835
996
|
function commitPendingNodes() {
|
|
@@ -838,7 +999,7 @@ function commitPendingNodes() {
|
|
|
838
999
|
// A node a lane pass re-staged this round is the lane's to reveal; one
|
|
839
1000
|
// a transaction holds (born held by a mainline pass, `passTx`) is that
|
|
840
1001
|
// transaction's to commit, at its landing.
|
|
841
|
-
if (!(n.
|
|
1002
|
+
if (!(n.l & (CONFIG_OVERRIDE | CONFIG_HELD))) commitPendingNode(n);
|
|
842
1003
|
}
|
|
843
1004
|
pendingNodes.length = 0;
|
|
844
1005
|
}
|
|
@@ -863,17 +1024,20 @@ function flush(e) {
|
|
|
863
1024
|
}
|
|
864
1025
|
}
|
|
865
1026
|
}
|
|
866
|
-
if (globalQueue.
|
|
1027
|
+
if (globalQueue.q) {
|
|
867
1028
|
return;
|
|
868
1029
|
}
|
|
869
1030
|
if (halted) return;
|
|
870
1031
|
while (scheduled) {
|
|
871
1032
|
globalQueue.flush();
|
|
872
1033
|
}
|
|
1034
|
+
// A cleanup that threw during the drain, now that the drain is complete
|
|
1035
|
+
// (owner.ts).
|
|
1036
|
+
rethrowCleanupError();
|
|
873
1037
|
}
|
|
874
1038
|
|
|
875
1039
|
function runQueue(e, n) {
|
|
876
|
-
for (let
|
|
1040
|
+
for (let o = 0; o < e.length; o++) e[o](n);
|
|
877
1041
|
}
|
|
878
1042
|
|
|
879
|
-
export { GlobalQueue, MAINLINE_QUESTION, ROOT_ERROR_HOOK, actionDepth, blocked, 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 };
|
|
1043
|
+
export { GlobalQueue, MAINLINE_QUESTION, ROOT_ERROR_HOOK, actionDepth, applyGuessesHook, blocked, 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 };
|