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