@solidjs/signals 2.0.0-rc.17 → 2.0.0-rc.19
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 +426 -241
- package/dist/dev.attribution.js +19 -16
- package/dist/dev.js +768 -409
- package/dist/observe/boundaries.js +88 -67
- package/dist/observe/core/action.js +8 -0
- package/dist/observe/core/async.js +183 -138
- package/dist/observe/core/attribution.js +30 -28
- package/dist/observe/core/constants.js +23 -1
- package/dist/observe/core/core.js +185 -157
- package/dist/observe/core/effect.js +34 -37
- package/dist/observe/core/graph.js +8 -8
- package/dist/observe/core/heap.js +5 -5
- package/dist/observe/core/lanes.js +104 -84
- package/dist/observe/core/owner.js +17 -16
- package/dist/observe/core/scheduler.js +61 -47
- package/dist/observe/core/truth.js +6 -6
- package/dist/observe/core/verdict.js +15 -15
- package/dist/observe/deferred.js +40 -38
- package/dist/observe/from.js +248 -0
- package/dist/observe/index.js +4 -2
- package/dist/observe/map.js +22 -22
- package/dist/observe/reveal.js +5 -5
- package/dist/observe/signals.js +57 -15
- package/dist/observe/store/affects.js +1 -1
- package/dist/observe/store/hooks.js +3 -0
- package/dist/observe/store/optimistic.js +11 -11
- package/dist/observe/store/projection.js +34 -21
- package/dist/observe/store/reconcile.js +138 -122
- package/dist/observe/store/store.js +628 -641
- package/dist/observe/store/target.js +1 -5
- package/dist/observe/store/utils.js +7 -2
- package/dist/prod/boundaries.js +153 -132
- package/dist/prod/core/action.js +8 -0
- package/dist/prod/core/async.js +242 -197
- package/dist/prod/core/constants.js +16 -1
- package/dist/prod/core/core.js +166 -138
- package/dist/prod/core/effect.js +31 -34
- package/dist/prod/core/graph.js +5 -5
- package/dist/prod/core/heap.js +12 -12
- package/dist/prod/core/lanes.js +98 -78
- package/dist/prod/core/owner.js +15 -14
- package/dist/prod/core/scheduler.js +64 -50
- package/dist/prod/core/truth.js +6 -6
- package/dist/prod/core/verdict.js +17 -17
- package/dist/prod/deferred.js +40 -38
- package/dist/prod/from.js +248 -0
- package/dist/prod/index.js +4 -2
- package/dist/prod/map.js +14 -14
- package/dist/prod/reveal.js +8 -8
- package/dist/prod/signals.js +46 -4
- package/dist/prod/store/affects.js +1 -1
- package/dist/prod/store/hooks.js +3 -0
- package/dist/prod/store/optimistic.js +16 -16
- package/dist/prod/store/projection.js +22 -9
- package/dist/prod/store/reconcile.js +138 -122
- package/dist/prod/store/store.js +308 -331
- package/dist/prod/store/target.js +13 -17
- package/dist/prod/store/utils.js +2 -0
- package/dist/types/core/action.d.ts +8 -0
- package/dist/types/core/async.d.ts +12 -2
- package/dist/types/core/constants.d.ts +25 -0
- package/dist/types/core/core.d.ts +33 -4
- package/dist/types/core/dev.d.ts +1 -1
- package/dist/types/core/index.d.ts +1 -1
- package/dist/types/core/lanes.d.ts +5 -1
- package/dist/types/core/scheduler.d.ts +17 -3
- package/dist/types/from.d.ts +47 -0
- package/dist/types/index.d.ts +4 -1
- package/dist/types/signals.d.ts +37 -0
- package/dist/types/store/target.d.ts +2 -0
- package/package.json +1 -1
package/dist/prod/core/core.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { notifyStatus, releaseFlightTeardown, handleAsync, clearStatus, parkLoadingWindow, stageError, settlePendingSource, releaseIfSettledUnobserved, settleErroredDependents } from "./async.js";
|
|
2
2
|
|
|
3
|
-
import { STATUS_UNINITIALIZED, REACTIVE_LAZY, REACTIVE_NONE, defaultContext, CONFIG_TRANSPARENT, CONFIG_OWNED_WRITE, CONFIG_AUTO_DISPOSE, CONFIG_SYNC, CONFIG_NO_SNAPSHOT, CONFIG_IN_SNAPSHOT_SCOPE, NOT_PENDING,
|
|
3
|
+
import { STATUS_UNINITIALIZED, REACTIVE_LAZY, REACTIVE_NONE, defaultContext, CONFIG_TRANSPARENT, CONFIG_OWNED_WRITE, CONFIG_AUTO_DISPOSE, CONFIG_SYNC, CONFIG_NO_SNAPSHOT, CONFIG_IN_SNAPSHOT_SCOPE, NOT_PENDING, CONFIG_HELD, REACTIVE_MANUAL_WRITE, STATUS_ERROR, REACTIVE_DIRTY, CONFIG_OVERRIDE, NO_SNAPSHOT, CONFIG_HAS_SNAPSHOT, REACTIVE_DISPOSED, REACTIVE_CHECK, REACTIVE_IN_HEAP, REACTIVE_REASK, REACTIVE_SNAPSHOT_STALE, CONFIG_CHILDREN_FORBIDDEN, CONFIG_VERDICT, STATUS_PENDING, REACTIVE_LANE_DIRTY, CONFIG_GUESS, REACTIVE_RECOMPUTING_DEPS, CONFIG_LAGS, EFFECT_RENDER, CONFIG_HELD_FRAME, EFFECT_TRACKED, CONFIG_STAGED, CONFIG_STAGED_ERROR, REACTIVE_ZOMBIE, REACTIVE_MISSED_WAKE, REACTIVE_JOINED, REACTIVE_STAGED_READ, REACTIVE_PROBED, REACTIVE_FRAME_READ, REACTIVE_LANE_READ, REACTIVE_SCREEN_READ, REACTIVE_PROBE_UNANSWERED, CAUGHT_REARMED, CONFIG_PROMOTED, CONFIG_INPUTS_PUBLISHED, CONFIG_FRESH_READ, REACTIVE_IN_HEAP_HEIGHT, CONFIG_SLOT_NODE, EFFECT_USER, NewSet, STATUS_NONE } from "./constants.js";
|
|
4
4
|
|
|
5
5
|
import { NotReadyError } from "./error.js";
|
|
6
6
|
|
|
@@ -24,7 +24,7 @@ GlobalQueue._e = e => {
|
|
|
24
24
|
if (e.i === EFFECT_TRACKED) {
|
|
25
25
|
deleteFromHeap(e, dirtyQueue);
|
|
26
26
|
e.Ae = true;
|
|
27
|
-
globalQueue.enqueue(EFFECT_USER, e.
|
|
27
|
+
globalQueue.enqueue(EFFECT_USER, e.Z);
|
|
28
28
|
}
|
|
29
29
|
// L2: a zombie's fate is the seam's — its owner's commit disposes it, or
|
|
30
30
|
// the park cancels the pass (the write that dirtied it is held). It runs
|
|
@@ -51,7 +51,7 @@ GlobalQueue._e = e => {
|
|
|
51
51
|
* hole's render effect). */ function laneFrameMember(e) {
|
|
52
52
|
let t = e._parent;
|
|
53
53
|
while (t.R & REACTIVE_ZOMBIE) t = t._parent;
|
|
54
|
-
return (t.l & (CONFIG_OVERRIDE | CONFIG_GUESS)) === CONFIG_OVERRIDE || t.i && t.T?.Re?.
|
|
54
|
+
return (t.l & (CONFIG_OVERRIDE | CONFIG_GUESS)) === CONFIG_OVERRIDE || t.i && t.T?.Re?.P;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
GlobalQueue.Ce = disposeChildren;
|
|
@@ -78,11 +78,23 @@ function ownerInSnapshotScope(e) {
|
|
|
78
78
|
|
|
79
79
|
function setSnapshotCapture(e) {
|
|
80
80
|
snapshotCaptureActive = e;
|
|
81
|
-
if (e && !snapshotSources) snapshotSources = new
|
|
81
|
+
if (e && !snapshotSources) snapshotSources = new NewSet;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
/**
|
|
85
|
+
* Marks `owner` a snapshot scope: while capture is on, the readers created
|
|
86
|
+
* under it serve each source's snapshot (the value it had when hydration
|
|
87
|
+
* began) and replay at `releaseSnapshotScope`.
|
|
88
|
+
*
|
|
89
|
+
* With `served` (hydration), `owner` is instead a hydrated computed and
|
|
90
|
+
* `served` its server answer — what the server rendered every reader of the
|
|
91
|
+
* node with — landing after the node was created pending (so the creation
|
|
92
|
+
* arm skipped it). It becomes the node's snapshot when it has none, whether
|
|
93
|
+
* the node committed the answer or a client write had already taken its
|
|
94
|
+
* compute over. A claim made later reads the value its markup shows, and
|
|
95
|
+
* replays the node's own value at its release, as for a written signal.
|
|
96
|
+
*/ function markSnapshotScope(e, t) {
|
|
97
|
+
if (arguments.length < 2) e.Oe = true; else if (snapshotCaptureActive && e.T?.De === undefined) takeSnapshot(e, t);
|
|
86
98
|
}
|
|
87
99
|
|
|
88
100
|
function releaseSnapshotScope(e) {
|
|
@@ -92,10 +104,10 @@ function releaseSnapshotScope(e) {
|
|
|
92
104
|
}
|
|
93
105
|
|
|
94
106
|
function releaseSubtree(e) {
|
|
95
|
-
let t = e.
|
|
107
|
+
let t = e.pe;
|
|
96
108
|
while (t) {
|
|
97
109
|
if (t.Oe) {
|
|
98
|
-
t = t.
|
|
110
|
+
t = t.Ge;
|
|
99
111
|
continue;
|
|
100
112
|
}
|
|
101
113
|
if (t.X) {
|
|
@@ -104,12 +116,12 @@ function releaseSubtree(e) {
|
|
|
104
116
|
if (e.R & REACTIVE_SNAPSHOT_STALE) {
|
|
105
117
|
e.R &= ~REACTIVE_SNAPSHOT_STALE;
|
|
106
118
|
e.R |= REACTIVE_DIRTY;
|
|
107
|
-
if (dirtyQueue.
|
|
119
|
+
if (dirtyQueue.Fe > e.he) dirtyQueue.Fe = e.he;
|
|
108
120
|
insertIntoHeap(e, dirtyQueue);
|
|
109
121
|
}
|
|
110
122
|
}
|
|
111
123
|
releaseSubtree(t);
|
|
112
|
-
t = t.
|
|
124
|
+
t = t.Ge;
|
|
113
125
|
}
|
|
114
126
|
}
|
|
115
127
|
|
|
@@ -121,7 +133,7 @@ function clearSnapshots() {
|
|
|
121
133
|
// `!== undefined` — assign, don't `delete`: deleting a field pushes the
|
|
122
134
|
// object to dictionary mode for every later read of every field.
|
|
123
135
|
const t = e.T;
|
|
124
|
-
if (t != null) t.
|
|
136
|
+
if (t != null) t.De = undefined;
|
|
125
137
|
}
|
|
126
138
|
snapshotSources = null;
|
|
127
139
|
}
|
|
@@ -156,14 +168,14 @@ function recompute(e, t = false) {
|
|
|
156
168
|
// content creates is that mount's too (#4166). Lanes only, like the seat.
|
|
157
169
|
const s = laneRoot;
|
|
158
170
|
const i = laneStageHook && (laneRoot = t && (E || s));
|
|
159
|
-
setPassLane(!laneStageHook ? null : (e.R & REACTIVE_LANE_DIRTY || (e.l & (CONFIG_OVERRIDE | CONFIG_GUESS)) === CONFIG_OVERRIDE) && e.T?.Re?.
|
|
160
|
-
t && E?._parent.
|
|
171
|
+
setPassLane(!laneStageHook ? null : (e.R & REACTIVE_LANE_DIRTY || (e.l & (CONFIG_OVERRIDE | CONFIG_GUESS)) === CONFIG_OVERRIDE) && e.T?.Re?.P ? e.T.Re : // Loose `!=`: false with no creator lane (undefined == null).
|
|
172
|
+
t && E?._parent.Pe != E && creatorPass(context)?.R & REACTIVE_RECOMPUTING_DEPS ? E : null);
|
|
161
173
|
// CARVE 2: lane posture resolution (OPTIMISTIC_DIRTY, derived-override
|
|
162
174
|
// re-derivation, lane adoption through deps) went with the optimistic engine.
|
|
163
175
|
if (!t) {
|
|
164
176
|
deleteFromHeap(e, dirtyQueue);
|
|
165
177
|
if (e.T !== null) {
|
|
166
|
-
e.T.
|
|
178
|
+
e.T.Ee = null;
|
|
167
179
|
// Supersede is where an iterator flight dies (#3122): close it now.
|
|
168
180
|
// Idempotent with the cleanup-channel close.
|
|
169
181
|
releaseFlightTeardown(e);
|
|
@@ -179,6 +191,8 @@ function recompute(e, t = false) {
|
|
|
179
191
|
// A29, or mounted by a pass of it still awaiting its commit, #4010) keeps
|
|
180
192
|
// staging: live writes keep it current, it effects at the swap (ruling
|
|
181
193
|
// A).
|
|
194
|
+
// A boundary's structure (CONFIG_FRAME) is the frame like a render
|
|
195
|
+
// effect: it publishes mainline outside its transaction's flush (#4283).
|
|
182
196
|
// A pending flight is the transition's own work (#3884). A probe on an
|
|
183
197
|
// earlier pass left CONFIG_VERDICT, and a frame read here would serve the
|
|
184
198
|
// committed screen, settle the flight and drop the hold — a superseding
|
|
@@ -192,15 +206,19 @@ function recompute(e, t = false) {
|
|
|
192
206
|
// world (`read`), and has left it otherwise.
|
|
193
207
|
// An answered deferred node's re-pass is a lagging question (D-40):
|
|
194
208
|
// it joins nothing, so the write that reached it commits mainline.
|
|
195
|
-
|
|
209
|
+
// `EFFECT_RENDER << 23` is CONFIG_FRAME (a render effect, a boundary's
|
|
210
|
+
// tree and output), spelled from the effect type as at creation.
|
|
211
|
+
if (t.P || e.l & CONFIG_LAGS) ; else if (!(e.l & (CONFIG_VERDICT | EFFECT_RENDER << 23))) joinPassTx(t, freshHook?.(e, e.m & STATUS_UNINITIALIZED)); else if (t !== flushTransaction && !(e.m & STATUS_UNINITIALIZED || e.l & CONFIG_HELD_FRAME)) {
|
|
196
212
|
// Published mainline, it is not held: the frame this pass builds is
|
|
197
213
|
// replaced on the spot (#3404) — this flush's commit retires the
|
|
198
214
|
// committed frame the held pass had parked (`commitPendingNode`),
|
|
199
215
|
// after the new frame's first runs, and the held pass's own
|
|
200
216
|
// children die below (`disposeChildren`). The landing re-derives a
|
|
201
217
|
// fresh held frame for the reveal (`_reruns`).
|
|
202
|
-
|
|
203
|
-
|
|
218
|
+
// Not held any more, whichever frame reader it is: its readers read
|
|
219
|
+
// the value this pass publishes.
|
|
220
|
+
e.ie = NOT_PENDING;
|
|
221
|
+
e.l &= ~CONFIG_HELD;
|
|
204
222
|
}
|
|
205
223
|
}
|
|
206
224
|
// The previous pass's frame. If that pass is still awaiting its commit
|
|
@@ -217,11 +235,11 @@ function recompute(e, t = false) {
|
|
|
217
235
|
deferReleases(o = []);
|
|
218
236
|
disposeChildren(e);
|
|
219
237
|
deferReleases(t);
|
|
220
|
-
} else if (e.
|
|
238
|
+
} else if (e.pe !== null || e.He !== null) parkChildren(e);
|
|
221
239
|
// An error a pass staged (D-41) is replaced by this pass's answer: a
|
|
222
240
|
// value, a flight, or an error of its own (#4109, #4145).
|
|
223
241
|
if (e.l & CONFIG_STAGED_ERROR) {
|
|
224
|
-
e.
|
|
242
|
+
e.ie = NOT_PENDING;
|
|
225
243
|
e.l ^= CONFIG_STAGED_ERROR;
|
|
226
244
|
}
|
|
227
245
|
}
|
|
@@ -229,16 +247,16 @@ function recompute(e, t = false) {
|
|
|
229
247
|
// Capture both error and pending status before the compute clears them.
|
|
230
248
|
// A conditional can drop its pending source and recover to an unchanged
|
|
231
249
|
// value, leaving blocked dependents outside that source’s settle walk.
|
|
232
|
-
const r = e.m & STATUS_ERROR ? e.T?.
|
|
250
|
+
const r = e.m & STATUS_ERROR ? e.T?.U : undefined;
|
|
233
251
|
const u = (e.m & STATUS_PENDING) !== 0;
|
|
234
|
-
const
|
|
252
|
+
const a = u ? e.T?.fe : undefined;
|
|
235
253
|
// Pending SOURCE-hood, captured before the compute clears status: a node
|
|
236
254
|
// whose own flight parked dependents self-registers in _pendingSources
|
|
237
255
|
// (notifyStatus, isSource). If this recompute supersedes that flight and
|
|
238
256
|
// settles synchronously, those dependents settle HERE — asyncWrite's
|
|
239
257
|
// settlePendingSource walk never runs for a landing that was preempted
|
|
240
258
|
// (#3181).
|
|
241
|
-
const
|
|
259
|
+
const _ = e.T?.fe?.has(e);
|
|
242
260
|
// D-41: this pass's error is staged, not published (`stageError`).
|
|
243
261
|
let I = false;
|
|
244
262
|
// A19 exc. 2: a `refresh()` re-asks the question already answered — quiet
|
|
@@ -254,11 +272,11 @@ function recompute(e, t = false) {
|
|
|
254
272
|
// live writes until the commit that disposes it, so every per-pass wipe
|
|
255
273
|
// — here, the finally below, updateIfNecessary — carries it (#3543).
|
|
256
274
|
e.R = REACTIVE_RECOMPUTING_DEPS | e.R & (REACTIVE_ZOMBIE | REACTIVE_REASK);
|
|
257
|
-
e.
|
|
275
|
+
e.Se = clock;
|
|
258
276
|
// The pass's previous value: its staging, else a lane node's lane value
|
|
259
277
|
// when it has one (lanes.ts), else the committed one.
|
|
260
|
-
let T = e.
|
|
261
|
-
let S = e.
|
|
278
|
+
let T = e.ie !== NOT_PENDING ? e.ie : e.l & CONFIG_OVERRIDE && e.T.P !== NOT_PENDING ? e.T.P : e.le;
|
|
279
|
+
let S = e.he;
|
|
262
280
|
let c = false;
|
|
263
281
|
// L2: did this pass read the future (REACTIVE_JOINED, set by read) — or,
|
|
264
282
|
// a lane's mount, a staging of this flush (#3851, #4166)?
|
|
@@ -268,19 +286,19 @@ function recompute(e, t = false) {
|
|
|
268
286
|
try {
|
|
269
287
|
if (!false && e.l & CONFIG_SYNC) {
|
|
270
288
|
T = e.X(T);
|
|
271
|
-
if (e.T !== null) e.T.
|
|
289
|
+
if (e.T !== null) e.T.Ee = null;
|
|
272
290
|
e.ce = false;
|
|
273
291
|
} else {
|
|
274
292
|
// A projection's body self-registers its flight through handleAsync
|
|
275
293
|
// (with the commit as setter): its undefined return is not a sync
|
|
276
294
|
// answer, and the outer handleAsync must not clobber the registration.
|
|
277
|
-
const t = e.T?.
|
|
295
|
+
const t = e.T?.Ee;
|
|
278
296
|
const n = e.X(T);
|
|
279
297
|
const o = typeof n === "object" && n !== null;
|
|
280
|
-
const E = e.T?.
|
|
298
|
+
const E = e.T?.Ee !== t;
|
|
281
299
|
T = E || !o ? n : handleAsync(e, n);
|
|
282
300
|
if (!E && !o) {
|
|
283
|
-
if (e.T !== null) e.T.
|
|
301
|
+
if (e.T !== null) e.T.Ee = null;
|
|
284
302
|
// A sync (non-object) return is the first real answer; async-shaped
|
|
285
303
|
// results clear inside handleAsync at their own landing points, and a
|
|
286
304
|
// self-registered flight clears when its own handleAsync lands.
|
|
@@ -307,11 +325,11 @@ function recompute(e, t = false) {
|
|
|
307
325
|
// D-41: a pass of a held node, or one that read a staging of this
|
|
308
326
|
// flush, stages its error (`stageError`).
|
|
309
327
|
if (n || !(I = stageError(e, t, true))) {
|
|
310
|
-
if (n) ext(e).
|
|
328
|
+
if (n) ext(e).se = true;
|
|
311
329
|
notifyStatus(e, n ? STATUS_PENDING : STATUS_ERROR, t);
|
|
312
330
|
// The replacement source is fully propagated now. If no new flight
|
|
313
331
|
// re-owned self, retire the superseded flight and its dependent copies.
|
|
314
|
-
if (n &&
|
|
332
|
+
if (n && _ && !e.T?.Ee) settlePendingSource(e);
|
|
315
333
|
}
|
|
316
334
|
// A re-park drops what the earlier pass carried (#3456): a source this
|
|
317
335
|
// pass no longer reaches — its branch switched, or a fresh flight
|
|
@@ -322,7 +340,7 @@ function recompute(e, t = false) {
|
|
|
322
340
|
// re-park twin of the unchanged-value recovery sweep below; dependents
|
|
323
341
|
// with another path keep the source (retryReaches). A staged error
|
|
324
342
|
// answered what the pass carried: the same.
|
|
325
|
-
if ((n || I) &&
|
|
343
|
+
if ((n || I) && a) for (const t of a) if (t !== e && !e.T?.fe?.has(t)) settlePendingSource(e, t);
|
|
326
344
|
}
|
|
327
345
|
} finally {
|
|
328
346
|
tracking = R;
|
|
@@ -369,7 +387,7 @@ function recompute(e, t = false) {
|
|
|
369
387
|
// attribution frame opened at the top is still closed.
|
|
370
388
|
if (e.R & REACTIVE_DISPOSED) {
|
|
371
389
|
clearDeps(e);
|
|
372
|
-
if (e.T !== null) e.T.
|
|
390
|
+
if (e.T !== null) e.T.Ee = null;
|
|
373
391
|
setPassLane(E);
|
|
374
392
|
return;
|
|
375
393
|
}
|
|
@@ -381,7 +399,7 @@ function recompute(e, t = false) {
|
|
|
381
399
|
let C = !laneStageHook ? null : passLane;
|
|
382
400
|
// Listed before its staging, a pending pass included (the lane's own
|
|
383
401
|
// flight is the lane's); false: the pass left the lane (lanes.ts).
|
|
384
|
-
const d = !!e.T?.
|
|
402
|
+
const d = !!e.T?.U || I;
|
|
385
403
|
if (laneStageHook && C !== null) {
|
|
386
404
|
if (laneStageHook(e, C, t, d)) setPassLane(C); else C = null;
|
|
387
405
|
}
|
|
@@ -390,9 +408,9 @@ function recompute(e, t = false) {
|
|
|
390
408
|
// publishes to — the staged `_pendingValue` when staged; a lane node's
|
|
391
409
|
// lane value (a node entering the lane this pass has none yet — its
|
|
392
410
|
// committed value, `laneStage` having voided a frame staging).
|
|
393
|
-
const o = e.
|
|
411
|
+
const o = e.ie !== NOT_PENDING ? e.ie : e.l & CONFIG_OVERRIDE && e.T.P !== NOT_PENDING ? e.T.P : e.le;
|
|
394
412
|
// A throwing comparator is contained inside `isSame` (A3, #2837; #4111).
|
|
395
|
-
const E = !n && l || !!e.T?.
|
|
413
|
+
const E = !n && l || !!e.T?.Ie || !isSame(e, o, T);
|
|
396
414
|
// Effects use `_equals: false` (no per-effect closure). The side effects that
|
|
397
415
|
// the equals closure used to perform — flagging the effect dirty and enqueueing
|
|
398
416
|
// its runner — happen here instead. `!create` matches the previous `initialized`
|
|
@@ -401,10 +419,10 @@ function recompute(e, t = false) {
|
|
|
401
419
|
if (n && E &&
|
|
402
420
|
// A stale reader of a lane (REACTIVE_SCREEN_READ) that computed what it
|
|
403
421
|
// last applied owes no run (lanes.ts).
|
|
404
|
-
!(e.R & REACTIVE_SCREEN_READ && !e.Ae && Object.is(e.
|
|
422
|
+
!(e.R & REACTIVE_SCREEN_READ && !e.Ae && Object.is(e.ke, T)) &&
|
|
405
423
|
// A re-staging pass owes no run: the commit replays the effect (#3802).
|
|
406
|
-
e.
|
|
407
|
-
e.Ae = !e.T?.
|
|
424
|
+
e.ie === NOT_PENDING) {
|
|
425
|
+
e.Ae = !e.T?.U;
|
|
408
426
|
// Reuse one bound runner per effect — runEffect no-ops on a stale
|
|
409
427
|
// `_modified`, so re-enqueueing the same function is harmless.
|
|
410
428
|
// Lane work's run is the lane's (released at its reveal) — a first
|
|
@@ -418,7 +436,8 @@ function recompute(e, t = false) {
|
|
|
418
436
|
// confirm is a landing too — the lane's end commits it) and notifies
|
|
419
437
|
// only a correction: an equal truth re-runs nothing (lanes.ts). (As the
|
|
420
438
|
// lane's own work the lane judged it — `laneStage`.)
|
|
421
|
-
|
|
439
|
+
// (The hook first: a build without lanes folds the arm away.)
|
|
440
|
+
if (laneOutcomeHook && !t && C === null && e.l & CONFIG_GUESS && laneOutcomeHook(e, T, d)) ; else if (d) ; else if (E) {
|
|
422
441
|
// L2: a first pass is born held — staged, committed with the
|
|
423
442
|
// transaction, its effect's first run the landing's (A29; `effect()`
|
|
424
443
|
// skips the synchronous first run on a staged value) — when the flush
|
|
@@ -442,8 +461,8 @@ function recompute(e, t = false) {
|
|
|
442
461
|
// effect's value slot is private: the run the lane holds is what
|
|
443
462
|
// shows it — unless the effect was born held, whose first run is the
|
|
444
463
|
// commit's (A29): it re-stages, as below.
|
|
445
|
-
if (n && e.
|
|
446
|
-
} else if (t ? !((flushTransaction !== null || passTx !== null) && (A || creatorPass(f)?.R & REACTIVE_JOINED)) : n && e.
|
|
464
|
+
if (n && e.ie === NOT_PENDING) e.le = T; else if (n || C.ge) e.ie = T; else e.T.P = T;
|
|
465
|
+
} else if (t ? !((flushTransaction !== null || passTx !== null) && (A || creatorPass(f)?.R & REACTIVE_JOINED)) : n && e.ie === NOT_PENDING) {
|
|
447
466
|
// A first pass publishes directly. So does an effect: its value slot
|
|
448
467
|
// is private (only its own run reads it), and the staging round-trip
|
|
449
468
|
// (queuePendingNode + commitPendingNodes) paid per effect on the
|
|
@@ -454,9 +473,9 @@ function recompute(e, t = false) {
|
|
|
454
473
|
// sources commit) must see the fresh value while PLAIN reads stay
|
|
455
474
|
// committed until flush (#3009 purity). The pending round-trip is
|
|
456
475
|
// that separation; it cannot be skipped on any path a pull can reach.
|
|
457
|
-
e.
|
|
476
|
+
e.le = T;
|
|
458
477
|
} else {
|
|
459
|
-
e.
|
|
478
|
+
e.ie = T;
|
|
460
479
|
// Born held: in the transaction from birth, so a read of it this
|
|
461
480
|
// tick is a read of its world (the mount-during-a-hold case), not
|
|
462
481
|
// only after the seam parks it.
|
|
@@ -484,7 +503,7 @@ function recompute(e, t = false) {
|
|
|
484
503
|
// Lane work re-staged: the lane's members re-derive as its work.
|
|
485
504
|
if (C !== null) laneDirty(e, C);
|
|
486
505
|
}
|
|
487
|
-
} else if (e.
|
|
506
|
+
} else if (e.he != S) {
|
|
488
507
|
for (let t = e.o; t !== null; t = t.t) insertIntoHeapHeight(t.u, dirtyQueue);
|
|
489
508
|
}
|
|
490
509
|
// Silent recovery: errored → unchanged value fires no notification, but
|
|
@@ -492,16 +511,16 @@ function recompute(e, t = false) {
|
|
|
492
511
|
// in an errored run and may sit on stale commits (#2949). Changed-value
|
|
493
512
|
// recoveries ride insertSubs above; a comparator throw re-errored the node
|
|
494
513
|
// (el._x?._error re-set), so this only runs on a genuinely clean recovery.
|
|
495
|
-
if (!E && !e.T?.
|
|
514
|
+
if (!E && !e.T?.U) {
|
|
496
515
|
if (r !== undefined) settleErroredDependents(e, r);
|
|
497
516
|
// Self-registration (this node's own superseded flight) is the #3181
|
|
498
517
|
// sweep's business below — retiring it here too would walk twice.
|
|
499
|
-
if (
|
|
518
|
+
if (a) for (const t of a) if (t !== e) settlePendingSource(e, t);
|
|
500
519
|
}
|
|
501
520
|
// #3181: a synchronous settle supersedes the old landing callback, so
|
|
502
521
|
// recompute owns its pending-source sweep. An uninitialized node without
|
|
503
522
|
// a replacement source still has no truth to reveal and must stay parked.
|
|
504
|
-
if (
|
|
523
|
+
if (_ && !(e.m & (STATUS_PENDING | STATUS_UNINITIALIZED))) settlePendingSource(e);
|
|
505
524
|
}
|
|
506
525
|
// A first pass that went pending where the born-held arm would have held
|
|
507
526
|
// its value (it read a held node, or a verdict lane's mount read a staging,
|
|
@@ -513,7 +532,7 @@ function recompute(e, t = false) {
|
|
|
513
532
|
// re-armed by `on` owns what it collects outright (D-14): not held.
|
|
514
533
|
if (t && C === null && A && e.m & STATUS_PENDING) {
|
|
515
534
|
const t = flushTransaction ?? passTx;
|
|
516
|
-
if (t && !t.
|
|
535
|
+
if (t && !t.P && freshHook?.(e) !== CAUGHT_REARMED) holdNode(e, t);
|
|
517
536
|
}
|
|
518
537
|
// Dependencies are the committed frame's until it is replaced (A30, #3410):
|
|
519
538
|
// a pass that staged its value leaves the previous pass's tail linked for
|
|
@@ -532,7 +551,7 @@ function recompute(e, t = false) {
|
|
|
532
551
|
// kept at a park — one spurious recompute at most). A creation pass and a
|
|
533
552
|
// tracked effect trim now: their frames are replaceable like a direct
|
|
534
553
|
// commit, and a tracked effect's spurious run would be user-visible.
|
|
535
|
-
if (!e.T?.
|
|
554
|
+
if (!e.T?.U && e.ie === NOT_PENDING && !(n && e.Ae)) {
|
|
536
555
|
if (t || n === EFFECT_TRACKED) trimStaleDeps(e); else heldTrim(e);
|
|
537
556
|
}
|
|
538
557
|
// A staged value, a parked frame (L2: the commit retires it), or status the
|
|
@@ -540,7 +559,7 @@ function recompute(e, t = false) {
|
|
|
540
559
|
// node for this flush's commit. A first pass queues only when pending or
|
|
541
560
|
// born held: otherwise its value published directly. A queued pass's
|
|
542
561
|
// children are uncommitted (CONFIG_STAGED) until that commit.
|
|
543
|
-
if (e.
|
|
562
|
+
if (e.ie !== NOT_PENDING || e.T !== null && (e.T.Ue !== null || e.T.me !== null) || (e.m & (STATUS_PENDING | STATUS_UNINITIALIZED)) !== 0 && (!t || (e.m & STATUS_PENDING) !== 0)) {
|
|
544
563
|
e.l |= CONFIG_STAGED;
|
|
545
564
|
// Lane work is the lane's to reveal (its seam), not this flush's commit.
|
|
546
565
|
if (C === null) queuePendingNode(e);
|
|
@@ -576,7 +595,7 @@ function updateIfNecessary(e) {
|
|
|
576
595
|
}
|
|
577
596
|
}
|
|
578
597
|
}
|
|
579
|
-
if (e.R & REACTIVE_DIRTY || e.T?.
|
|
598
|
+
if (e.R & REACTIVE_DIRTY || e.T?.U && e.Se < clock && !e.T?.Ee) {
|
|
580
599
|
recompute(e);
|
|
581
600
|
}
|
|
582
601
|
// The guard above refused an already-disposed node; the recompute it just
|
|
@@ -606,14 +625,14 @@ function computed(e, t) {
|
|
|
606
625
|
// plus the slots.
|
|
607
626
|
const E = {
|
|
608
627
|
id: inheritId(t, n, context),
|
|
609
|
-
l: (n ? CONFIG_TRANSPARENT : 0) | (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (!context || t?.lazy ? CONFIG_AUTO_DISPOSE : 0) | (t?.sync ? CONFIG_SYNC : 0) | (t?.
|
|
628
|
+
l: (n ? CONFIG_TRANSPARENT : 0) | (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (!context || t?.lazy ? CONFIG_AUTO_DISPOSE : 0) | (t?.sync ? CONFIG_SYNC : 0) | (t?.ue ? CONFIG_NO_SNAPSHOT : 0) | (t?.Le ?? 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
|
|
610
629
|
xe: t?.equals ?? isEqual,
|
|
611
630
|
He: null,
|
|
612
631
|
A: context?.A ?? defaultContext,
|
|
613
632
|
ye: 0,
|
|
614
633
|
X: e,
|
|
615
|
-
|
|
616
|
-
|
|
634
|
+
le: o ? t.loadingValue : undefined,
|
|
635
|
+
he: 0,
|
|
617
636
|
be: undefined,
|
|
618
637
|
We: null,
|
|
619
638
|
C: null,
|
|
@@ -622,14 +641,14 @@ function computed(e, t) {
|
|
|
622
641
|
o: null,
|
|
623
642
|
ve: null,
|
|
624
643
|
_parent: context,
|
|
625
|
-
|
|
644
|
+
Ge: null,
|
|
626
645
|
Ze: null,
|
|
627
|
-
|
|
646
|
+
pe: null,
|
|
628
647
|
R: t?.lazy ? REACTIVE_LAZY : REACTIVE_NONE,
|
|
629
648
|
// A loadingValue node is born committed: commit #0 is already in _value.
|
|
630
649
|
m: o ? 0 : STATUS_UNINITIALIZED,
|
|
631
|
-
|
|
632
|
-
|
|
650
|
+
Se: clock,
|
|
651
|
+
ie: NOT_PENDING,
|
|
633
652
|
Me: -1,
|
|
634
653
|
ce: o,
|
|
635
654
|
// Cold machinery (async slots) lives one hop away in the lazily-allocated extension — the core literal MUST
|
|
@@ -647,23 +666,23 @@ function computed(e, t) {
|
|
|
647
666
|
* this; hot paths read `el._x?._field` gated by the _config presence bits.
|
|
648
667
|
* Never call ext() just to store a field's default. */ function ext(e) {
|
|
649
668
|
return e.T ??= {
|
|
650
|
-
|
|
669
|
+
Ee: null,
|
|
651
670
|
ae: null,
|
|
652
|
-
|
|
653
|
-
|
|
671
|
+
U: undefined,
|
|
672
|
+
se: undefined,
|
|
654
673
|
fe: undefined,
|
|
655
674
|
we: undefined,
|
|
656
|
-
|
|
675
|
+
De: undefined,
|
|
657
676
|
Ue: null,
|
|
658
677
|
me: null,
|
|
659
678
|
Re: null,
|
|
660
679
|
Ve: false,
|
|
661
680
|
je: NOT_PENDING,
|
|
662
681
|
Be: -1,
|
|
663
|
-
|
|
664
|
-
|
|
682
|
+
Te: 0,
|
|
683
|
+
P: NOT_PENDING,
|
|
665
684
|
I: 0,
|
|
666
|
-
|
|
685
|
+
Ie: undefined
|
|
667
686
|
};
|
|
668
687
|
}
|
|
669
688
|
|
|
@@ -727,10 +746,16 @@ function computed(e, t) {
|
|
|
727
746
|
// read the screen. Lane work with no committed value yet reads a flight
|
|
728
747
|
// as a mount's memo does: it enters, and the boundary it mounts catches
|
|
729
748
|
// the pending (#3540).
|
|
749
|
+
// A boundary's tree and output (CONFIG_FRAME) read as a render effect
|
|
750
|
+
// does: structure, not a derivation of what it shows (#4283). A mount
|
|
751
|
+
// under a hold still enters it: the content memo it reads is born held,
|
|
752
|
+
// with no committed value to serve.
|
|
730
753
|
// (Verdict readers and lanes exist only with their engines installed.)
|
|
731
754
|
const n = observeFlightHook && e.l & CONFIG_VERDICT;
|
|
732
755
|
const o = laneStageHook && passLane !== null;
|
|
733
|
-
if ((!o || e.m & STATUS_UNINITIALIZED && t.m & STATUS_PENDING) && !n && (
|
|
756
|
+
if ((!o || e.m & STATUS_UNINITIALIZED && t.m & STATUS_PENDING) && !n && (
|
|
757
|
+
// CONFIG_FRAME, spelled as at the effect's creation.
|
|
758
|
+
!(e.l & EFFECT_RENDER << 23) || t.l & CONFIG_INPUTS_PUBLISHED)) return false;
|
|
734
759
|
const E = txOf(t);
|
|
735
760
|
// Lane work and verdict readers are never the transaction's pass, whichever
|
|
736
761
|
// flush runs it: a node T holds re-enters T when it re-runs (`recompute`'s
|
|
@@ -760,9 +785,9 @@ function computed(e, t) {
|
|
|
760
785
|
markDisposal(e);
|
|
761
786
|
const t = ext(e);
|
|
762
787
|
t.me = e.He;
|
|
763
|
-
t.Ue = e.
|
|
788
|
+
t.Ue = e.pe;
|
|
764
789
|
e.He = null;
|
|
765
|
-
e.
|
|
790
|
+
e.pe = null;
|
|
766
791
|
e.ye = 0;
|
|
767
792
|
}
|
|
768
793
|
|
|
@@ -780,14 +805,17 @@ function computed(e, t) {
|
|
|
780
805
|
// the options to get it.
|
|
781
806
|
const i = {
|
|
782
807
|
id: inheritId(E, s, context),
|
|
783
|
-
l:
|
|
808
|
+
l:
|
|
809
|
+
// CONFIG_FRAME for a render effect (EFFECT_RENDER = 1; a user
|
|
810
|
+
// effect's 1 << 24 is unused).
|
|
811
|
+
o << 23 | (s ? CONFIG_TRANSPARENT : 0) | (E?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (E?.sync ? CONFIG_SYNC : 0) | (E?.Le ?? 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
|
|
784
812
|
xe: false,
|
|
785
813
|
He: null,
|
|
786
814
|
A: context?.A ?? defaultContext,
|
|
787
815
|
ye: 0,
|
|
788
816
|
X: e,
|
|
789
|
-
|
|
790
|
-
|
|
817
|
+
le: undefined,
|
|
818
|
+
he: 0,
|
|
791
819
|
be: undefined,
|
|
792
820
|
We: null,
|
|
793
821
|
C: null,
|
|
@@ -796,17 +824,17 @@ function computed(e, t) {
|
|
|
796
824
|
o: null,
|
|
797
825
|
ve: null,
|
|
798
826
|
_parent: context,
|
|
799
|
-
|
|
827
|
+
Ge: null,
|
|
800
828
|
Ze: null,
|
|
801
|
-
|
|
829
|
+
pe: null,
|
|
802
830
|
R: REACTIVE_LAZY,
|
|
803
831
|
m: STATUS_UNINITIALIZED,
|
|
804
|
-
|
|
805
|
-
|
|
832
|
+
Se: clock,
|
|
833
|
+
ie: NOT_PENDING,
|
|
806
834
|
Me: -1,
|
|
807
835
|
ce: false,
|
|
808
836
|
Ae: false,
|
|
809
|
-
|
|
837
|
+
ke: undefined,
|
|
810
838
|
Ke: t,
|
|
811
839
|
qe: n,
|
|
812
840
|
Je: undefined,
|
|
@@ -839,7 +867,7 @@ function setEffectStatusNotify(e) {
|
|
|
839
867
|
* 0, and only effect literals carry the field) get the shared notifier.
|
|
840
868
|
* Presence doubles as the "display consumer" membership test in the status
|
|
841
869
|
* walks, exactly as the per-node field did when every effect carried one. */ function statusNotifierOf(e) {
|
|
842
|
-
return e.i ? effectStatusNotify
|
|
870
|
+
return e.i ? effectStatusNotify : null;
|
|
843
871
|
}
|
|
844
872
|
|
|
845
873
|
const lazyOptions = {
|
|
@@ -850,15 +878,11 @@ function setupComputedNode(e, t) {
|
|
|
850
878
|
e.We = e;
|
|
851
879
|
const n = context?.Qe ? context.Ye : context;
|
|
852
880
|
if (context) linkChild(context, e);
|
|
853
|
-
if (n) e.
|
|
881
|
+
if (n) e.he = n.he + 1;
|
|
854
882
|
wireExternalSourceHook?.(e);
|
|
855
883
|
!t?.lazy && recompute(e, true);
|
|
856
884
|
if (snapshotCaptureActive && !t?.lazy) {
|
|
857
|
-
if (!(e.m & STATUS_PENDING) && !(e.l & CONFIG_NO_SNAPSHOT))
|
|
858
|
-
ext(e).Pe = e.Ie === undefined ? NO_SNAPSHOT : e.Ie;
|
|
859
|
-
e.l |= CONFIG_HAS_SNAPSHOT;
|
|
860
|
-
snapshotSources.add(e);
|
|
861
|
-
}
|
|
885
|
+
if (!(e.m & STATUS_PENDING) && !(e.l & CONFIG_NO_SNAPSHOT)) takeSnapshot(e, e.le);
|
|
862
886
|
}
|
|
863
887
|
}
|
|
864
888
|
|
|
@@ -869,12 +893,12 @@ function signal(e, t) {
|
|
|
869
893
|
// and staying null on internal signals — one shape either way).
|
|
870
894
|
const n = {
|
|
871
895
|
xe: t?.equals ?? isEqual,
|
|
872
|
-
l: (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (t?.
|
|
873
|
-
|
|
896
|
+
l: (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (t?.ue ? CONFIG_NO_SNAPSHOT : 0),
|
|
897
|
+
le: e,
|
|
874
898
|
o: null,
|
|
875
899
|
ve: null,
|
|
876
|
-
|
|
877
|
-
|
|
900
|
+
Se: clock,
|
|
901
|
+
ie: NOT_PENDING,
|
|
878
902
|
// Signal-literal diet (§12e): NO _time/_fn/_statusFlags slots. Stores
|
|
879
903
|
// materialize one signal per touched leaf, so signal bytes are store
|
|
880
904
|
// bytes. _time is write-only on signals (every read site is computed-
|
|
@@ -884,11 +908,7 @@ function signal(e, t) {
|
|
|
884
908
|
T: null
|
|
885
909
|
};
|
|
886
910
|
if (t?.unobserved) ext(n).we = t.unobserved;
|
|
887
|
-
if (snapshotCaptureActive && !(n.l & CONFIG_NO_SNAPSHOT))
|
|
888
|
-
ext(n).Pe = e === undefined ? NO_SNAPSHOT : e;
|
|
889
|
-
n.l |= CONFIG_HAS_SNAPSHOT;
|
|
890
|
-
snapshotSources.add(n);
|
|
891
|
-
}
|
|
911
|
+
if (snapshotCaptureActive && !(n.l & CONFIG_NO_SNAPSHOT)) takeSnapshot(n, e);
|
|
892
912
|
return n;
|
|
893
913
|
}
|
|
894
914
|
|
|
@@ -910,11 +930,11 @@ function isEqual(e, t) {
|
|
|
910
930
|
const s = {
|
|
911
931
|
xe: t,
|
|
912
932
|
l: CONFIG_OWNED_WRITE | CONFIG_SLOT_NODE,
|
|
913
|
-
|
|
933
|
+
le: e,
|
|
914
934
|
o: null,
|
|
915
935
|
ve: null,
|
|
916
|
-
|
|
917
|
-
|
|
936
|
+
Se: clock,
|
|
937
|
+
ie: NOT_PENDING,
|
|
918
938
|
Me: -1,
|
|
919
939
|
T: null,
|
|
920
940
|
O: n,
|
|
@@ -1044,7 +1064,7 @@ function setVerdict(e) {
|
|
|
1044
1064
|
* never the graph — A32).
|
|
1045
1065
|
* False means the reader derives from the staged value.
|
|
1046
1066
|
*/ function readerSeesCommitted(e, t) {
|
|
1047
|
-
return !t || e.
|
|
1067
|
+
return !t || e.ie === NOT_PENDING || t.l & CONFIG_CHILDREN_FORBIDDEN;
|
|
1048
1068
|
}
|
|
1049
1069
|
|
|
1050
1070
|
/** A28 — set when a node is staged (queuePendingNode) OUTSIDE a flush;
|
|
@@ -1065,12 +1085,12 @@ function markUnflushedStaged() {
|
|
|
1065
1085
|
* is unflushed. Exempt: promoted writes (A28 (4): a write issued inside a
|
|
1066
1086
|
* recompute is promoted at that recompute's end — boundary and loading
|
|
1067
1087
|
* machinery, signals declared for in-computation writes). */ function unflushedValue(e) {
|
|
1068
|
-
if (globalQueue.q || e.
|
|
1088
|
+
if (globalQueue.q || e.ie === NOT_PENDING || e.l & CONFIG_PROMOTED) return NOT_PENDING;
|
|
1069
1089
|
// A held node (L2) carries a staged value between flushes too, but it was
|
|
1070
1090
|
// flushed — and parked. Not an unflushed write — unless rewritten since:
|
|
1071
1091
|
// then the flushed staging is what the world sees until the next flush.
|
|
1072
1092
|
if (e.l & CONFIG_HELD) return e.T.Be === clock ? e.T.je : NOT_PENDING;
|
|
1073
|
-
return e.
|
|
1093
|
+
return e.le;
|
|
1074
1094
|
}
|
|
1075
1095
|
|
|
1076
1096
|
/** A28: a held node rewritten outside a flush keeps the staging the last
|
|
@@ -1078,7 +1098,7 @@ function markUnflushedStaged() {
|
|
|
1078
1098
|
* stash is stamped with the clock, which that flush advances. */ function stashFlushed(e) {
|
|
1079
1099
|
const t = e.T;
|
|
1080
1100
|
if (t.Be !== clock) {
|
|
1081
|
-
t.je = e.
|
|
1101
|
+
t.je = e.ie;
|
|
1082
1102
|
t.Be = clock;
|
|
1083
1103
|
unflushedStaged = true;
|
|
1084
1104
|
}
|
|
@@ -1107,7 +1127,7 @@ function resyncUnflushedCompanions() {
|
|
|
1107
1127
|
/** A tracked read's pull of a computed: bring it current when the flush has
|
|
1108
1128
|
* reached its height (or the reader is a fresh-pull waiter), and sit the
|
|
1109
1129
|
* reader above it. */ function pullComputed(e, t) {
|
|
1110
|
-
if (e.
|
|
1130
|
+
if (e.he >= dirtyQueue.Fe) {
|
|
1111
1131
|
markNode(t);
|
|
1112
1132
|
markHeap(dirtyQueue);
|
|
1113
1133
|
updateIfNecessary(e);
|
|
@@ -1119,9 +1139,9 @@ function resyncUnflushedCompanions() {
|
|
|
1119
1139
|
// node no-ops and updateIfNecessary refuses disposed nodes (#2983) —
|
|
1120
1140
|
// a dead target serves its last value, which is already quiescent.
|
|
1121
1141
|
else if (t.l & CONFIG_FRESH_READ) updateIfNecessary(e);
|
|
1122
|
-
const n = e.
|
|
1142
|
+
const n = e.he;
|
|
1123
1143
|
// parent check is shallow, might need to be recursive
|
|
1124
|
-
if (n >= t.
|
|
1144
|
+
if (n >= t.he && e._parent !== t) t.he = n + 1;
|
|
1125
1145
|
}
|
|
1126
1146
|
|
|
1127
1147
|
function read(e) {
|
|
@@ -1137,7 +1157,7 @@ function read(e) {
|
|
|
1137
1157
|
// The verdict windows (`latest`, `isPending`; verdict.ts): one cold path,
|
|
1138
1158
|
// off this one, present only while a window is open.
|
|
1139
1159
|
if (verdict !== null) return verdict(e, t);
|
|
1140
|
-
if (!n.X && e.T?.
|
|
1160
|
+
if (!n.X && e.T?.De === undefined && !snapshotCaptureActive && (!unflushedStaged || e.ie === NOT_PENDING) && // A28, see readNodeFast
|
|
1141
1161
|
!(e.l & CONFIG_OVERRIDE) && // lanes: the slow path's one arm
|
|
1142
1162
|
true) {
|
|
1143
1163
|
if (t) {
|
|
@@ -1153,18 +1173,18 @@ function read(e) {
|
|
|
1153
1173
|
// future (REACTIVE_JOINED): a first pass that did is born held (A29),
|
|
1154
1174
|
// wherever it was created. Lane work sees the screen (`frameRead`).
|
|
1155
1175
|
if (e.l & CONFIG_HELD && !(t.l & CONFIG_CHILDREN_FORBIDDEN)) {
|
|
1156
|
-
if (frameRead(t, e)) return e.
|
|
1176
|
+
if (frameRead(t, e)) return e.le;
|
|
1157
1177
|
joinPass(t, e);
|
|
1158
1178
|
}
|
|
1159
1179
|
}
|
|
1160
1180
|
// Committed visibility for untracked and children-forbidden readers.
|
|
1161
|
-
if (!t || e.
|
|
1181
|
+
if (!t || e.ie === NOT_PENDING || t.l & CONFIG_CHILDREN_FORBIDDEN) return e.le;
|
|
1162
1182
|
// (A plain signal is never uninitialized — the carve-out is `serve`'s.)
|
|
1163
1183
|
// Lane work over a folded lane (D-34), once the frame has a
|
|
1164
1184
|
// transaction, reads the screen too.
|
|
1165
|
-
if ((observeFlightHook && t.l & CONFIG_VERDICT || laneStageHook && passLane?.folded) && stagedScreen(t)) return e.
|
|
1185
|
+
if ((observeFlightHook && t.l & CONFIG_VERDICT || laneStageHook && passLane?.folded) && stagedScreen(t)) return e.le;
|
|
1166
1186
|
stagedRead(t);
|
|
1167
|
-
return e.
|
|
1187
|
+
return e.ie;
|
|
1168
1188
|
}
|
|
1169
1189
|
// Rule 3: this read serves the committed value to a render effect outside a
|
|
1170
1190
|
// parking flush (decided after the pull below — a pull can join).
|
|
@@ -1197,6 +1217,19 @@ function read(e) {
|
|
|
1197
1217
|
const n = laneReadHook(t, e);
|
|
1198
1218
|
if (n !== NOT_PENDING) return n;
|
|
1199
1219
|
}
|
|
1220
|
+
// A reader in a snapshot scope (a hydration claim) is served the value the
|
|
1221
|
+
// server rendered with: a client write's recompute still in flight does
|
|
1222
|
+
// not pull the claim off the markup it adopts — the reader replays at the
|
|
1223
|
+
// scope's release. An errored node throws below as for any reader.
|
|
1224
|
+
if (snapshotCaptureActive && t && t.l & CONFIG_IN_SNAPSHOT_SCOPE) {
|
|
1225
|
+
const n = e.T?.De;
|
|
1226
|
+
if (n !== undefined && !(o.m & STATUS_ERROR)) {
|
|
1227
|
+
const o = n === NO_SNAPSHOT ? undefined : n;
|
|
1228
|
+
const E = e.ie !== NOT_PENDING ? e.ie : e.le;
|
|
1229
|
+
if (E !== o) t.R |= REACTIVE_SNAPSHOT_STALE;
|
|
1230
|
+
return o;
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1200
1233
|
// A stale reader of a held flight (rule 3, A15's reveal corollary) is served
|
|
1201
1234
|
// the committed value — coherent with the frame, whose inputs are the
|
|
1202
1235
|
// committed ones too — and does not go pending on it. So is lane work
|
|
@@ -1229,10 +1262,10 @@ function read(e) {
|
|
|
1229
1262
|
// the node settles — unless nobody is reading (`spectating`, #3528)
|
|
1230
1263
|
// or the reader is the node itself.
|
|
1231
1264
|
if (!tracking && !spectating && e !== t) link(e, t);
|
|
1232
|
-
throw o.T?.
|
|
1265
|
+
throw o.T?.U;
|
|
1233
1266
|
}
|
|
1234
1267
|
} else if (o.m & STATUS_UNINITIALIZED) {
|
|
1235
|
-
throw o.T?.
|
|
1268
|
+
throw o.T?.U;
|
|
1236
1269
|
}
|
|
1237
1270
|
}
|
|
1238
1271
|
// An errored derive throws for every late reader instead of silently
|
|
@@ -1241,24 +1274,15 @@ function read(e) {
|
|
|
1241
1274
|
// Only a genuine reactive re-read may retry an errored async source:
|
|
1242
1275
|
// - tracking: owned/tracked scope only (never events / `untrack` / effect side-effect phase)
|
|
1243
1276
|
// - owner._time < clock: only on a later cycle than the one the error was found
|
|
1244
|
-
if (tracking && o.
|
|
1277
|
+
if (tracking && o.Se < clock) {
|
|
1245
1278
|
recompute(o);
|
|
1246
1279
|
return read(e);
|
|
1247
|
-
} else throw o.T?.
|
|
1248
|
-
}
|
|
1249
|
-
if (snapshotCaptureActive && t && t.l & CONFIG_IN_SNAPSHOT_SCOPE) {
|
|
1250
|
-
const n = e.T?.Pe;
|
|
1251
|
-
if (n !== undefined) {
|
|
1252
|
-
const o = n === NO_SNAPSHOT ? undefined : n;
|
|
1253
|
-
const E = e.se !== NOT_PENDING ? e.se : e.Ie;
|
|
1254
|
-
if (E !== o) t.R |= REACTIVE_SNAPSHOT_STALE;
|
|
1255
|
-
return o;
|
|
1256
|
-
}
|
|
1280
|
+
} else throw o.T?.U;
|
|
1257
1281
|
}
|
|
1258
|
-
if (E) return e.
|
|
1282
|
+
if (E) return e.le;
|
|
1259
1283
|
// D-41: an untracked read reads the screen, and a shown error is on it
|
|
1260
1284
|
// until the answer that replaces it commits.
|
|
1261
|
-
if (!t && e.T?.
|
|
1285
|
+
if (!t && e.T?.Ie) throw e.T.Ie;
|
|
1262
1286
|
const s = serve(e, t);
|
|
1263
1287
|
if (!t && typeof n.X === "function" && e.l & CONFIG_AUTO_DISPOSE && !(o.m & STATUS_PENDING) && !e.o) {
|
|
1264
1288
|
// Deferred, not inline (#3078): an inline unobserved() here made untracked
|
|
@@ -1287,18 +1311,18 @@ function read(e) {
|
|
|
1287
1311
|
// an untracked reader has nothing to serve and holds (A19 exception 1) — a
|
|
1288
1312
|
// bookkeeping read (`spectate`) likewise, and a children-forbidden reader
|
|
1289
1313
|
// (A32: the frame, which has nothing here).
|
|
1290
|
-
if (e.
|
|
1314
|
+
if (e.ie !== NOT_PENDING && e.m & STATUS_UNINITIALIZED && (!t || spectating || t.l & CONFIG_CHILDREN_FORBIDDEN)) throw new NotReadyError(null);
|
|
1291
1315
|
const n = t && unflushedStaged ? unflushedValue(e) : NOT_PENDING;
|
|
1292
1316
|
if (n !== NOT_PENDING) {
|
|
1293
1317
|
markLateLinker(t);
|
|
1294
1318
|
return n;
|
|
1295
1319
|
}
|
|
1296
|
-
if (readerSeesCommitted(e, t)) return e.
|
|
1297
|
-
if (observeFlightHook && t.l & CONFIG_VERDICT && !(e.m & STATUS_UNINITIALIZED) && stagedScreen(t)) return e.
|
|
1320
|
+
if (readerSeesCommitted(e, t)) return e.le;
|
|
1321
|
+
if (observeFlightHook && t.l & CONFIG_VERDICT && !(e.m & STATUS_UNINITIALIZED) && stagedScreen(t)) return e.le;
|
|
1298
1322
|
stagedRead(t, e);
|
|
1299
1323
|
// D-41: the staged answer is an error (`stageError`).
|
|
1300
|
-
if (e.l & CONFIG_STAGED_ERROR) throw e.
|
|
1301
|
-
return e.
|
|
1324
|
+
if (e.l & CONFIG_STAGED_ERROR) throw e.ie;
|
|
1325
|
+
return e.ie;
|
|
1302
1326
|
}
|
|
1303
1327
|
|
|
1304
1328
|
/** A pass read a staging of this flush: it derives from what the flush may
|
|
@@ -1352,8 +1376,12 @@ function read(e) {
|
|
|
1352
1376
|
* REVEALS — the creation-time arm skips pending computeds for the same
|
|
1353
1377
|
* reason. Firewall leaves belong to a projection whose compute is the tree's
|
|
1354
1378
|
* own work, captured (or deliberately not) at creation. */ function captureWriteSnapshot(e, t) {
|
|
1355
|
-
if (e.l & CONFIG_NO_SNAPSHOT || e.X !== undefined || e.T?.
|
|
1356
|
-
|
|
1379
|
+
if (e.l & CONFIG_NO_SNAPSHOT || e.X !== undefined || e.T?.De !== undefined) return;
|
|
1380
|
+
takeSnapshot(e, t);
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
function takeSnapshot(e, t) {
|
|
1384
|
+
ext(e).De = t === undefined ? NO_SNAPSHOT : t;
|
|
1357
1385
|
e.l |= CONFIG_HAS_SNAPSHOT;
|
|
1358
1386
|
snapshotSources.add(e);
|
|
1359
1387
|
}
|
|
@@ -1393,7 +1421,7 @@ function read(e) {
|
|
|
1393
1421
|
|
|
1394
1422
|
function setSignal(e, t) {
|
|
1395
1423
|
if (writeHook !== null) writeHook(e);
|
|
1396
|
-
const n = e.
|
|
1424
|
+
const n = e.ie === NOT_PENDING ? e.le : e.ie;
|
|
1397
1425
|
if (typeof t === "function") t = t(n);
|
|
1398
1426
|
// Lanes: a write landing on a guess is its truth (A18 — an async guess's
|
|
1399
1427
|
// own landing), judged against the guess; one landing on a lane's
|
|
@@ -1408,7 +1436,7 @@ function setSignal(e, t) {
|
|
|
1408
1436
|
// user comparator must not run against `undefined` (matches recompute).
|
|
1409
1437
|
// D-41: a write over a shown error is a change whatever the last good
|
|
1410
1438
|
// value was — the error leaves at its commit (`commitPendingNode`).
|
|
1411
|
-
const o = !!(e.m & STATUS_UNINITIALIZED) || !!e.T?.
|
|
1439
|
+
const o = !!(e.m & STATUS_UNINITIALIZED) || !!e.T?.Ie || !isSame(e, n, t);
|
|
1412
1440
|
if (!o) return t;
|
|
1413
1441
|
// A write during hydration's snapshot capture to a source that has no
|
|
1414
1442
|
// snapshot — created BEFORE capture began (module-level state: an identity
|
|
@@ -1420,16 +1448,16 @@ function setSignal(e, t) {
|
|
|
1420
1448
|
// pass reads a value the server never had. Store leaves written here are
|
|
1421
1449
|
// plain signals and qualify the same way.
|
|
1422
1450
|
if (snapshotCaptureActive) captureWriteSnapshot(e, n);
|
|
1423
|
-
const E = e.
|
|
1451
|
+
const E = e.ie !== NOT_PENDING;
|
|
1424
1452
|
if (!E) queuePendingNode(e);
|
|
1425
1453
|
// A28: a held node rewritten outside a flush keeps its flushed staging
|
|
1426
1454
|
// for every reader until the flush that carries the rewrite.
|
|
1427
1455
|
else if (e.l & CONFIG_HELD && !globalQueue.q) stashFlushed(e);
|
|
1428
|
-
e.
|
|
1456
|
+
e.ie = t;
|
|
1429
1457
|
if (context !== null) notePromotedWrite(e);
|
|
1430
1458
|
// _time is a computed-only slot (§12e): writing it on a signal would fork
|
|
1431
1459
|
// the lean shape. Every read site is computed-typed.
|
|
1432
|
-
if (e.X !== undefined) e.
|
|
1460
|
+
if (e.X !== undefined) e.Se = clock;
|
|
1433
1461
|
// Staged-rewrite fast path (§12d): a re-write to a node whose subscribers
|
|
1434
1462
|
// were already walked — and where nothing has recomputed or linked since
|
|
1435
1463
|
// (epoch) — re-stages the value and stops. The walk is idempotent (subs
|
|
@@ -1462,7 +1490,7 @@ function setSignal(e, t) {
|
|
|
1462
1490
|
const n = e.l & CONFIG_HELD && !(e.R & REACTIVE_MANUAL_WRITE) && txOf(e) !== flushTransaction;
|
|
1463
1491
|
// The writer read the committed frame; its updater composes on it.
|
|
1464
1492
|
if (n && typeof t === "function") {
|
|
1465
|
-
const n = t(e.
|
|
1493
|
+
const n = t(e.le);
|
|
1466
1494
|
t = () => n;
|
|
1467
1495
|
}
|
|
1468
1496
|
// #4110: the write replaces a failed derivation's outcome. It lands without
|
|
@@ -1470,7 +1498,7 @@ function setSignal(e, t) {
|
|
|
1470
1498
|
// every later read keeps throwing the error the derivation left behind.
|
|
1471
1499
|
// The error stays the display until the write commits (D-41).
|
|
1472
1500
|
if (e.m & STATUS_ERROR) {
|
|
1473
|
-
e.T.
|
|
1501
|
+
e.T.Ie = e.T.U;
|
|
1474
1502
|
notifyStatus(e, STATUS_NONE, undefined);
|
|
1475
1503
|
}
|
|
1476
1504
|
t = setSignal(e, t);
|
|
@@ -1482,7 +1510,7 @@ function setSignal(e, t) {
|
|
|
1482
1510
|
e.R = e.R & ~REACTIVE_CHECK | REACTIVE_DIRTY;
|
|
1483
1511
|
insertIntoHeap(e, dirtyQueue);
|
|
1484
1512
|
schedule();
|
|
1485
|
-
} else if (e.
|
|
1513
|
+
} else if (e.ie !== NOT_PENDING) e.R |= REACTIVE_MANUAL_WRITE;
|
|
1486
1514
|
return t;
|
|
1487
1515
|
}
|
|
1488
1516
|
|