@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
|
@@ -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_PLUMBING, 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_PLUMBING, 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
|
|
|
@@ -28,7 +28,7 @@ GlobalQueue.Ze = e => {
|
|
|
28
28
|
if (e.i === EFFECT_TRACKED) {
|
|
29
29
|
deleteFromHeap(e, dirtyQueue);
|
|
30
30
|
e.Qe = true;
|
|
31
|
-
globalQueue.enqueue(EFFECT_USER, e.
|
|
31
|
+
globalQueue.enqueue(EFFECT_USER, e.Z);
|
|
32
32
|
}
|
|
33
33
|
// L2: a zombie's fate is the seam's — its owner's commit disposes it, or
|
|
34
34
|
// the park cancels the pass (the write that dirtied it is held). It runs
|
|
@@ -82,11 +82,23 @@ function ownerInSnapshotScope(e) {
|
|
|
82
82
|
|
|
83
83
|
function setSnapshotCapture(e) {
|
|
84
84
|
snapshotCaptureActive = e;
|
|
85
|
-
if (e && !snapshotSources) snapshotSources = new
|
|
85
|
+
if (e && !snapshotSources) snapshotSources = new NewSet;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Marks `owner` a snapshot scope: while capture is on, the readers created
|
|
90
|
+
* under it serve each source's snapshot (the value it had when hydration
|
|
91
|
+
* began) and replay at `releaseSnapshotScope`.
|
|
92
|
+
*
|
|
93
|
+
* With `served` (hydration), `owner` is instead a hydrated computed and
|
|
94
|
+
* `served` its server answer — what the server rendered every reader of the
|
|
95
|
+
* node with — landing after the node was created pending (so the creation
|
|
96
|
+
* arm skipped it). It becomes the node's snapshot when it has none, whether
|
|
97
|
+
* the node committed the answer or a client write had already taken its
|
|
98
|
+
* compute over. A claim made later reads the value its markup shows, and
|
|
99
|
+
* replays the node's own value at its release, as for a written signal.
|
|
100
|
+
*/ function markSnapshotScope(e, t) {
|
|
101
|
+
if (arguments.length < 2) e.ot = true; else if (snapshotCaptureActive && e.T?.st === undefined) takeSnapshot(e, t);
|
|
90
102
|
}
|
|
91
103
|
|
|
92
104
|
function releaseSnapshotScope(e) {
|
|
@@ -108,7 +120,7 @@ function releaseSubtree(e) {
|
|
|
108
120
|
if (e.R & REACTIVE_SNAPSHOT_STALE) {
|
|
109
121
|
e.R &= ~REACTIVE_SNAPSHOT_STALE;
|
|
110
122
|
e.R |= REACTIVE_DIRTY;
|
|
111
|
-
if (dirtyQueue.
|
|
123
|
+
if (dirtyQueue.lt > e.Et) dirtyQueue.lt = e.Et;
|
|
112
124
|
insertIntoHeap(e, dirtyQueue);
|
|
113
125
|
}
|
|
114
126
|
}
|
|
@@ -125,7 +137,7 @@ function clearSnapshots() {
|
|
|
125
137
|
// `!== undefined` — assign, don't `delete`: deleting a field pushes the
|
|
126
138
|
// object to dictionary mode for every later read of every field.
|
|
127
139
|
const t = e.T;
|
|
128
|
-
if (t != null) t.
|
|
140
|
+
if (t != null) t.st = undefined;
|
|
129
141
|
}
|
|
130
142
|
snapshotSources = null;
|
|
131
143
|
}
|
|
@@ -158,8 +170,8 @@ function recompute(e, t = false) {
|
|
|
158
170
|
// The mount a first pass belongs to: its creator's lane, or the one its
|
|
159
171
|
// creator's own mount came from — a render effect a verdict lane's mount's
|
|
160
172
|
// content creates is that mount's too (#4166). Lanes only, like the seat.
|
|
161
|
-
const
|
|
162
|
-
const
|
|
173
|
+
const l = laneRoot;
|
|
174
|
+
const E = laneStageHook && (laneRoot = t && (s || l));
|
|
163
175
|
setPassLane(!laneStageHook ? null : (e.R & REACTIVE_LANE_DIRTY || (e.l & (CONFIG_OVERRIDE | CONFIG_GUESS)) === CONFIG_OVERRIDE) && e.T?.Je?.V ? e.T.Je : // Loose `!=`: false with no creator lane (undefined == null).
|
|
164
176
|
t && s?._parent.it != s && creatorPass(context)?.R & REACTIVE_RECOMPUTING_DEPS ? s : null);
|
|
165
177
|
// Attribution hook: fired before this run touches the dep list — `_deps`
|
|
@@ -175,7 +187,7 @@ function recompute(e, t = false) {
|
|
|
175
187
|
if (!t) {
|
|
176
188
|
deleteFromHeap(e, dirtyQueue);
|
|
177
189
|
if (e.T !== null) {
|
|
178
|
-
e.T.
|
|
190
|
+
e.T.Ee = null;
|
|
179
191
|
// Supersede is where an iterator flight dies (#3122): close it now.
|
|
180
192
|
// Idempotent with the cleanup-channel close.
|
|
181
193
|
releaseFlightTeardown(e);
|
|
@@ -191,6 +203,8 @@ function recompute(e, t = false) {
|
|
|
191
203
|
// A29, or mounted by a pass of it still awaiting its commit, #4010) keeps
|
|
192
204
|
// staging: live writes keep it current, it effects at the swap (ruling
|
|
193
205
|
// A).
|
|
206
|
+
// A boundary's structure (CONFIG_FRAME) is the frame like a render
|
|
207
|
+
// effect: it publishes mainline outside its transaction's flush (#4283).
|
|
194
208
|
// A pending flight is the transition's own work (#3884). A probe on an
|
|
195
209
|
// earlier pass left CONFIG_VERDICT, and a frame read here would serve the
|
|
196
210
|
// committed screen, settle the flight and drop the hold — a superseding
|
|
@@ -204,15 +218,19 @@ function recompute(e, t = false) {
|
|
|
204
218
|
// world (`read`), and has left it otherwise.
|
|
205
219
|
// An answered deferred node's re-pass is a lagging question (D-40):
|
|
206
220
|
// it joins nothing, so the write that reached it commits mainline.
|
|
207
|
-
|
|
221
|
+
// `EFFECT_RENDER << 23` is CONFIG_FRAME (a render effect, a boundary's
|
|
222
|
+
// tree and output), spelled from the effect type as at creation.
|
|
223
|
+
if (t.V || 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)) {
|
|
208
224
|
// Published mainline, it is not held: the frame this pass builds is
|
|
209
225
|
// replaced on the spot (#3404) — this flush's commit retires the
|
|
210
226
|
// committed frame the held pass had parked (`commitPendingNode`),
|
|
211
227
|
// after the new frame's first runs, and the held pass's own
|
|
212
228
|
// children die below (`disposeChildren`). The landing re-derives a
|
|
213
229
|
// fresh held frame for the reveal (`_reruns`).
|
|
214
|
-
|
|
215
|
-
|
|
230
|
+
// Not held any more, whichever frame reader it is: its readers read
|
|
231
|
+
// the value this pass publishes.
|
|
232
|
+
e.ie = NOT_PENDING;
|
|
233
|
+
e.l &= ~CONFIG_HELD;
|
|
216
234
|
}
|
|
217
235
|
}
|
|
218
236
|
// The previous pass's frame. If that pass is still awaiting its commit
|
|
@@ -233,7 +251,7 @@ function recompute(e, t = false) {
|
|
|
233
251
|
// An error a pass staged (D-41) is replaced by this pass's answer: a
|
|
234
252
|
// value, a flight, or an error of its own (#4109, #4145).
|
|
235
253
|
if (e.l & CONFIG_STAGED_ERROR) {
|
|
236
|
-
e.
|
|
254
|
+
e.ie = NOT_PENDING;
|
|
237
255
|
e.l ^= CONFIG_STAGED_ERROR;
|
|
238
256
|
}
|
|
239
257
|
}
|
|
@@ -241,22 +259,22 @@ function recompute(e, t = false) {
|
|
|
241
259
|
// Capture both error and pending status before the compute clears them.
|
|
242
260
|
// A conditional can drop its pending source and recover to an unchanged
|
|
243
261
|
// value, leaving blocked dependents outside that source’s settle walk.
|
|
244
|
-
const
|
|
245
|
-
const
|
|
246
|
-
const _ =
|
|
262
|
+
const a = e.m & STATUS_ERROR ? e.T?.F : undefined;
|
|
263
|
+
const u = (e.m & STATUS_PENDING) !== 0;
|
|
264
|
+
const _ = u ? e.T?.se : undefined;
|
|
247
265
|
// Pending SOURCE-hood, captured before the compute clears status: a node
|
|
248
266
|
// whose own flight parked dependents self-registers in _pendingSources
|
|
249
267
|
// (notifyStatus, isSource). If this recompute supersedes that flight and
|
|
250
268
|
// settles synchronously, those dependents settle HERE — asyncWrite's
|
|
251
269
|
// settlePendingSource walk never runs for a landing that was preempted
|
|
252
270
|
// (#3181).
|
|
253
|
-
const I = e.T?.
|
|
271
|
+
const I = e.T?.se?.has(e);
|
|
254
272
|
// D-41: this pass's error is staged, not published (`stageError`).
|
|
255
|
-
let
|
|
273
|
+
let f = false;
|
|
256
274
|
// A19 exc. 2: a `refresh()` re-asks the question already answered — quiet
|
|
257
275
|
// for the verdict — unless the question in flight is a new one, which the
|
|
258
276
|
// re-ask does not launder (a poll during a quiet confirm stays quiet).
|
|
259
|
-
const
|
|
277
|
+
const N = (e.R & REACTIVE_REASK) !== 0 && !(e.m & STATUS_PENDING && e.T !== null && !e.T.ut);
|
|
260
278
|
const T = context;
|
|
261
279
|
context = e;
|
|
262
280
|
e._ = null;
|
|
@@ -266,33 +284,33 @@ function recompute(e, t = false) {
|
|
|
266
284
|
// live writes until the commit that disposes it, so every per-pass wipe
|
|
267
285
|
// — here, the finally below, updateIfNecessary — carries it (#3543).
|
|
268
286
|
e.R = REACTIVE_RECOMPUTING_DEPS | e.R & (REACTIVE_ZOMBIE | REACTIVE_REASK);
|
|
269
|
-
e.
|
|
287
|
+
e.ae = clock;
|
|
270
288
|
// The pass's previous value: its staging, else a lane node's lane value
|
|
271
289
|
// when it has one (lanes.ts), else the committed one.
|
|
272
|
-
let S = e.
|
|
290
|
+
let S = e.ie !== NOT_PENDING ? e.ie : e.l & CONFIG_OVERRIDE && e.T.V !== NOT_PENDING ? e.T.V : e.le;
|
|
273
291
|
let c = e.Et;
|
|
274
292
|
let A = false;
|
|
275
293
|
// L2: did this pass read the future (REACTIVE_JOINED, set by read) — or,
|
|
276
294
|
// a lane's mount, a staging of this flush (#3851, #4166)?
|
|
277
295
|
let R = false;
|
|
278
|
-
let
|
|
296
|
+
let C = tracking;
|
|
279
297
|
tracking = true;
|
|
280
298
|
try {
|
|
281
299
|
if (!false && e.l & CONFIG_SYNC) {
|
|
282
300
|
S = e.X(S);
|
|
283
|
-
if (e.T !== null) e.T.
|
|
301
|
+
if (e.T !== null) e.T.Ee = null;
|
|
284
302
|
e.ce = false;
|
|
285
303
|
} else {
|
|
286
304
|
// A projection's body self-registers its flight through handleAsync
|
|
287
305
|
// (with the commit as setter): its undefined return is not a sync
|
|
288
306
|
// answer, and the outer handleAsync must not clobber the registration.
|
|
289
|
-
const t = e.T?.
|
|
307
|
+
const t = e.T?.Ee;
|
|
290
308
|
const n = e.X(S);
|
|
291
309
|
const o = typeof n === "object" && n !== null;
|
|
292
|
-
const s = e.T?.
|
|
310
|
+
const s = e.T?.Ee !== t;
|
|
293
311
|
S = s || !o ? n : handleAsync(e, n);
|
|
294
312
|
if (!s && !o) {
|
|
295
|
-
if (e.T !== null) e.T.
|
|
313
|
+
if (e.T !== null) e.T.Ee = null;
|
|
296
314
|
// A sync (non-object) return is the first real answer; async-shaped
|
|
297
315
|
// results clear inside handleAsync at their own landing points, and a
|
|
298
316
|
// self-registered flight clears when its own handleAsync lands.
|
|
@@ -318,12 +336,12 @@ function recompute(e, t = false) {
|
|
|
318
336
|
} else {
|
|
319
337
|
// D-41: a pass of a held node, or one that read a staging of this
|
|
320
338
|
// flush, stages its error (`stageError`).
|
|
321
|
-
if (n || !(
|
|
322
|
-
if (n) ext(e).
|
|
339
|
+
if (n || !(f = stageError(e, t, true))) {
|
|
340
|
+
if (n) ext(e).fe = true;
|
|
323
341
|
notifyStatus(e, n ? STATUS_PENDING : STATUS_ERROR, t);
|
|
324
342
|
// The replacement source is fully propagated now. If no new flight
|
|
325
343
|
// re-owned self, retire the superseded flight and its dependent copies.
|
|
326
|
-
if (n && I && !e.T?.
|
|
344
|
+
if (n && I && !e.T?.Ee) settlePendingSource(e);
|
|
327
345
|
}
|
|
328
346
|
// A re-park drops what the earlier pass carried (#3456): a source this
|
|
329
347
|
// pass no longer reaches — its branch switched, or a fresh flight
|
|
@@ -334,11 +352,11 @@ function recompute(e, t = false) {
|
|
|
334
352
|
// re-park twin of the unchanged-value recovery sweep below; dependents
|
|
335
353
|
// with another path keep the source (retryReaches). A staged error
|
|
336
354
|
// answered what the pass carried: the same.
|
|
337
|
-
if ((n ||
|
|
355
|
+
if ((n || f) && _) for (const t of _) if (t !== e && !e.T?.se?.has(t)) settlePendingSource(e, t);
|
|
338
356
|
}
|
|
339
357
|
} finally {
|
|
340
|
-
tracking =
|
|
341
|
-
if (laneStageHook) laneRoot =
|
|
358
|
+
tracking = C;
|
|
359
|
+
if (laneStageHook) laneRoot = l;
|
|
342
360
|
// Consume the missed-wake latch (#3037, set by insertSubs): a dep write
|
|
343
361
|
// landed beneath this pass on a link it had already validated. The wipe
|
|
344
362
|
// below must not key off DIRTY/CHECK — the read-time pull protocol
|
|
@@ -348,7 +366,7 @@ function recompute(e, t = false) {
|
|
|
348
366
|
// A mount's staged read joins it — except a nested derivation left
|
|
349
367
|
// pending: no render effect reaches it yet, so it holds nothing and
|
|
350
368
|
// reaches no boundary (Reader, #4203). A reader that does is caught.
|
|
351
|
-
R = !!(e.R & (
|
|
369
|
+
R = !!(e.R & (E && (s || n || !(e.m & STATUS_PENDING)) ? REACTIVE_JOINED | REACTIVE_STAGED_READ : REACTIVE_JOINED));
|
|
352
370
|
// A verdict reader is one for as long as it probes, or reads a memo
|
|
353
371
|
// that does (`read`): a pass that did neither (REACTIVE_PROBED, the same
|
|
354
372
|
// bit) is an ordinary derivation again.
|
|
@@ -366,7 +384,7 @@ function recompute(e, t = false) {
|
|
|
366
384
|
// A19 exc. 2: a pass that went pending on a `refresh()` re-asks the
|
|
367
385
|
// question already answered — quiet for the verdict; any other pending
|
|
368
386
|
// pass is a new question. Settled, there is nothing to classify.
|
|
369
|
-
if (e.T !== null) e.T.ut =
|
|
387
|
+
if (e.T !== null) e.T.ut = N && (e.m & STATUS_PENDING) !== 0;
|
|
370
388
|
}
|
|
371
389
|
// A node that died during its own pass (#3621) is dead at the end of it,
|
|
372
390
|
// and the pass is void. Its owner's teardown already unlinked its deps,
|
|
@@ -381,7 +399,7 @@ function recompute(e, t = false) {
|
|
|
381
399
|
// attribution frame opened at the top is still closed.
|
|
382
400
|
if (e.R & REACTIVE_DISPOSED) {
|
|
383
401
|
clearDeps(e);
|
|
384
|
-
if (e.T !== null) e.T.
|
|
402
|
+
if (e.T !== null) e.T.Ee = null;
|
|
385
403
|
if (attrHooks !== null) attrHooks.recomputeEnd(e, t, false, false, false, false);
|
|
386
404
|
setPassLane(s);
|
|
387
405
|
return;
|
|
@@ -391,12 +409,12 @@ function recompute(e, t = false) {
|
|
|
391
409
|
// seat that read none of the lane's world has left it: its result is the
|
|
392
410
|
// frame's (a derivation whose branch no longer reaches the guess). A
|
|
393
411
|
// guess is written, not derived — it never leaves this way.
|
|
394
|
-
let
|
|
412
|
+
let d = !laneStageHook ? null : passLane;
|
|
395
413
|
// Listed before its staging, a pending pass included (the lane's own
|
|
396
414
|
// flight is the lane's); false: the pass left the lane (lanes.ts).
|
|
397
|
-
const O = !!e.T?.F ||
|
|
398
|
-
if (laneStageHook &&
|
|
399
|
-
if (laneStageHook(e,
|
|
415
|
+
const O = !!e.T?.F || f;
|
|
416
|
+
if (laneStageHook && d !== null) {
|
|
417
|
+
if (laneStageHook(e, d, t, O)) setPassLane(d); else d = null;
|
|
400
418
|
}
|
|
401
419
|
if (!O) {
|
|
402
420
|
// Observe-tier fan-in (HUGE_FAN_IN): the validated prefix [_deps.._depsTail]
|
|
@@ -419,9 +437,9 @@ function recompute(e, t = false) {
|
|
|
419
437
|
// publishes to — the staged `_pendingValue` when staged; a lane node's
|
|
420
438
|
// lane value (a node entering the lane this pass has none yet — its
|
|
421
439
|
// committed value, `laneStage` having voided a frame staging).
|
|
422
|
-
const o = e.
|
|
440
|
+
const o = e.ie !== NOT_PENDING ? e.ie : e.l & CONFIG_OVERRIDE && e.T.V !== NOT_PENDING ? e.T.V : e.le;
|
|
423
441
|
// A throwing comparator is contained inside `isSame` (A3, #2837; #4111).
|
|
424
|
-
const s = !n && r || !!e.T?.
|
|
442
|
+
const s = !n && r || !!e.T?.Te || !isSame(e, o, S);
|
|
425
443
|
// A committed derived change becomes a cause for this node's subscribers,
|
|
426
444
|
// chaining their attribution through this node to the root write.
|
|
427
445
|
if (attrHooks !== null) {
|
|
@@ -438,14 +456,14 @@ function recompute(e, t = false) {
|
|
|
438
456
|
// last applied owes no run (lanes.ts).
|
|
439
457
|
!(e.R & REACTIVE_SCREEN_READ && !e.Qe && Object.is(e._t, S)) &&
|
|
440
458
|
// A re-staging pass owes no run: the commit replays the effect (#3802).
|
|
441
|
-
e.
|
|
459
|
+
e.ie === NOT_PENDING) {
|
|
442
460
|
e.Qe = !e.T?.F;
|
|
443
461
|
// Reuse one bound runner per effect — runEffect no-ops on a stale
|
|
444
462
|
// `_modified`, so re-enqueueing the same function is harmless.
|
|
445
463
|
// Lane work's run is the lane's (released at its reveal) — a first
|
|
446
464
|
// pass under a lane too: `effect()` skips the synchronous first run of
|
|
447
465
|
// a lane's node.
|
|
448
|
-
if (!t ||
|
|
466
|
+
if (!t || d !== null) globalQueue.enqueue(n, e.W ??= GlobalQueue.K.bind(null, e), d);
|
|
449
467
|
}
|
|
450
468
|
// Lanes. A written guess's own pass from the frame: its source
|
|
451
469
|
// recomputed it — the truth (A18: "the source is whatever recomputes the
|
|
@@ -453,7 +471,8 @@ function recompute(e, t = false) {
|
|
|
453
471
|
// confirm is a landing too — the lane's end commits it) and notifies
|
|
454
472
|
// only a correction: an equal truth re-runs nothing (lanes.ts). (As the
|
|
455
473
|
// lane's own work the lane judged it — `laneStage`.)
|
|
456
|
-
|
|
474
|
+
// (The hook first: a build without lanes folds the arm away.)
|
|
475
|
+
if (laneOutcomeHook && !t && d === null && e.l & CONFIG_GUESS && laneOutcomeHook(e, S, O)) ; else if (O) ; else if (s) {
|
|
457
476
|
// L2: a first pass is born held — staged, committed with the
|
|
458
477
|
// transaction, its effect's first run the landing's (A29; `effect()`
|
|
459
478
|
// skips the synchronous first run on a staged value) — when the flush
|
|
@@ -470,15 +489,15 @@ function recompute(e, t = false) {
|
|
|
470
489
|
// pass that runs before anything joins: the pass's input, not a verdict
|
|
471
490
|
// after it. An effect still carrying an uncommitted staged value
|
|
472
491
|
// re-stages: the commit applies the latest pass, not the born-held one.
|
|
473
|
-
if (
|
|
492
|
+
if (d !== null) {
|
|
474
493
|
// Lane work: the lane's value — into the slot while the lane has not
|
|
475
494
|
// revealed (its reveal shows it), as its staging once it has (the
|
|
476
495
|
// next reveal promotes it; the screen keeps the revealed value). An
|
|
477
496
|
// effect's value slot is private: the run the lane holds is what
|
|
478
497
|
// shows it — unless the effect was born held, whose first run is the
|
|
479
498
|
// commit's (A29): it re-stages, as below.
|
|
480
|
-
if (n && e.
|
|
481
|
-
} else if (t ? !((flushTransaction !== null || passTx !== null) && (R || creatorPass(T)?.R & REACTIVE_JOINED)) : n && e.
|
|
499
|
+
if (n && e.ie === NOT_PENDING) e.le = S; else if (n || d.It) e.ie = S; else e.T.V = S;
|
|
500
|
+
} else if (t ? !((flushTransaction !== null || passTx !== null) && (R || creatorPass(T)?.R & REACTIVE_JOINED)) : n && e.ie === NOT_PENDING) {
|
|
482
501
|
// A first pass publishes directly. So does an effect: its value slot
|
|
483
502
|
// is private (only its own run reads it), and the staging round-trip
|
|
484
503
|
// (queuePendingNode + commitPendingNodes) paid per effect on the
|
|
@@ -489,9 +508,9 @@ function recompute(e, t = false) {
|
|
|
489
508
|
// sources commit) must see the fresh value while PLAIN reads stay
|
|
490
509
|
// committed until flush (#3009 purity). The pending round-trip is
|
|
491
510
|
// that separation; it cannot be skipped on any path a pull can reach.
|
|
492
|
-
e.
|
|
511
|
+
e.le = S;
|
|
493
512
|
} else {
|
|
494
|
-
e.
|
|
513
|
+
e.ie = S;
|
|
495
514
|
// Born held: in the transaction from birth, so a read of it this
|
|
496
515
|
// tick is a read of its world (the mount-during-a-hold case), not
|
|
497
516
|
// only after the seam parks it.
|
|
@@ -509,7 +528,7 @@ function recompute(e, t = false) {
|
|
|
509
528
|
// #3851): the mount is part of the hold, and the boundary appears
|
|
510
529
|
// at its commit, its fallback never seen (D-13, U-3) — a `<Show>`
|
|
511
530
|
// mounting it over a landing included.
|
|
512
|
-
freshHook?.(e, !
|
|
531
|
+
freshHook?.(e, !E && flushTransaction);
|
|
513
532
|
}
|
|
514
533
|
}
|
|
515
534
|
// insertSubs only walks _subs (no scheduling of its own), so a
|
|
@@ -517,7 +536,7 @@ function recompute(e, t = false) {
|
|
|
517
536
|
if (e.o !== null) {
|
|
518
537
|
insertSubs(e);
|
|
519
538
|
// Lane work re-staged: the lane's members re-derive as its work.
|
|
520
|
-
if (
|
|
539
|
+
if (d !== null) laneDirty(e, d);
|
|
521
540
|
}
|
|
522
541
|
} else if (e.Et != c) {
|
|
523
542
|
for (let t = e.o; t !== null; t = t.t) insertIntoHeapHeight(t.u, dirtyQueue);
|
|
@@ -528,7 +547,7 @@ function recompute(e, t = false) {
|
|
|
528
547
|
// recoveries ride insertSubs above; a comparator throw re-errored the node
|
|
529
548
|
// (el._x?._error re-set), so this only runs on a genuinely clean recovery.
|
|
530
549
|
if (!s && !e.T?.F) {
|
|
531
|
-
if (
|
|
550
|
+
if (a !== undefined) settleErroredDependents(e, a);
|
|
532
551
|
// Self-registration (this node's own superseded flight) is the #3181
|
|
533
552
|
// sweep's business below — retiring it here too would walk twice.
|
|
534
553
|
if (_) for (const t of _) if (t !== e) settlePendingSource(e, t);
|
|
@@ -546,7 +565,7 @@ function recompute(e, t = false) {
|
|
|
546
565
|
// hold's commit (D-12) — but the hold does not wait for it: its only
|
|
547
566
|
// readers are behind the fallback (`_hidden`; F-4, #3540). A boundary
|
|
548
567
|
// re-armed by `on` owns what it collects outright (D-14): not held.
|
|
549
|
-
if (t &&
|
|
568
|
+
if (t && d === null && R && e.m & STATUS_PENDING) {
|
|
550
569
|
const t = flushTransaction ?? passTx;
|
|
551
570
|
if (t && !t.V && freshHook?.(e) !== CAUGHT_REARMED) holdNode(e, t);
|
|
552
571
|
}
|
|
@@ -567,21 +586,21 @@ function recompute(e, t = false) {
|
|
|
567
586
|
// kept at a park — one spurious recompute at most). A creation pass and a
|
|
568
587
|
// tracked effect trim now: their frames are replaceable like a direct
|
|
569
588
|
// commit, and a tracked effect's spurious run would be user-visible.
|
|
570
|
-
if (!e.T?.F && e.
|
|
589
|
+
if (!e.T?.F && e.ie === NOT_PENDING && !(n && e.Qe)) {
|
|
571
590
|
if (t || n === EFFECT_TRACKED) trimStaleDeps(e); else heldTrim(e);
|
|
572
591
|
}
|
|
573
592
|
// Observe: the posture the run executed under — lane work (optimistic:
|
|
574
593
|
// overlay, never waste), a transaction's pass (held), or plain.
|
|
575
|
-
if (attrHooks !== null) attrHooks.recomputeEnd(e, t, i,
|
|
594
|
+
if (attrHooks !== null) attrHooks.recomputeEnd(e, t, i, d !== null, flushTransaction !== null || passTx !== null || (e.l & CONFIG_HELD) !== 0, e.ie !== NOT_PENDING);
|
|
576
595
|
// A staged value, a parked frame (L2: the commit retires it), or status the
|
|
577
596
|
// commit sweep must settle (a pending or uninitialized pass), queues the
|
|
578
597
|
// node for this flush's commit. A first pass queues only when pending or
|
|
579
598
|
// born held: otherwise its value published directly. A queued pass's
|
|
580
599
|
// children are uncommitted (CONFIG_STAGED) until that commit.
|
|
581
|
-
if (e.
|
|
600
|
+
if (e.ie !== NOT_PENDING || e.T !== null && (e.T.ft !== null || e.T.Nt !== null) || (e.m & (STATUS_PENDING | STATUS_UNINITIALIZED)) !== 0 && (!t || (e.m & STATUS_PENDING) !== 0)) {
|
|
582
601
|
e.l |= CONFIG_STAGED;
|
|
583
602
|
// Lane work is the lane's to reveal (its seam), not this flush's commit.
|
|
584
|
-
if (
|
|
603
|
+
if (d === null) queuePendingNode(e);
|
|
585
604
|
}
|
|
586
605
|
setPassLane(s);
|
|
587
606
|
// Missed-wake reschedule (see the finally above): values this pass read
|
|
@@ -614,7 +633,7 @@ function updateIfNecessary(e) {
|
|
|
614
633
|
}
|
|
615
634
|
}
|
|
616
635
|
}
|
|
617
|
-
if (e.R & REACTIVE_DIRTY || e.T?.F && e.
|
|
636
|
+
if (e.R & REACTIVE_DIRTY || e.T?.F && e.ae < clock && !e.T?.Ee) {
|
|
618
637
|
recompute(e);
|
|
619
638
|
}
|
|
620
639
|
// The guard above refused an already-disposed node; the recompute it just
|
|
@@ -644,16 +663,16 @@ function computed(e, t) {
|
|
|
644
663
|
// plus the slots.
|
|
645
664
|
const s = {
|
|
646
665
|
id: inheritId(t, n, context),
|
|
647
|
-
l: (n ? CONFIG_TRANSPARENT : 0) | (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (!context || t?.lazy ? CONFIG_AUTO_DISPOSE : 0) | (t?.sync ? CONFIG_SYNC : 0) | (t?.
|
|
666
|
+
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) | (
|
|
648
667
|
// Plumbing is an observe-tier notion (a name and records to
|
|
649
668
|
// withhold); the prod literal never carries the bit.
|
|
650
669
|
t?.Tt ? CONFIG_PLUMBING : 0) | (t?.St ?? 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
|
|
651
|
-
|
|
670
|
+
je: t?.equals ?? isEqual,
|
|
652
671
|
rt: null,
|
|
653
672
|
A: context?.A ?? defaultContext,
|
|
654
673
|
ct: 0,
|
|
655
674
|
X: e,
|
|
656
|
-
|
|
675
|
+
le: o ? t.loadingValue : undefined,
|
|
657
676
|
Et: 0,
|
|
658
677
|
At: undefined,
|
|
659
678
|
Rt: null,
|
|
@@ -661,15 +680,15 @@ function computed(e, t) {
|
|
|
661
680
|
_: null,
|
|
662
681
|
Ne: 0,
|
|
663
682
|
o: null,
|
|
664
|
-
|
|
683
|
+
Ct: null,
|
|
665
684
|
_parent: context,
|
|
666
685
|
Xe: null,
|
|
667
|
-
|
|
686
|
+
dt: null,
|
|
668
687
|
ze: null,
|
|
669
688
|
R: t?.lazy ? REACTIVE_LAZY : REACTIVE_NONE,
|
|
670
689
|
m: o ? 0 : STATUS_UNINITIALIZED,
|
|
671
|
-
|
|
672
|
-
|
|
690
|
+
ae: clock,
|
|
691
|
+
ie: NOT_PENDING,
|
|
673
692
|
Ot: -1,
|
|
674
693
|
ce: o,
|
|
675
694
|
T: null,
|
|
@@ -691,23 +710,23 @@ function computed(e, t) {
|
|
|
691
710
|
* this; hot paths read `el._x?._field` gated by the _config presence bits.
|
|
692
711
|
* Never call ext() just to store a field's default. */ function ext(e) {
|
|
693
712
|
return e.T ??= {
|
|
713
|
+
Ee: null,
|
|
694
714
|
Se: null,
|
|
695
|
-
ae: null,
|
|
696
715
|
F: undefined,
|
|
697
|
-
se: undefined,
|
|
698
716
|
fe: undefined,
|
|
717
|
+
se: undefined,
|
|
699
718
|
Dt: undefined,
|
|
700
|
-
|
|
701
|
-
Nt: null,
|
|
719
|
+
st: undefined,
|
|
702
720
|
ft: null,
|
|
721
|
+
Nt: null,
|
|
703
722
|
Je: null,
|
|
704
723
|
ut: false,
|
|
705
724
|
Gt: NOT_PENDING,
|
|
706
725
|
Ft: -1,
|
|
707
|
-
|
|
726
|
+
de: 0,
|
|
708
727
|
V: NOT_PENDING,
|
|
709
728
|
I: 0,
|
|
710
|
-
|
|
729
|
+
Te: undefined
|
|
711
730
|
};
|
|
712
731
|
}
|
|
713
732
|
|
|
@@ -771,10 +790,16 @@ function computed(e, t) {
|
|
|
771
790
|
// read the screen. Lane work with no committed value yet reads a flight
|
|
772
791
|
// as a mount's memo does: it enters, and the boundary it mounts catches
|
|
773
792
|
// the pending (#3540).
|
|
793
|
+
// A boundary's tree and output (CONFIG_FRAME) read as a render effect
|
|
794
|
+
// does: structure, not a derivation of what it shows (#4283). A mount
|
|
795
|
+
// under a hold still enters it: the content memo it reads is born held,
|
|
796
|
+
// with no committed value to serve.
|
|
774
797
|
// (Verdict readers and lanes exist only with their engines installed.)
|
|
775
798
|
const n = observeFlightHook && e.l & CONFIG_VERDICT;
|
|
776
799
|
const o = laneStageHook && passLane !== null;
|
|
777
|
-
if ((!o || e.m & STATUS_UNINITIALIZED && t.m & STATUS_PENDING) && !n && (
|
|
800
|
+
if ((!o || e.m & STATUS_UNINITIALIZED && t.m & STATUS_PENDING) && !n && (
|
|
801
|
+
// CONFIG_FRAME, spelled as at the effect's creation.
|
|
802
|
+
!(e.l & EFFECT_RENDER << 23) || t.l & CONFIG_INPUTS_PUBLISHED)) return false;
|
|
778
803
|
const s = txOf(t);
|
|
779
804
|
// Lane work and verdict readers are never the transaction's pass, whichever
|
|
780
805
|
// flush runs it: a node T holds re-enters T when it re-runs (`recompute`'s
|
|
@@ -794,7 +819,7 @@ function computed(e, t) {
|
|
|
794
819
|
|
|
795
820
|
/** The computed whose pass a creation under `owner` belongs to (a root's is
|
|
796
821
|
* its nearest computed ancestor's), or null outside every pass. */ function creatorPass(e) {
|
|
797
|
-
return e !== null && e.Pt ? e.
|
|
822
|
+
return e !== null && e.Pt ? e.ht : e;
|
|
798
823
|
}
|
|
799
824
|
|
|
800
825
|
/** L2 — park the previous pass's frame for this node's commit: the children
|
|
@@ -803,8 +828,8 @@ function computed(e, t) {
|
|
|
803
828
|
* the frame that replaces them (`commitPendingNode`) or when the owner does. */ function parkChildren(e) {
|
|
804
829
|
markDisposal(e);
|
|
805
830
|
const t = ext(e);
|
|
806
|
-
t.
|
|
807
|
-
t.
|
|
831
|
+
t.Nt = e.rt;
|
|
832
|
+
t.ft = e.ze;
|
|
808
833
|
e.rt = null;
|
|
809
834
|
e.ze = null;
|
|
810
835
|
e.ct = 0;
|
|
@@ -816,21 +841,24 @@ function computed(e, t) {
|
|
|
816
841
|
* mode (recompute is called explicitly by `effect()`), so we hardcode the lazy bits and skip
|
|
817
842
|
* the auto-dispose CONFIG bit (effect() previously cleared it post-construction).
|
|
818
843
|
*/ function createEffectNode(e, t, n, o, s) {
|
|
819
|
-
const
|
|
844
|
+
const l = s?.transparent ?? false;
|
|
820
845
|
// Prod and observe boilerplates — see computed() for why the observe tier
|
|
821
846
|
// gets its `_name` as a literal slot rather than a write after the fact.
|
|
822
847
|
// The default label is the node kind (tracked effects relabel their computed
|
|
823
848
|
// in trackedEffect); the wrappers in signals.ts no longer spread a name into
|
|
824
849
|
// the options to get it.
|
|
825
|
-
const
|
|
826
|
-
id: inheritId(s,
|
|
827
|
-
l:
|
|
828
|
-
|
|
850
|
+
const E = {
|
|
851
|
+
id: inheritId(s, l, context),
|
|
852
|
+
l:
|
|
853
|
+
// CONFIG_FRAME for a render effect (EFFECT_RENDER = 1; a user
|
|
854
|
+
// effect's 1 << 24 is unused).
|
|
855
|
+
o << 23 | (l ? CONFIG_TRANSPARENT : 0) | (s?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (s?.sync ? CONFIG_SYNC : 0) | (s?.St ?? 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
|
|
856
|
+
je: false,
|
|
829
857
|
rt: null,
|
|
830
858
|
A: context?.A ?? defaultContext,
|
|
831
859
|
ct: 0,
|
|
832
860
|
X: e,
|
|
833
|
-
|
|
861
|
+
le: undefined,
|
|
834
862
|
Et: 0,
|
|
835
863
|
At: undefined,
|
|
836
864
|
Rt: null,
|
|
@@ -838,21 +866,21 @@ function computed(e, t) {
|
|
|
838
866
|
_: null,
|
|
839
867
|
Ne: 0,
|
|
840
868
|
o: null,
|
|
841
|
-
|
|
869
|
+
Ct: null,
|
|
842
870
|
_parent: context,
|
|
843
871
|
Xe: null,
|
|
844
|
-
|
|
872
|
+
dt: null,
|
|
845
873
|
ze: null,
|
|
846
874
|
R: REACTIVE_LAZY,
|
|
847
875
|
m: STATUS_UNINITIALIZED,
|
|
848
|
-
|
|
849
|
-
|
|
876
|
+
ae: clock,
|
|
877
|
+
ie: NOT_PENDING,
|
|
850
878
|
Ot: -1,
|
|
851
879
|
ce: false,
|
|
852
880
|
Qe: false,
|
|
853
881
|
_t: undefined,
|
|
854
882
|
Ge: t,
|
|
855
|
-
|
|
883
|
+
Ht: n,
|
|
856
884
|
kt: undefined,
|
|
857
885
|
i: o,
|
|
858
886
|
T: null,
|
|
@@ -865,9 +893,9 @@ function computed(e, t) {
|
|
|
865
893
|
// allocation on EVERY effect at creation (an alloc + 19 field stores,
|
|
866
894
|
// +23% effect creation, caught by the creation benches). Only genuinely
|
|
867
895
|
// per-node channels (boundaries) live on _x.
|
|
868
|
-
if (s?.unobserved) ext(
|
|
869
|
-
setupComputedNode(
|
|
870
|
-
return
|
|
896
|
+
if (s?.unobserved) ext(E).Dt = s.unobserved;
|
|
897
|
+
setupComputedNode(E, lazyOptions);
|
|
898
|
+
return E;
|
|
871
899
|
}
|
|
872
900
|
|
|
873
901
|
/**
|
|
@@ -886,7 +914,7 @@ function setEffectStatusNotify(e) {
|
|
|
886
914
|
* 0, and only effect literals carry the field) get the shared notifier.
|
|
887
915
|
* Presence doubles as the "display consumer" membership test in the status
|
|
888
916
|
* walks, exactly as the per-node field did when every effect carried one. */ function statusNotifierOf(e) {
|
|
889
|
-
return e.i ? effectStatusNotify
|
|
917
|
+
return e.i ? effectStatusNotify : null;
|
|
890
918
|
}
|
|
891
919
|
|
|
892
920
|
const lazyOptions = {
|
|
@@ -895,17 +923,13 @@ const lazyOptions = {
|
|
|
895
923
|
|
|
896
924
|
function setupComputedNode(e, t) {
|
|
897
925
|
e.Rt = e;
|
|
898
|
-
const n = context?.Pt ? context.
|
|
926
|
+
const n = context?.Pt ? context.ht : context;
|
|
899
927
|
if (context) linkChild(context, e);
|
|
900
928
|
if (n) e.Et = n.Et + 1;
|
|
901
929
|
wireExternalSourceHook?.(e);
|
|
902
930
|
!t?.lazy && recompute(e, true);
|
|
903
931
|
if (snapshotCaptureActive && !t?.lazy) {
|
|
904
|
-
if (!(e.m & STATUS_PENDING) && !(e.l & CONFIG_NO_SNAPSHOT))
|
|
905
|
-
ext(e).lt = e.Ie === undefined ? NO_SNAPSHOT : e.Ie;
|
|
906
|
-
e.l |= CONFIG_HAS_SNAPSHOT;
|
|
907
|
-
snapshotSources.add(e);
|
|
908
|
-
}
|
|
932
|
+
if (!(e.m & STATUS_PENDING) && !(e.l & CONFIG_NO_SNAPSHOT)) takeSnapshot(e, e.le);
|
|
909
933
|
}
|
|
910
934
|
}
|
|
911
935
|
|
|
@@ -915,13 +939,13 @@ function signal(e, t) {
|
|
|
915
939
|
// createSignal nodes so ownerPath can locate signal subjects; null here,
|
|
916
940
|
// and staying null on internal signals — one shape either way).
|
|
917
941
|
const n = {
|
|
918
|
-
|
|
919
|
-
l: (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (t?.
|
|
920
|
-
|
|
942
|
+
je: t?.equals ?? isEqual,
|
|
943
|
+
l: (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (t?.ue ? CONFIG_NO_SNAPSHOT : 0),
|
|
944
|
+
le: e,
|
|
921
945
|
o: null,
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
946
|
+
Ct: null,
|
|
947
|
+
ae: clock,
|
|
948
|
+
ie: NOT_PENDING,
|
|
925
949
|
Ot: -1,
|
|
926
950
|
T: null,
|
|
927
951
|
_name: t?.name ?? "signal",
|
|
@@ -929,11 +953,7 @@ function signal(e, t) {
|
|
|
929
953
|
H: null
|
|
930
954
|
};
|
|
931
955
|
if (t?.unobserved) ext(n).Dt = t.unobserved;
|
|
932
|
-
if (snapshotCaptureActive && !(n.l & CONFIG_NO_SNAPSHOT))
|
|
933
|
-
ext(n).lt = e === undefined ? NO_SNAPSHOT : e;
|
|
934
|
-
n.l |= CONFIG_HAS_SNAPSHOT;
|
|
935
|
-
snapshotSources.add(n);
|
|
936
|
-
}
|
|
956
|
+
if (snapshotCaptureActive && !(n.l & CONFIG_NO_SNAPSHOT)) takeSnapshot(n, e);
|
|
937
957
|
return n;
|
|
938
958
|
}
|
|
939
959
|
|
|
@@ -952,14 +972,14 @@ function isEqual(e, t) {
|
|
|
952
972
|
* the store's notification machinery. The observe literal adds the label
|
|
953
973
|
* and location slots (the store relabels `store.<key>` and stamps the
|
|
954
974
|
* store's `loc` when the engine is installed). */ function slotSignal(e, t, n, o, s) {
|
|
955
|
-
const
|
|
956
|
-
|
|
975
|
+
const l = {
|
|
976
|
+
je: t,
|
|
957
977
|
l: CONFIG_OWNED_WRITE | CONFIG_SLOT_NODE,
|
|
958
|
-
|
|
978
|
+
le: e,
|
|
959
979
|
o: null,
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
980
|
+
Ct: null,
|
|
981
|
+
ae: clock,
|
|
982
|
+
ie: NOT_PENDING,
|
|
963
983
|
Ot: -1,
|
|
964
984
|
T: null,
|
|
965
985
|
O: n,
|
|
@@ -971,7 +991,7 @@ function isEqual(e, t) {
|
|
|
971
991
|
_loc: undefined,
|
|
972
992
|
H: null
|
|
973
993
|
};
|
|
974
|
-
return
|
|
994
|
+
return l;
|
|
975
995
|
}
|
|
976
996
|
|
|
977
997
|
/**
|
|
@@ -1092,7 +1112,7 @@ function setVerdict(e) {
|
|
|
1092
1112
|
* never the graph — A32).
|
|
1093
1113
|
* False means the reader derives from the staged value.
|
|
1094
1114
|
*/ function readerSeesCommitted(e, t) {
|
|
1095
|
-
return !t || e.
|
|
1115
|
+
return !t || e.ie === NOT_PENDING || t.l & CONFIG_CHILDREN_FORBIDDEN;
|
|
1096
1116
|
}
|
|
1097
1117
|
|
|
1098
1118
|
/** A28 — set when a node is staged (queuePendingNode) OUTSIDE a flush;
|
|
@@ -1113,12 +1133,12 @@ function markUnflushedStaged() {
|
|
|
1113
1133
|
* is unflushed. Exempt: promoted writes (A28 (4): a write issued inside a
|
|
1114
1134
|
* recompute is promoted at that recompute's end — boundary and loading
|
|
1115
1135
|
* machinery, signals declared for in-computation writes). */ function unflushedValue(e) {
|
|
1116
|
-
if (globalQueue.q || e.
|
|
1136
|
+
if (globalQueue.q || e.ie === NOT_PENDING || e.l & CONFIG_PROMOTED) return NOT_PENDING;
|
|
1117
1137
|
// A held node (L2) carries a staged value between flushes too, but it was
|
|
1118
1138
|
// flushed — and parked. Not an unflushed write — unless rewritten since:
|
|
1119
1139
|
// then the flushed staging is what the world sees until the next flush.
|
|
1120
1140
|
if (e.l & CONFIG_HELD) return e.T.Ft === clock ? e.T.Gt : NOT_PENDING;
|
|
1121
|
-
return e.
|
|
1141
|
+
return e.le;
|
|
1122
1142
|
}
|
|
1123
1143
|
|
|
1124
1144
|
/** A28: a held node rewritten outside a flush keeps the staging the last
|
|
@@ -1126,7 +1146,7 @@ function markUnflushedStaged() {
|
|
|
1126
1146
|
* stash is stamped with the clock, which that flush advances. */ function stashFlushed(e) {
|
|
1127
1147
|
const t = e.T;
|
|
1128
1148
|
if (t.Ft !== clock) {
|
|
1129
|
-
t.Gt = e.
|
|
1149
|
+
t.Gt = e.ie;
|
|
1130
1150
|
t.Ft = clock;
|
|
1131
1151
|
unflushedStaged = true;
|
|
1132
1152
|
}
|
|
@@ -1155,7 +1175,7 @@ function resyncUnflushedCompanions() {
|
|
|
1155
1175
|
/** A tracked read's pull of a computed: bring it current when the flush has
|
|
1156
1176
|
* reached its height (or the reader is a fresh-pull waiter), and sit the
|
|
1157
1177
|
* reader above it. */ function pullComputed(e, t) {
|
|
1158
|
-
if (e.Et >= dirtyQueue.
|
|
1178
|
+
if (e.Et >= dirtyQueue.lt) {
|
|
1159
1179
|
markNode(t);
|
|
1160
1180
|
markHeap(dirtyQueue);
|
|
1161
1181
|
updateIfNecessary(e);
|
|
@@ -1174,7 +1194,7 @@ function resyncUnflushedCompanions() {
|
|
|
1174
1194
|
|
|
1175
1195
|
function read(e) {
|
|
1176
1196
|
let t = context;
|
|
1177
|
-
if (t?.Pt) t = t.
|
|
1197
|
+
if (t?.Pt) t = t.ht;
|
|
1178
1198
|
const n = e;
|
|
1179
1199
|
// CARVE 1: `owner` was `el._firewall || el` — a store leaf's status,
|
|
1180
1200
|
// height and error were read off its projection computed. (Typed loosely
|
|
@@ -1185,7 +1205,7 @@ function read(e) {
|
|
|
1185
1205
|
// The verdict windows (`latest`, `isPending`; verdict.ts): one cold path,
|
|
1186
1206
|
// off this one, present only while a window is open.
|
|
1187
1207
|
if (verdict !== null) return verdict(e, t);
|
|
1188
|
-
if (!n.X && e.T?.
|
|
1208
|
+
if (!n.X && e.T?.st === undefined && !snapshotCaptureActive && (!unflushedStaged || e.ie === NOT_PENDING) && // A28, see readNodeFast
|
|
1189
1209
|
!(e.l & CONFIG_OVERRIDE) && // lanes: the slow path's one arm
|
|
1190
1210
|
true) {
|
|
1191
1211
|
if (t) {
|
|
@@ -1201,18 +1221,18 @@ function read(e) {
|
|
|
1201
1221
|
// future (REACTIVE_JOINED): a first pass that did is born held (A29),
|
|
1202
1222
|
// wherever it was created. Lane work sees the screen (`frameRead`).
|
|
1203
1223
|
if (e.l & CONFIG_HELD && !(t.l & CONFIG_CHILDREN_FORBIDDEN)) {
|
|
1204
|
-
if (frameRead(t, e)) return e.
|
|
1224
|
+
if (frameRead(t, e)) return e.le;
|
|
1205
1225
|
joinPass(t, e);
|
|
1206
1226
|
}
|
|
1207
1227
|
}
|
|
1208
1228
|
// Committed visibility for untracked and children-forbidden readers.
|
|
1209
|
-
if (!t || e.
|
|
1229
|
+
if (!t || e.ie === NOT_PENDING || t.l & CONFIG_CHILDREN_FORBIDDEN) return e.le;
|
|
1210
1230
|
// (A plain signal is never uninitialized — the carve-out is `serve`'s.)
|
|
1211
1231
|
// Lane work over a folded lane (D-34), once the frame has a
|
|
1212
1232
|
// transaction, reads the screen too.
|
|
1213
|
-
if ((observeFlightHook && t.l & CONFIG_VERDICT || laneStageHook && passLane?.folded) && stagedScreen(t)) return e.
|
|
1233
|
+
if ((observeFlightHook && t.l & CONFIG_VERDICT || laneStageHook && passLane?.folded) && stagedScreen(t)) return e.le;
|
|
1214
1234
|
stagedRead(t);
|
|
1215
|
-
return e.
|
|
1235
|
+
return e.ie;
|
|
1216
1236
|
}
|
|
1217
1237
|
// Rule 3: this read serves the committed value to a render effect outside a
|
|
1218
1238
|
// parking flush (decided after the pull below — a pull can join).
|
|
@@ -1245,6 +1265,19 @@ function read(e) {
|
|
|
1245
1265
|
const n = laneReadHook(t, e);
|
|
1246
1266
|
if (n !== NOT_PENDING) return n;
|
|
1247
1267
|
}
|
|
1268
|
+
// A reader in a snapshot scope (a hydration claim) is served the value the
|
|
1269
|
+
// server rendered with: a client write's recompute still in flight does
|
|
1270
|
+
// not pull the claim off the markup it adopts — the reader replays at the
|
|
1271
|
+
// scope's release. An errored node throws below as for any reader.
|
|
1272
|
+
if (snapshotCaptureActive && t && t.l & CONFIG_IN_SNAPSHOT_SCOPE) {
|
|
1273
|
+
const n = e.T?.st;
|
|
1274
|
+
if (n !== undefined && !(o.m & STATUS_ERROR)) {
|
|
1275
|
+
const o = n === NO_SNAPSHOT ? undefined : n;
|
|
1276
|
+
const s = e.ie !== NOT_PENDING ? e.ie : e.le;
|
|
1277
|
+
if (s !== o) t.R |= REACTIVE_SNAPSHOT_STALE;
|
|
1278
|
+
return o;
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1248
1281
|
// A stale reader of a held flight (rule 3, A15's reveal corollary) is served
|
|
1249
1282
|
// the committed value — coherent with the frame, whose inputs are the
|
|
1250
1283
|
// committed ones too — and does not go pending on it. So is lane work
|
|
@@ -1289,25 +1322,16 @@ function read(e) {
|
|
|
1289
1322
|
// Only a genuine reactive re-read may retry an errored async source:
|
|
1290
1323
|
// - tracking: owned/tracked scope only (never events / `untrack` / effect side-effect phase)
|
|
1291
1324
|
// - owner._time < clock: only on a later cycle than the one the error was found
|
|
1292
|
-
if (tracking && o.
|
|
1325
|
+
if (tracking && o.ae < clock) {
|
|
1293
1326
|
recompute(o);
|
|
1294
1327
|
return read(e);
|
|
1295
1328
|
} else throw o.T?.F;
|
|
1296
1329
|
}
|
|
1297
|
-
if (
|
|
1298
|
-
const n = e.T?.lt;
|
|
1299
|
-
if (n !== undefined) {
|
|
1300
|
-
const o = n === NO_SNAPSHOT ? undefined : n;
|
|
1301
|
-
const s = e.ue !== NOT_PENDING ? e.ue : e.Ie;
|
|
1302
|
-
if (s !== o) t.R |= REACTIVE_SNAPSHOT_STALE;
|
|
1303
|
-
return o;
|
|
1304
|
-
}
|
|
1305
|
-
}
|
|
1306
|
-
if (s) return e.Ie;
|
|
1330
|
+
if (s) return e.le;
|
|
1307
1331
|
// D-41: an untracked read reads the screen, and a shown error is on it
|
|
1308
1332
|
// until the answer that replaces it commits.
|
|
1309
|
-
if (!t && e.T?.
|
|
1310
|
-
const
|
|
1333
|
+
if (!t && e.T?.Te) throw e.T.Te;
|
|
1334
|
+
const l = serve(e, t);
|
|
1311
1335
|
if (!t && typeof n.X === "function" && e.l & CONFIG_AUTO_DISPOSE && !(o.m & STATUS_PENDING) && !e.o) {
|
|
1312
1336
|
// Deferred, not inline (#3078): an inline unobserved() here made untracked
|
|
1313
1337
|
// reads destructive — dispose on this read, full revival recompute on the
|
|
@@ -1318,7 +1342,7 @@ function read(e) {
|
|
|
1318
1342
|
dormantNodes.add(e);
|
|
1319
1343
|
schedule();
|
|
1320
1344
|
}
|
|
1321
|
-
return
|
|
1345
|
+
return l;
|
|
1322
1346
|
}
|
|
1323
1347
|
|
|
1324
1348
|
/**
|
|
@@ -1335,18 +1359,18 @@ function read(e) {
|
|
|
1335
1359
|
// an untracked reader has nothing to serve and holds (A19 exception 1) — a
|
|
1336
1360
|
// bookkeeping read (`spectate`) likewise, and a children-forbidden reader
|
|
1337
1361
|
// (A32: the frame, which has nothing here).
|
|
1338
|
-
if (e.
|
|
1362
|
+
if (e.ie !== NOT_PENDING && e.m & STATUS_UNINITIALIZED && (!t || spectating || t.l & CONFIG_CHILDREN_FORBIDDEN)) throw new NotReadyError(null);
|
|
1339
1363
|
const n = t && unflushedStaged ? unflushedValue(e) : NOT_PENDING;
|
|
1340
1364
|
if (n !== NOT_PENDING) {
|
|
1341
1365
|
markLateLinker(t);
|
|
1342
1366
|
return n;
|
|
1343
1367
|
}
|
|
1344
|
-
if (readerSeesCommitted(e, t)) return e.
|
|
1345
|
-
if (observeFlightHook && t.l & CONFIG_VERDICT && !(e.m & STATUS_UNINITIALIZED) && stagedScreen(t)) return e.
|
|
1368
|
+
if (readerSeesCommitted(e, t)) return e.le;
|
|
1369
|
+
if (observeFlightHook && t.l & CONFIG_VERDICT && !(e.m & STATUS_UNINITIALIZED) && stagedScreen(t)) return e.le;
|
|
1346
1370
|
stagedRead(t, e);
|
|
1347
1371
|
// D-41: the staged answer is an error (`stageError`).
|
|
1348
|
-
if (e.l & CONFIG_STAGED_ERROR) throw e.
|
|
1349
|
-
return e.
|
|
1372
|
+
if (e.l & CONFIG_STAGED_ERROR) throw e.ie;
|
|
1373
|
+
return e.ie;
|
|
1350
1374
|
}
|
|
1351
1375
|
|
|
1352
1376
|
/** A pass read a staging of this flush: it derives from what the flush may
|
|
@@ -1400,8 +1424,12 @@ function read(e) {
|
|
|
1400
1424
|
* REVEALS — the creation-time arm skips pending computeds for the same
|
|
1401
1425
|
* reason. Firewall leaves belong to a projection whose compute is the tree's
|
|
1402
1426
|
* own work, captured (or deliberately not) at creation. */ function captureWriteSnapshot(e, t) {
|
|
1403
|
-
if (e.l & CONFIG_NO_SNAPSHOT || e.X !== undefined || e.T?.
|
|
1404
|
-
|
|
1427
|
+
if (e.l & CONFIG_NO_SNAPSHOT || e.X !== undefined || e.T?.st !== undefined) return;
|
|
1428
|
+
takeSnapshot(e, t);
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
function takeSnapshot(e, t) {
|
|
1432
|
+
ext(e).st = t === undefined ? NO_SNAPSHOT : t;
|
|
1405
1433
|
e.l |= CONFIG_HAS_SNAPSHOT;
|
|
1406
1434
|
snapshotSources.add(e);
|
|
1407
1435
|
}
|
|
@@ -1431,9 +1459,9 @@ function read(e) {
|
|
|
1431
1459
|
* unwinding a flush or a setter; answered "same" so nothing commits.
|
|
1432
1460
|
* @internal
|
|
1433
1461
|
*/ function isSame(e, t, n) {
|
|
1434
|
-
if (e.
|
|
1462
|
+
if (e.je === isEqual) return t === n;
|
|
1435
1463
|
try {
|
|
1436
|
-
return e.
|
|
1464
|
+
return e.je && e.je(t, n);
|
|
1437
1465
|
} catch (t) {
|
|
1438
1466
|
if (!(t instanceof NotReadyError)) return notifyStatus(e, STATUS_ERROR, t), true;
|
|
1439
1467
|
}
|
|
@@ -1441,7 +1469,7 @@ function read(e) {
|
|
|
1441
1469
|
|
|
1442
1470
|
function setSignal(e, t) {
|
|
1443
1471
|
if (writeHook !== null) writeHook(e);
|
|
1444
|
-
const n = e.
|
|
1472
|
+
const n = e.ie === NOT_PENDING ? e.le : e.ie;
|
|
1445
1473
|
if (typeof t === "function") t = t(n);
|
|
1446
1474
|
// Lanes: a write landing on a guess is its truth (A18 — an async guess's
|
|
1447
1475
|
// own landing), judged against the guess; one landing on a lane's
|
|
@@ -1456,7 +1484,7 @@ function setSignal(e, t) {
|
|
|
1456
1484
|
// user comparator must not run against `undefined` (matches recompute).
|
|
1457
1485
|
// D-41: a write over a shown error is a change whatever the last good
|
|
1458
1486
|
// value was — the error leaves at its commit (`commitPendingNode`).
|
|
1459
|
-
const o = !!(e.m & STATUS_UNINITIALIZED) || !!e.T?.
|
|
1487
|
+
const o = !!(e.m & STATUS_UNINITIALIZED) || !!e.T?.Te || !isSame(e, n, t);
|
|
1460
1488
|
if (!o) return t;
|
|
1461
1489
|
// Attribution hook: this committed write is where a re-run chain begins.
|
|
1462
1490
|
if (attrHooks !== null) attrHooks.write(e, n, t);
|
|
@@ -1470,16 +1498,16 @@ function setSignal(e, t) {
|
|
|
1470
1498
|
// pass reads a value the server never had. Store leaves written here are
|
|
1471
1499
|
// plain signals and qualify the same way.
|
|
1472
1500
|
if (snapshotCaptureActive) captureWriteSnapshot(e, n);
|
|
1473
|
-
const s = e.
|
|
1501
|
+
const s = e.ie !== NOT_PENDING;
|
|
1474
1502
|
if (!s) queuePendingNode(e);
|
|
1475
1503
|
// A28: a held node rewritten outside a flush keeps its flushed staging
|
|
1476
1504
|
// for every reader until the flush that carries the rewrite.
|
|
1477
1505
|
else if (e.l & CONFIG_HELD && !globalQueue.q) stashFlushed(e);
|
|
1478
|
-
e.
|
|
1506
|
+
e.ie = t;
|
|
1479
1507
|
if (context !== null) notePromotedWrite(e);
|
|
1480
1508
|
// _time is a computed-only slot (§12e): writing it on a signal would fork
|
|
1481
1509
|
// the lean shape. Every read site is computed-typed.
|
|
1482
|
-
if (e.X !== undefined) e.
|
|
1510
|
+
if (e.X !== undefined) e.ae = clock;
|
|
1483
1511
|
// Staged-rewrite fast path (§12d): a re-write to a node whose subscribers
|
|
1484
1512
|
// were already walked — and where nothing has recomputed or linked since
|
|
1485
1513
|
// (epoch) — re-stages the value and stops. The walk is idempotent (subs
|
|
@@ -1512,7 +1540,7 @@ function setSignal(e, t) {
|
|
|
1512
1540
|
const n = e.l & CONFIG_HELD && !(e.R & REACTIVE_MANUAL_WRITE) && txOf(e) !== flushTransaction;
|
|
1513
1541
|
// The writer read the committed frame; its updater composes on it.
|
|
1514
1542
|
if (n && typeof t === "function") {
|
|
1515
|
-
const n = t(e.
|
|
1543
|
+
const n = t(e.le);
|
|
1516
1544
|
t = () => n;
|
|
1517
1545
|
}
|
|
1518
1546
|
// #4110: the write replaces a failed derivation's outcome. It lands without
|
|
@@ -1520,7 +1548,7 @@ function setSignal(e, t) {
|
|
|
1520
1548
|
// every later read keeps throwing the error the derivation left behind.
|
|
1521
1549
|
// The error stays the display until the write commits (D-41).
|
|
1522
1550
|
if (e.m & STATUS_ERROR) {
|
|
1523
|
-
e.T.
|
|
1551
|
+
e.T.Te = e.T.F;
|
|
1524
1552
|
notifyStatus(e, STATUS_NONE, undefined);
|
|
1525
1553
|
}
|
|
1526
1554
|
t = setSignal(e, t);
|
|
@@ -1532,7 +1560,7 @@ function setSignal(e, t) {
|
|
|
1532
1560
|
e.R = e.R & ~REACTIVE_CHECK | REACTIVE_DIRTY;
|
|
1533
1561
|
insertIntoHeap(e, dirtyQueue);
|
|
1534
1562
|
schedule();
|
|
1535
|
-
} else if (e.
|
|
1563
|
+
} else if (e.ie !== NOT_PENDING) e.R |= REACTIVE_MANUAL_WRITE;
|
|
1536
1564
|
return t;
|
|
1537
1565
|
}
|
|
1538
1566
|
|