@solidjs/signals 2.0.0-rc.14 → 2.0.0-rc.15
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 +248 -189
- package/dist/dev.attribution.js +124 -41
- package/dist/dev.js +553 -322
- package/dist/observe/affects.js +34 -31
- package/dist/observe/boundaries.js +166 -132
- package/dist/observe/core/async.js +90 -83
- package/dist/observe/core/attribution-queries.js +1 -1
- package/dist/observe/core/attribution.js +281 -197
- package/dist/observe/core/constants.js +7 -2
- package/dist/observe/core/context.js +3 -3
- package/dist/observe/core/core.js +277 -245
- package/dist/observe/core/dev.js +3 -3
- package/dist/observe/core/effect.js +21 -21
- package/dist/observe/core/external.js +2 -2
- package/dist/observe/core/graph.js +25 -25
- package/dist/observe/core/heap.js +9 -9
- package/dist/observe/core/lanes.js +206 -160
- package/dist/observe/core/owner.js +27 -27
- package/dist/observe/core/scheduler.js +92 -86
- package/dist/observe/core/verdict.js +66 -54
- package/dist/observe/map.js +35 -35
- package/dist/observe/reveal.js +24 -24
- package/dist/observe/signals.js +38 -28
- package/dist/observe/store/affects.js +75 -68
- package/dist/observe/store/hooks.js +13 -0
- package/dist/observe/store/index.js +0 -2
- package/dist/observe/store/optimistic.js +67 -34
- package/dist/observe/store/projection.js +37 -31
- package/dist/observe/store/reconcile.js +32 -30
- package/dist/observe/store/store.js +128 -85
- package/dist/prod/affects.js +34 -31
- package/dist/prod/boundaries.js +143 -109
- package/dist/prod/core/async.js +64 -67
- package/dist/prod/core/constants.js +7 -2
- package/dist/prod/core/context.js +3 -3
- package/dist/prod/core/core.js +302 -270
- package/dist/prod/core/effect.js +38 -38
- package/dist/prod/core/external.js +4 -4
- package/dist/prod/core/graph.js +27 -27
- package/dist/prod/core/heap.js +30 -30
- package/dist/prod/core/lanes.js +257 -211
- package/dist/prod/core/owner.js +39 -39
- package/dist/prod/core/scheduler.js +125 -119
- package/dist/prod/core/verdict.js +70 -58
- package/dist/prod/map.js +73 -73
- package/dist/prod/reveal.js +38 -38
- package/dist/prod/signals.js +30 -20
- package/dist/prod/store/affects.js +75 -68
- package/dist/prod/store/hooks.js +13 -0
- package/dist/prod/store/index.js +0 -2
- package/dist/prod/store/optimistic.js +114 -81
- package/dist/prod/store/projection.js +30 -24
- package/dist/prod/store/reconcile.js +32 -30
- package/dist/prod/store/store.js +136 -93
- package/dist/types/boundaries.d.ts +4 -0
- package/dist/types/core/attribution.d.ts +6 -2
- package/dist/types/core/constants.d.ts +5 -0
- package/dist/types/core/core.d.ts +33 -3
- package/dist/types/core/lanes.d.ts +10 -0
- package/dist/types/core/scheduler.d.ts +12 -6
- package/dist/types/core/types.d.ts +0 -6
- package/dist/types/store/affects.d.ts +11 -0
- package/dist/types/store/hooks.d.ts +48 -0
- package/dist/types/store/index.d.ts +0 -1
- package/dist/types/store/store.d.ts +3 -38
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { releaseFlightTeardown, handleAsync, clearStatus, parkLoadingWindow, notifyStatus, settlePendingSource, 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, NO_SNAPSHOT, CONFIG_HAS_SNAPSHOT, CONFIG_HELD, REACTIVE_MANUAL_WRITE, REACTIVE_DIRTY, CONFIG_OVERRIDE, REACTIVE_DISPOSED, REACTIVE_CHECK, REACTIVE_IN_HEAP, REACTIVE_REASK, REACTIVE_SNAPSHOT_STALE, REACTIVE_LANE_DIRTY, CONFIG_GUESS, REACTIVE_RECOMPUTING_DEPS,
|
|
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, NO_SNAPSHOT, CONFIG_HAS_SNAPSHOT, CONFIG_HELD, REACTIVE_MANUAL_WRITE, REACTIVE_DIRTY, CONFIG_OVERRIDE, REACTIVE_DISPOSED, REACTIVE_CHECK, REACTIVE_IN_HEAP, REACTIVE_REASK, REACTIVE_SNAPSHOT_STALE, REACTIVE_LANE_DIRTY, CONFIG_GUESS, REACTIVE_RECOMPUTING_DEPS, STATUS_PENDING, CONFIG_VERDICT, EFFECT_RENDER, CONFIG_HELD_FRAME, EFFECT_TRACKED, CONFIG_STAGED, STATUS_ERROR, REACTIVE_ZOMBIE, REACTIVE_MISSED_WAKE, REACTIVE_JOINED, REACTIVE_PROBED, REACTIVE_FRAME_READ, REACTIVE_STAGED_READ, REACTIVE_LANE_READ, REACTIVE_SCREEN_READ, REACTIVE_PROBE_UNANSWERED, CONFIG_CHILDREN_FORBIDDEN, CONFIG_PROMOTED, CONFIG_INPUTS_PUBLISHED, CONFIG_FRESH_READ, REACTIVE_IN_HEAP_HEIGHT, CONFIG_SLOT_NODE, EFFECT_USER } from "./constants.js";
|
|
4
4
|
|
|
5
5
|
import { NotReadyError } from "./error.js";
|
|
6
6
|
|
|
@@ -25,10 +25,10 @@ import { schedule, GlobalQueue, clock, txOf, flushTransaction, joinFuture, queue
|
|
|
25
25
|
// directly into the user queue ran in the SAME pass, read the old value, and
|
|
26
26
|
// nothing re-notified it when the value landed (#3291).
|
|
27
27
|
GlobalQueue.et = e => {
|
|
28
|
-
if (e.
|
|
28
|
+
if (e.u === EFFECT_TRACKED) {
|
|
29
29
|
deleteFromHeap(e, dirtyQueue);
|
|
30
30
|
e.lt = true;
|
|
31
|
-
globalQueue.enqueue(EFFECT_USER, e.
|
|
31
|
+
globalQueue.enqueue(EFFECT_USER, e.M);
|
|
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
|
|
@@ -39,7 +39,7 @@ GlobalQueue.et = e => {
|
|
|
39
39
|
else if (e.R & REACTIVE_ZOMBIE && !(e.R & REACTIVE_LANE_DIRTY)) deferZombie(e); else recompute(e);
|
|
40
40
|
};
|
|
41
41
|
|
|
42
|
-
GlobalQueue.
|
|
42
|
+
GlobalQueue.Et = disposeChildren;
|
|
43
43
|
|
|
44
44
|
let tracking = false;
|
|
45
45
|
|
|
@@ -51,7 +51,7 @@ let snapshotSources = null;
|
|
|
51
51
|
|
|
52
52
|
function ownerInSnapshotScope(e) {
|
|
53
53
|
while (e) {
|
|
54
|
-
if (e.
|
|
54
|
+
if (e.st) return true;
|
|
55
55
|
e = e._parent;
|
|
56
56
|
}
|
|
57
57
|
return false;
|
|
@@ -63,25 +63,25 @@ function setSnapshotCapture(e) {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
function markSnapshotScope(e) {
|
|
66
|
-
e.
|
|
66
|
+
e.st = true;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
function releaseSnapshotScope(e) {
|
|
70
|
-
e.
|
|
70
|
+
e.st = false;
|
|
71
71
|
releaseSubtree(e);
|
|
72
72
|
schedule();
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
function releaseSubtree(e) {
|
|
76
|
-
let t = e.
|
|
76
|
+
let t = e.nn;
|
|
77
77
|
while (t) {
|
|
78
|
-
if (t.
|
|
79
|
-
t = t.
|
|
78
|
+
if (t.st) {
|
|
79
|
+
t = t.tn;
|
|
80
80
|
continue;
|
|
81
81
|
}
|
|
82
|
-
if (t.
|
|
82
|
+
if (t.ee) {
|
|
83
83
|
const e = t;
|
|
84
|
-
e.
|
|
84
|
+
e.i &= ~CONFIG_IN_SNAPSHOT_SCOPE;
|
|
85
85
|
if (e.R & REACTIVE_SNAPSHOT_STALE) {
|
|
86
86
|
e.R &= ~REACTIVE_SNAPSHOT_STALE;
|
|
87
87
|
e.R |= REACTIVE_DIRTY;
|
|
@@ -90,7 +90,7 @@ function releaseSubtree(e) {
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
releaseSubtree(t);
|
|
93
|
-
t = t.
|
|
93
|
+
t = t.tn;
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
|
|
@@ -112,7 +112,7 @@ function clearSnapshots() {
|
|
|
112
112
|
function recompute(e, t = false) {
|
|
113
113
|
// §12d: any recompute can clean a marked subscriber — invalidate skips.
|
|
114
114
|
bumpNotifyEpoch();
|
|
115
|
-
const n = e.
|
|
115
|
+
const n = e.u;
|
|
116
116
|
// Lanes (plan sec. 28): the seat of a pass is its node's. A node carrying a lane's
|
|
117
117
|
// derived value runs as the lane's whoever dirtied it — a sync write, a
|
|
118
118
|
// boundary reset, a frame rerun — so its children and its result are the
|
|
@@ -129,14 +129,14 @@ function recompute(e, t = false) {
|
|
|
129
129
|
// Restored at the end, after this pass's staging and runs have been
|
|
130
130
|
// routed.
|
|
131
131
|
const l = passLane;
|
|
132
|
-
setPassLane((e.R & REACTIVE_LANE_DIRTY || (e.
|
|
132
|
+
setPassLane((e.R & REACTIVE_LANE_DIRTY || (e.i & (CONFIG_OVERRIDE | CONFIG_GUESS)) === CONFIG_OVERRIDE) && e.m?._t?.B ? e.m._t : // Loose `!=`: false with no creator lane (undefined == null).
|
|
133
133
|
t && l?._parent.rt != l && (creatorPass(context)?.R ?? 0) & REACTIVE_RECOMPUTING_DEPS ? l : null);
|
|
134
134
|
// Attribution hook: fired before this run touches the dep list — `_deps`
|
|
135
135
|
// still holds the previous run's links (the subscriptions that could have
|
|
136
136
|
// triggered this run, and the baseline for the engine's subscription diff).
|
|
137
|
-
let
|
|
137
|
+
let E = false;
|
|
138
138
|
{
|
|
139
|
-
e.
|
|
139
|
+
e.Ze = 0;
|
|
140
140
|
if (attrHooks !== null) attrHooks.recomputeStart(e, t);
|
|
141
141
|
}
|
|
142
142
|
// CARVE 2: lane posture resolution (OPTIMISTIC_DIRTY, derived-override
|
|
@@ -144,7 +144,7 @@ function recompute(e, t = false) {
|
|
|
144
144
|
if (!t) {
|
|
145
145
|
deleteFromHeap(e, dirtyQueue);
|
|
146
146
|
if (e.m !== null) {
|
|
147
|
-
e.m.
|
|
147
|
+
e.m.Te = null;
|
|
148
148
|
// Supersede is where an iterator flight dies (#3122): close it now.
|
|
149
149
|
// Idempotent with the cleanup-channel close.
|
|
150
150
|
releaseFlightTeardown(e);
|
|
@@ -156,23 +156,30 @@ function recompute(e, t = false) {
|
|
|
156
156
|
// joins all the same. Not a render effect (rule 3): it is the frame.
|
|
157
157
|
// Re-run outside its transaction's flush it publishes mainline — the
|
|
158
158
|
// value it staged there is superseded (`frameRead` remembers it for the
|
|
159
|
-
// landing). A frame born into the transaction (
|
|
160
|
-
// A29
|
|
161
|
-
// swap (ruling
|
|
162
|
-
|
|
159
|
+
// landing). A frame born into the transaction (`bornInto`: never run,
|
|
160
|
+
// A29, or mounted by a pass of it still awaiting its commit, #4010) keeps
|
|
161
|
+
// staging: live writes keep it current, it effects at the swap (ruling
|
|
162
|
+
// A).
|
|
163
|
+
// A pending flight is the transition's own work (#3884). A probe on an
|
|
164
|
+
// earlier pass left CONFIG_VERDICT, and a frame read here would serve the
|
|
165
|
+
// committed screen, settle the flight and drop the hold — a superseding
|
|
166
|
+
// write's verdict would depend on that probe. Cleared for this pass; a
|
|
167
|
+
// probe in the body re-stamps it after the plain reads.
|
|
168
|
+
if (e.S & STATUS_PENDING) e.i &= ~CONFIG_VERDICT;
|
|
169
|
+
if (e.i & CONFIG_HELD) {
|
|
163
170
|
const t = txOf(e);
|
|
164
171
|
// Held by a blocked lane: the frame joins nothing — a lane never holds
|
|
165
172
|
// a sync write (#3460). The pass is the lane's if it reads the lane's
|
|
166
173
|
// world (`read`), and has left it otherwise.
|
|
167
|
-
if (t.
|
|
174
|
+
if (t.B) ; else if (n !== EFFECT_RENDER && !(e.i & CONFIG_VERDICT)) joinPassTx(t); else if (t !== flushTransaction && !(e.S & STATUS_UNINITIALIZED || e.i & CONFIG_HELD_FRAME)) {
|
|
168
175
|
// Published mainline, it is not held: the frame this pass builds is
|
|
169
176
|
// replaced on the spot (#3404) — this flush's commit retires the
|
|
170
177
|
// committed frame the held pass had parked (`commitPendingNode`),
|
|
171
178
|
// after the new frame's first runs, and the held pass's own
|
|
172
179
|
// children die below (`disposeChildren`). The landing re-derives a
|
|
173
180
|
// fresh held frame for the reveal (`_reruns`).
|
|
174
|
-
e.
|
|
175
|
-
if (n === EFFECT_RENDER) e.
|
|
181
|
+
e.Oe = NOT_PENDING;
|
|
182
|
+
if (n === EFFECT_RENDER) e.i &= ~CONFIG_HELD;
|
|
176
183
|
}
|
|
177
184
|
}
|
|
178
185
|
// The previous pass's frame. If that pass is still awaiting its commit
|
|
@@ -180,14 +187,14 @@ function recompute(e, t = false) {
|
|
|
180
187
|
// frame parked before it stays parked. Otherwise they ARE the frame:
|
|
181
188
|
// park them until this pass commits. Tracked effects run after the seam
|
|
182
189
|
// and own nothing to park.
|
|
183
|
-
if (n === EFFECT_TRACKED || e.
|
|
190
|
+
if (n === EFFECT_TRACKED || e.i & CONFIG_STAGED) disposeChildren(e); else if (e.nn !== null || e.Re !== null) parkChildren(e);
|
|
184
191
|
}
|
|
185
|
-
const
|
|
192
|
+
const s = !!(e.S & STATUS_UNINITIALIZED);
|
|
186
193
|
// Capture both error and pending status before the compute clears them.
|
|
187
194
|
// A conditional can drop its pending source and recover to an unchanged
|
|
188
195
|
// value, leaving blocked dependents outside that source’s settle walk.
|
|
189
|
-
const u = e.
|
|
190
|
-
const i = (e.
|
|
196
|
+
const u = e.S & STATUS_ERROR ? e.m?.F : undefined;
|
|
197
|
+
const i = (e.S & STATUS_PENDING) !== 0;
|
|
191
198
|
const o = i ? e.m?.Se : undefined;
|
|
192
199
|
// Pending SOURCE-hood, captured before the compute clears status: a node
|
|
193
200
|
// whose own flight parked dependents self-registers in _pendingSources
|
|
@@ -199,20 +206,20 @@ function recompute(e, t = false) {
|
|
|
199
206
|
// A19 exc. 2: a `refresh()` re-asks the question already answered — quiet
|
|
200
207
|
// for the verdict — unless the question in flight is a new one, which the
|
|
201
208
|
// re-ask does not launder (a poll during a quiet confirm stays quiet).
|
|
202
|
-
const r = (e.R & REACTIVE_REASK) !== 0 && !(e.
|
|
209
|
+
const r = (e.R & REACTIVE_REASK) !== 0 && !(e.S & STATUS_PENDING && e.m !== null && !e.m.It);
|
|
203
210
|
const a = context;
|
|
204
211
|
context = e;
|
|
205
|
-
e.
|
|
206
|
-
e.
|
|
212
|
+
e.p = null;
|
|
213
|
+
e.pe++;
|
|
207
214
|
// REACTIVE_ZOMBIE is position: it says the node sits on its owner's parked
|
|
208
215
|
// frame, and the splice/reawaken paths key off it. A zombie reruns for
|
|
209
216
|
// live writes until the commit that disposes it, so every per-pass wipe
|
|
210
217
|
// — here, the finally below, updateIfNecessary — carries it (#3543).
|
|
211
218
|
e.R = REACTIVE_RECOMPUTING_DEPS | e.R & REACTIVE_ZOMBIE;
|
|
212
|
-
e.
|
|
213
|
-
// The pass's previous value: its staging, else
|
|
214
|
-
//
|
|
215
|
-
let I = e.
|
|
219
|
+
e.Ie = clock;
|
|
220
|
+
// The pass's previous value: its staging, else a lane node's lane value
|
|
221
|
+
// when it has one (lanes.ts), else the committed one.
|
|
222
|
+
let I = e.Oe !== NOT_PENDING ? e.Oe : e.i & CONFIG_OVERRIDE && e.m.B !== NOT_PENDING ? e.m.B : e.Ae;
|
|
216
223
|
let N = e.it;
|
|
217
224
|
let f = false;
|
|
218
225
|
// L2: did this pass read the future (REACTIVE_JOINED, set by read)?
|
|
@@ -220,35 +227,35 @@ function recompute(e, t = false) {
|
|
|
220
227
|
let S = tracking;
|
|
221
228
|
tracking = true;
|
|
222
229
|
try {
|
|
223
|
-
if (!false && e.
|
|
224
|
-
I = e.
|
|
225
|
-
if (e.m !== null) e.m.
|
|
226
|
-
e.
|
|
230
|
+
if (!false && e.i & CONFIG_SYNC) {
|
|
231
|
+
I = e.ee(I);
|
|
232
|
+
if (e.m !== null) e.m.Te = null;
|
|
233
|
+
e.De = false;
|
|
227
234
|
} else {
|
|
228
235
|
// A projection's body self-registers its flight through handleAsync
|
|
229
236
|
// (with the commit as setter): its undefined return is not a sync
|
|
230
237
|
// answer, and the outer handleAsync must not clobber the registration.
|
|
231
|
-
const t = e.m?.
|
|
232
|
-
const n = e.
|
|
238
|
+
const t = e.m?.Te;
|
|
239
|
+
const n = e.ee(I);
|
|
233
240
|
const l = typeof n === "object" && n !== null;
|
|
234
|
-
const
|
|
235
|
-
I =
|
|
236
|
-
if (!
|
|
237
|
-
if (e.m !== null) e.m.
|
|
241
|
+
const E = e.m?.Te !== t;
|
|
242
|
+
I = E || !l ? n : handleAsync(e, n);
|
|
243
|
+
if (!E && !l) {
|
|
244
|
+
if (e.m !== null) e.m.Te = null;
|
|
238
245
|
// A sync (non-object) return is the first real answer; async-shaped
|
|
239
246
|
// results clear inside handleAsync at their own landing points, and a
|
|
240
247
|
// self-registered flight clears when its own handleAsync lands.
|
|
241
|
-
e.
|
|
248
|
+
e.De = false;
|
|
242
249
|
}
|
|
243
250
|
}
|
|
244
251
|
// On a status-free node clearStatus is a guaranteed no-op: every field
|
|
245
252
|
// its body gates on is either _statusFlags or lives in the cold
|
|
246
253
|
// extension — no extension, no status to clear. (_x from an unrelated
|
|
247
254
|
// installer just makes clearStatus a cheap re-verified no-op.)
|
|
248
|
-
if (e.
|
|
255
|
+
if (e.S !== 0 || e.m !== null) clearStatus(e, t);
|
|
249
256
|
} catch (t) {
|
|
250
257
|
const n = t instanceof NotReadyError;
|
|
251
|
-
if (n && e.
|
|
258
|
+
if (n && e.De) {
|
|
252
259
|
// Loading window with an unready sync dependency: register for the
|
|
253
260
|
// source's settle (the settlePendingSource walk runs off
|
|
254
261
|
// _pendingSources + _blocked alone) but take NO read-visible pending
|
|
@@ -258,11 +265,11 @@ function recompute(e, t = false) {
|
|
|
258
265
|
// retry can actually run.
|
|
259
266
|
parkLoadingWindow(e, t);
|
|
260
267
|
} else {
|
|
261
|
-
if (n) ext(e).
|
|
268
|
+
if (n) ext(e).de = true;
|
|
262
269
|
notifyStatus(e, n ? STATUS_PENDING : STATUS_ERROR, t);
|
|
263
270
|
// The replacement source is fully propagated now. If no new flight
|
|
264
271
|
// re-owned self, retire the superseded flight and its dependent copies.
|
|
265
|
-
if (n && _ && !e.m?.
|
|
272
|
+
if (n && _ && !e.m?.Te) settlePendingSource(e);
|
|
266
273
|
// A re-park drops what the earlier pass carried (#3456): a source this
|
|
267
274
|
// pass no longer reaches — its branch switched, or a fresh flight
|
|
268
275
|
// replaced the inputs' pending with its own — stays copied onto
|
|
@@ -284,7 +291,7 @@ function recompute(e, t = false) {
|
|
|
284
291
|
T = (e.R & REACTIVE_JOINED) !== 0;
|
|
285
292
|
// A verdict reader is one for as long as it probes: a pass that entered
|
|
286
293
|
// no window (REACTIVE_PROBED) is an ordinary derivation again.
|
|
287
|
-
if (!(e.R & REACTIVE_PROBED)) e.
|
|
294
|
+
if (!(e.R & REACTIVE_PROBED)) e.i &= ~CONFIG_VERDICT;
|
|
288
295
|
// REACTIVE_DISPOSED survives too (#3621): the pass may have disposed its
|
|
289
296
|
// own owner (a memo calling its root's `dispose()`, a cleanup doing so
|
|
290
297
|
// #3601/#3606), and `disposeChildren` set the flag on this node
|
|
@@ -297,7 +304,7 @@ function recompute(e, t = false) {
|
|
|
297
304
|
// A19 exc. 2: a pass that went pending on a `refresh()` re-asks the
|
|
298
305
|
// question already answered — quiet for the verdict; any other pending
|
|
299
306
|
// pass is a new question. Settled, there is nothing to classify.
|
|
300
|
-
if (e.m !== null) e.m.It = r && (e.
|
|
307
|
+
if (e.m !== null) e.m.It = r && (e.S & STATUS_PENDING) !== 0;
|
|
301
308
|
}
|
|
302
309
|
// A node that died during its own pass (#3621) is dead at the end of it,
|
|
303
310
|
// and the pass is void. Its owner's teardown already unlinked its deps,
|
|
@@ -312,7 +319,7 @@ function recompute(e, t = false) {
|
|
|
312
319
|
// attribution frame opened at the top is still closed.
|
|
313
320
|
if (e.R & REACTIVE_DISPOSED) {
|
|
314
321
|
clearDeps(e);
|
|
315
|
-
if (e.m !== null) e.m.
|
|
322
|
+
if (e.m !== null) e.m.Te = null;
|
|
316
323
|
if (attrHooks !== null) attrHooks.recomputeEnd(e, t, false, false, false, false);
|
|
317
324
|
setPassLane(l);
|
|
318
325
|
return;
|
|
@@ -325,11 +332,11 @@ function recompute(e, t = false) {
|
|
|
325
332
|
let c = passLane;
|
|
326
333
|
// Listed before its staging, a pending pass included (the lane's own
|
|
327
334
|
// flight is the lane's); false: the pass left the lane (lanes.ts).
|
|
328
|
-
const A = !!e.m?.
|
|
335
|
+
const A = !!e.m?.F;
|
|
329
336
|
if (c !== null) {
|
|
330
|
-
if (GlobalQueue.
|
|
337
|
+
if (GlobalQueue.W(e, c, t, A)) setPassLane(c); else c = null;
|
|
331
338
|
}
|
|
332
|
-
if (!e.m?.
|
|
339
|
+
if (!e.m?.F) {
|
|
333
340
|
// Observe-tier fan-in (HUGE_FAN_IN): the validated prefix [_deps.._depsTail]
|
|
334
341
|
// IS this pass's distinct sources — count it here rather than per link. A
|
|
335
342
|
// begin/end bracket around the pass plus a per-link increment measured
|
|
@@ -340,9 +347,9 @@ function recompute(e, t = false) {
|
|
|
340
347
|
// this pass, or at its commit — see recompute's tail.)
|
|
341
348
|
{
|
|
342
349
|
let t = 0;
|
|
343
|
-
for (let n = e.
|
|
350
|
+
for (let n = e.G; n !== null; n = n._) {
|
|
344
351
|
t++;
|
|
345
|
-
if (n === e.
|
|
352
|
+
if (n === e.p) break;
|
|
346
353
|
}
|
|
347
354
|
if (t >= GRAPH_SIZE_WARN_AT) noteFanIn(e, t);
|
|
348
355
|
}
|
|
@@ -350,10 +357,10 @@ function recompute(e, t = false) {
|
|
|
350
357
|
// publishes to — the staged `_pendingValue` when staged; a lane node's
|
|
351
358
|
// lane value (a node entering the lane this pass has none yet — its
|
|
352
359
|
// committed value, `laneStage` having voided a frame staging).
|
|
353
|
-
const i = e.
|
|
360
|
+
const i = e.Oe !== NOT_PENDING ? e.Oe : e.i & CONFIG_OVERRIDE && e.m.B !== NOT_PENDING ? e.m.B : e.Ae;
|
|
354
361
|
let r = false;
|
|
355
362
|
try {
|
|
356
|
-
r = !n &&
|
|
363
|
+
r = !n && s || !e.en || !e.en(i, I);
|
|
357
364
|
} catch (t) {
|
|
358
365
|
// A throwing user comparator is an error of this node's computation.
|
|
359
366
|
// Route it through the same status path as a compute-phase throw so
|
|
@@ -364,8 +371,8 @@ function recompute(e, t = false) {
|
|
|
364
371
|
// A committed derived change becomes a cause for this node's subscribers,
|
|
365
372
|
// chaining their attribution through this node to the root write.
|
|
366
373
|
if (attrHooks !== null) {
|
|
367
|
-
|
|
368
|
-
if (
|
|
374
|
+
E = r && !e.m?.F;
|
|
375
|
+
if (E && !n && !t) attrHooks.derivedChanged(e);
|
|
369
376
|
}
|
|
370
377
|
// Effects use `_equals: false` (no per-effect closure). The side effects that
|
|
371
378
|
// the equals closure used to perform — flagging the effect dirty and enqueueing
|
|
@@ -377,14 +384,14 @@ function recompute(e, t = false) {
|
|
|
377
384
|
// last applied owes no run (lanes.ts).
|
|
378
385
|
!(e.R & REACTIVE_SCREEN_READ && !e.lt && Object.is(e.Nt, I)) &&
|
|
379
386
|
// A re-staging pass owes no run: the commit replays the effect (#3802).
|
|
380
|
-
e.
|
|
381
|
-
e.lt = !e.m?.
|
|
387
|
+
e.Oe === NOT_PENDING) {
|
|
388
|
+
e.lt = !e.m?.F;
|
|
382
389
|
// Reuse one bound runner per effect — runEffect no-ops on a stale
|
|
383
390
|
// `_modified`, so re-enqueueing the same function is harmless.
|
|
384
391
|
// Lane work's run is the lane's (released at its reveal) — a first
|
|
385
392
|
// pass under a lane too: `effect()` skips the synchronous first run of
|
|
386
393
|
// a lane's node.
|
|
387
|
-
if (!t || c !== null) globalQueue.enqueue(n, e.
|
|
394
|
+
if (!t || c !== null) globalQueue.enqueue(n, e.Y ??= GlobalQueue.q.bind(null, e), c);
|
|
388
395
|
}
|
|
389
396
|
// Lanes. A written guess's own pass from the frame: its source
|
|
390
397
|
// recomputed it — the truth (A18: "the source is whatever recomputes the
|
|
@@ -392,7 +399,7 @@ function recompute(e, t = false) {
|
|
|
392
399
|
// confirm is a landing too — the lane's end commits it) and notifies
|
|
393
400
|
// only a correction: an equal truth re-runs nothing (lanes.ts). (As the
|
|
394
401
|
// lane's own work the lane judged it — `laneStage`.)
|
|
395
|
-
if (!t && c === null && e.
|
|
402
|
+
if (!t && c === null && e.i & CONFIG_GUESS && GlobalQueue.ft(e, I, A)) ; else if (A) ; else if (r) {
|
|
396
403
|
// L2: a first pass is born held — staged, committed with the
|
|
397
404
|
// transaction, its effect's first run the landing's (A29; `effect()`
|
|
398
405
|
// skips the synchronous first run on a staged value) — when the flush
|
|
@@ -416,8 +423,8 @@ function recompute(e, t = false) {
|
|
|
416
423
|
// effect's value slot is private: the run the lane holds is what
|
|
417
424
|
// shows it — unless the effect was born held, whose first run is the
|
|
418
425
|
// commit's (A29): it re-stages, as below.
|
|
419
|
-
if (n && e.
|
|
420
|
-
} else if (t ? !((flushTransaction !== null || passTx !== null) && (T || l && e.R & REACTIVE_STAGED_READ || (creatorPass(a)?.R ?? 0) & REACTIVE_JOINED)) : n && e.
|
|
426
|
+
if (n && e.Oe === NOT_PENDING) e.Ae = I; else if (n || c.Tt) e.Oe = I; else e.m.B = I;
|
|
427
|
+
} else if (t ? !((flushTransaction !== null || passTx !== null) && (T || l && e.R & REACTIVE_STAGED_READ || (creatorPass(a)?.R ?? 0) & REACTIVE_JOINED)) : n && e.Oe === NOT_PENDING) {
|
|
421
428
|
// A first pass publishes directly. So does an effect: its value slot
|
|
422
429
|
// is private (only its own run reads it), and the staging round-trip
|
|
423
430
|
// (queuePendingNode + commitPendingNodes) paid per effect on the
|
|
@@ -428,9 +435,9 @@ function recompute(e, t = false) {
|
|
|
428
435
|
// sources commit) must see the fresh value while PLAIN reads stay
|
|
429
436
|
// committed until flush (#3009 purity). The pending round-trip is
|
|
430
437
|
// that separation; it cannot be skipped on any path a pull can reach.
|
|
431
|
-
e.pe = I;
|
|
432
|
-
} else {
|
|
433
438
|
e.Ae = I;
|
|
439
|
+
} else {
|
|
440
|
+
e.Oe = I;
|
|
434
441
|
// Born held: in the transaction from birth, so a read of it this
|
|
435
442
|
// tick is a read of its world (the mount-during-a-hold case), not
|
|
436
443
|
// only after the seam parks it.
|
|
@@ -440,7 +447,11 @@ function recompute(e, t = false) {
|
|
|
440
447
|
// (`commitPendingNode` initializes it) — every reader of it
|
|
441
448
|
// derives from the future (`read`), an untracked one throws
|
|
442
449
|
// (A19 exc. 1).
|
|
443
|
-
e.
|
|
450
|
+
e.S |= STATUS_UNINITIALIZED;
|
|
451
|
+
// A boundary that has not shown content catches it: its fallback
|
|
452
|
+
// waits for the commit (#3540). (The pass's own status clear above
|
|
453
|
+
// dropped what the read recorded.)
|
|
454
|
+
GlobalQueue.fe?.(e);
|
|
444
455
|
}
|
|
445
456
|
}
|
|
446
457
|
// insertSubs only walks _subs (no scheduling of its own), so a
|
|
@@ -451,14 +462,14 @@ function recompute(e, t = false) {
|
|
|
451
462
|
if (c !== null) laneDirty(e, c);
|
|
452
463
|
}
|
|
453
464
|
} else if (e.it != N) {
|
|
454
|
-
for (let t = e.t; t !== null; t = t.
|
|
465
|
+
for (let t = e.t; t !== null; t = t.o) insertIntoHeapHeight(t.l, dirtyQueue);
|
|
455
466
|
}
|
|
456
467
|
// Silent recovery: errored → unchanged value fires no notification, but
|
|
457
468
|
// dependents still holding the propagated error consumed their dirty flag
|
|
458
469
|
// in an errored run and may sit on stale commits (#2949). Changed-value
|
|
459
470
|
// recoveries ride insertSubs above; a comparator throw re-errored the node
|
|
460
471
|
// (el._x?._error re-set), so this only runs on a genuinely clean recovery.
|
|
461
|
-
if (!r && !e.m?.
|
|
472
|
+
if (!r && !e.m?.F) {
|
|
462
473
|
if (u !== undefined) settleErroredDependents(e, u);
|
|
463
474
|
// Self-registration (this node's own superseded flight) is the #3181
|
|
464
475
|
// sweep's business below — retiring it here too would walk twice.
|
|
@@ -467,14 +478,17 @@ function recompute(e, t = false) {
|
|
|
467
478
|
// #3181: a synchronous settle supersedes the old landing callback, so
|
|
468
479
|
// recompute owns its pending-source sweep. An uninitialized node without
|
|
469
480
|
// a replacement source still has no truth to reveal and must stay parked.
|
|
470
|
-
if (_ && !(e.
|
|
481
|
+
if (_ && !(e.S & (STATUS_PENDING | STATUS_UNINITIALIZED))) settlePendingSource(e);
|
|
471
482
|
}
|
|
472
483
|
// A first pass that went pending where the born-held arm would have held
|
|
473
484
|
// its value (it read a held node, or a verdict lane's mount read a staging,
|
|
474
|
-
// #3851) is
|
|
475
|
-
//
|
|
476
|
-
//
|
|
477
|
-
if (t && c === null && (T || l && e.R & REACTIVE_STAGED_READ) && e.
|
|
485
|
+
// #3851) is held like it: its answer is the hold's, and the hold waits
|
|
486
|
+
// for it (A29, #3800) — unless a boundary that has not shown content
|
|
487
|
+
// catches it: then it is the boundary's, and the hold does not wait (#3540).
|
|
488
|
+
if (t && c === null && (T || l && e.R & REACTIVE_STAGED_READ) && e.S & STATUS_PENDING) {
|
|
489
|
+
const t = flushTransaction ?? passTx;
|
|
490
|
+
if (t !== null && !t.B && !GlobalQueue.fe?.(e)) holdNode(e, t);
|
|
491
|
+
}
|
|
478
492
|
// Dependencies are the committed frame's until it is replaced (A30, #3410):
|
|
479
493
|
// a pass that staged its value leaves the previous pass's tail linked for
|
|
480
494
|
// `commitPendingNode` to trim, so a write to a dependency the committed
|
|
@@ -492,19 +506,19 @@ function recompute(e, t = false) {
|
|
|
492
506
|
// kept at a park — one spurious recompute at most). A creation pass and a
|
|
493
507
|
// tracked effect trim now: their frames are replaceable like a direct
|
|
494
508
|
// commit, and a tracked effect's spurious run would be user-visible.
|
|
495
|
-
if (!e.m?.
|
|
509
|
+
if (!e.m?.F && e.Oe === NOT_PENDING && !(n && e.lt)) {
|
|
496
510
|
if (t || n === EFFECT_TRACKED) trimStaleDeps(e); else heldTrim(e);
|
|
497
511
|
}
|
|
498
512
|
// Observe: the posture the run executed under — lane work (optimistic:
|
|
499
513
|
// overlay, never waste), a transaction's pass (held), or plain.
|
|
500
|
-
if (attrHooks !== null) attrHooks.recomputeEnd(e, t,
|
|
514
|
+
if (attrHooks !== null) attrHooks.recomputeEnd(e, t, E, c !== null, flushTransaction !== null || passTx !== null || (e.i & CONFIG_HELD) !== 0, e.Oe !== NOT_PENDING);
|
|
501
515
|
// A staged value, a parked frame (L2: the commit retires it), or status the
|
|
502
516
|
// commit sweep must settle (a pending or uninitialized pass), queues the
|
|
503
517
|
// node for this flush's commit. A first pass queues only when pending or
|
|
504
518
|
// born held: otherwise its value published directly. A queued pass's
|
|
505
519
|
// children are uncommitted (CONFIG_STAGED) until that commit.
|
|
506
|
-
if (e.
|
|
507
|
-
e.
|
|
520
|
+
if (e.Oe !== NOT_PENDING || e.m !== null && (e.m.St !== null || e.m.ct !== null) || (e.S & (STATUS_PENDING | STATUS_UNINITIALIZED)) !== 0 && (!t || (e.S & STATUS_PENDING) !== 0)) {
|
|
521
|
+
e.i |= CONFIG_STAGED;
|
|
508
522
|
// Lane work is the lane's to reveal (its seam), not this flush's commit.
|
|
509
523
|
if (c === null) queuePendingNode(e);
|
|
510
524
|
}
|
|
@@ -529,9 +543,9 @@ function updateIfNecessary(e) {
|
|
|
529
543
|
// would resurrect it (#2983) — readers serve its last value.
|
|
530
544
|
if (e.R & (REACTIVE_RECOMPUTING_DEPS | REACTIVE_DISPOSED)) return;
|
|
531
545
|
if (e.R & REACTIVE_CHECK) {
|
|
532
|
-
for (let t = e.
|
|
546
|
+
for (let t = e.G; t; t = t._) {
|
|
533
547
|
const n = t.I;
|
|
534
|
-
if (n.
|
|
548
|
+
if (n.ee) {
|
|
535
549
|
updateIfNecessary(n);
|
|
536
550
|
}
|
|
537
551
|
if (e.R & REACTIVE_DIRTY) {
|
|
@@ -539,7 +553,7 @@ function updateIfNecessary(e) {
|
|
|
539
553
|
}
|
|
540
554
|
}
|
|
541
555
|
}
|
|
542
|
-
if (e.R & REACTIVE_DIRTY || e.m?.
|
|
556
|
+
if (e.R & REACTIVE_DIRTY || e.m?.F && e.Ie < clock && !e.m?.Te) {
|
|
543
557
|
recompute(e);
|
|
544
558
|
}
|
|
545
559
|
// The guard above refused an already-disposed node; the recompute it just
|
|
@@ -567,47 +581,47 @@ function computed(e, t) {
|
|
|
567
581
|
// (measured: the whole of the observe tier's creation overhead). Keep the
|
|
568
582
|
// two in sync — the dist artifact test pins observe's key set to prod's
|
|
569
583
|
// plus `_name`.
|
|
570
|
-
const
|
|
584
|
+
const E = {
|
|
571
585
|
id: inheritId(t, n, context),
|
|
572
|
-
|
|
586
|
+
i: (n ? CONFIG_TRANSPARENT : 0) | (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (!context || t?.lazy ? CONFIG_AUTO_DISPOSE : 0) | (t?.sync ? CONFIG_SYNC : 0) | (t?.ae ? CONFIG_NO_SNAPSHOT : 0) | (
|
|
573
587
|
// Plumbing is an observe-tier notion (a name and records to
|
|
574
588
|
// withhold); the prod literal never carries the bit.
|
|
575
|
-
t?.At ? CONFIG_PLUMBING : 0) | (t?.
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
589
|
+
t?.At ? CONFIG_PLUMBING : 0) | (t?.Ct ?? 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
|
|
590
|
+
en: t?.equals ?? isEqual,
|
|
591
|
+
Re: null,
|
|
592
|
+
D: context?.D ?? defaultContext,
|
|
593
|
+
dt: 0,
|
|
594
|
+
ee: e,
|
|
595
|
+
Ae: l ? t.loadingValue : undefined,
|
|
582
596
|
it: 0,
|
|
583
597
|
Ot: undefined,
|
|
584
598
|
Rt: null,
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
599
|
+
G: null,
|
|
600
|
+
p: null,
|
|
601
|
+
pe: 0,
|
|
588
602
|
t: null,
|
|
589
603
|
Dt: null,
|
|
590
604
|
_parent: context,
|
|
591
|
-
|
|
605
|
+
tn: null,
|
|
592
606
|
Gt: null,
|
|
593
|
-
|
|
607
|
+
nn: null,
|
|
594
608
|
R: t?.lazy ? REACTIVE_LAZY : REACTIVE_NONE,
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
609
|
+
S: l ? 0 : STATUS_UNINITIALIZED,
|
|
610
|
+
Ie: clock,
|
|
611
|
+
Oe: NOT_PENDING,
|
|
598
612
|
Pt: -1,
|
|
599
|
-
|
|
613
|
+
De: l,
|
|
600
614
|
m: null,
|
|
601
615
|
// The slot is always present (hidden class); plumbing leaves it
|
|
602
616
|
// unset, which `ownerPath` skips.
|
|
603
617
|
_name: t?.At ? undefined : t?.name ?? "computed",
|
|
604
618
|
// Observe: the verdict windows this pass entered (verdict.ts) — a
|
|
605
619
|
// slot, so the stamp is never a write after construction.
|
|
606
|
-
|
|
620
|
+
Ze: 0
|
|
607
621
|
};
|
|
608
|
-
if (t?.unobserved) ext(
|
|
609
|
-
setupComputedNode(
|
|
610
|
-
return
|
|
622
|
+
if (t?.unobserved) ext(E).Ft = t.unobserved;
|
|
623
|
+
setupComputedNode(E, t);
|
|
624
|
+
return E;
|
|
611
625
|
}
|
|
612
626
|
|
|
613
627
|
/** Lazily allocate a node's cold extension (ONE shape for signals and
|
|
@@ -615,22 +629,21 @@ function computed(e, t) {
|
|
|
615
629
|
* this; hot paths read `el._x?._field` gated by the _config presence bits.
|
|
616
630
|
* Never call ext() just to store a field's default. */ function ext(e) {
|
|
617
631
|
return e.m ??= {
|
|
618
|
-
De: null,
|
|
619
632
|
Te: null,
|
|
620
|
-
|
|
621
|
-
|
|
633
|
+
Ne: null,
|
|
634
|
+
F: undefined,
|
|
635
|
+
de: undefined,
|
|
622
636
|
Se: undefined,
|
|
623
637
|
Ft: undefined,
|
|
624
638
|
ot: undefined,
|
|
625
639
|
St: null,
|
|
626
640
|
ct: null,
|
|
627
641
|
_t: null,
|
|
628
|
-
Re: null,
|
|
629
642
|
It: false,
|
|
630
643
|
ht: NOT_PENDING,
|
|
631
644
|
Vt: -1,
|
|
632
|
-
|
|
633
|
-
|
|
645
|
+
_e: 0,
|
|
646
|
+
B: NOT_PENDING,
|
|
634
647
|
T: 0
|
|
635
648
|
};
|
|
636
649
|
}
|
|
@@ -647,7 +660,12 @@ function computed(e, t) {
|
|
|
647
660
|
* the boundary collects it and shows its fallback, and the pass is the
|
|
648
661
|
* boundary's, not the tick's (A29's boundary exemption, #3540). */ function joinPass(e, t) {
|
|
649
662
|
e.R |= REACTIVE_JOINED;
|
|
650
|
-
|
|
663
|
+
// A new memo's first pass is the hold's work (born held) but not the
|
|
664
|
+
// tick's: a render effect that reads it decides — under a boundary that
|
|
665
|
+
// catches it, the boundary's; otherwise the tick joins the hold (#3540:
|
|
666
|
+
// the reader's position, not where the memo was created). Its catch, if
|
|
667
|
+
// any, is at its staging.
|
|
668
|
+
if (t.S & STATUS_UNINITIALIZED || e.u !== EFFECT_RENDER) joinPassTx(txOf(t), !e.u && e.S & STATUS_UNINITIALIZED || GlobalQueue.fe?.(e));
|
|
651
669
|
}
|
|
652
670
|
|
|
653
671
|
/** A15's stale reader (shared-hole and reveal corollaries): a render effect
|
|
@@ -656,11 +674,11 @@ function computed(e, t) {
|
|
|
656
674
|
* re-derived after that transaction's landing (`_reruns`,
|
|
657
675
|
* REACTIVE_FRAME_READ) so the frame catches up with it once it is the
|
|
658
676
|
* committed world. Not a frame born into that transaction (held and never
|
|
659
|
-
* committed — created by one of its held passes, A29): that
|
|
660
|
-
* transaction's values and keeps staging. Not of a flight
|
|
661
|
-
* already visible (CONFIG_INPUTS_PUBLISHED, #3305): its
|
|
662
|
-
* would tear against them, so the reveal observes the
|
|
663
|
-
* per pass.
|
|
677
|
+
* committed — created by one of its held passes, A29; `bornInto`): that
|
|
678
|
+
* one reads its transaction's values and keeps staging. Not of a flight
|
|
679
|
+
* whose inputs are already visible (CONFIG_INPUTS_PUBLISHED, #3305): its
|
|
680
|
+
* committed value would tear against them, so the reveal observes the
|
|
681
|
+
* flight instead. Once per pass.
|
|
664
682
|
*
|
|
665
683
|
* Lane work is a stale reader too (maintainer, 2026-10-01: a lane sees the
|
|
666
684
|
* screen plus its own guesses). A held write is held because showing it
|
|
@@ -684,17 +702,25 @@ function computed(e, t) {
|
|
|
684
702
|
// read the screen. Lane work with no committed value yet reads a flight
|
|
685
703
|
// as a mount's memo does: it enters, and the boundary it mounts catches
|
|
686
704
|
// the pending (#3540).
|
|
687
|
-
const n = e.
|
|
688
|
-
if ((passLane === null || e.
|
|
705
|
+
const n = e.i & CONFIG_VERDICT;
|
|
706
|
+
if ((passLane === null || e.S & STATUS_UNINITIALIZED && t.S & STATUS_PENDING) && !n && (e.u !== EFFECT_RENDER || t.i & CONFIG_INPUTS_PUBLISHED)) return false;
|
|
689
707
|
const l = txOf(t);
|
|
690
708
|
// Lane work and verdict readers are never the transaction's pass, whichever
|
|
691
709
|
// flush runs it: a node T holds re-enters T when it re-runs (`recompute`'s
|
|
692
710
|
// head), and the pass may then read a guess and become the lane's.
|
|
693
|
-
if (l === flushTransaction && passLane === null && !n ||
|
|
711
|
+
if (l === flushTransaction && passLane === null && !n || bornInto(e, l)) return false;
|
|
694
712
|
staleReader(e, l);
|
|
695
713
|
return true;
|
|
696
714
|
}
|
|
697
715
|
|
|
716
|
+
/** A node born into transaction `t`: held by it and never committed (A29,
|
|
717
|
+
* ruling A) — uninitialized (born held), or a member of the frame one of its
|
|
718
|
+
* held passes built (CONFIG_HELD_FRAME). Its branch exists only in `t`: it
|
|
719
|
+
* has no screen to read, so it reads `t`'s values, whichever flush runs it,
|
|
720
|
+
* and keeps staging (#4010). */ function bornInto(e, t) {
|
|
721
|
+
return (e.S & STATUS_UNINITIALIZED || e.i & CONFIG_HELD_FRAME) && e.i & CONFIG_HELD && txOf(e) === t;
|
|
722
|
+
}
|
|
723
|
+
|
|
698
724
|
/** The computed whose pass a creation under `owner` belongs to (a root's is
|
|
699
725
|
* its nearest computed ancestor's), or null outside every pass. */ function creatorPass(e) {
|
|
700
726
|
return e !== null && e.Ht ? e.gt : e;
|
|
@@ -706,11 +732,11 @@ function computed(e, t) {
|
|
|
706
732
|
* the frame that replaces them (`commitPendingNode`) or when the owner does. */ function parkChildren(e) {
|
|
707
733
|
markDisposal(e);
|
|
708
734
|
const t = ext(e);
|
|
709
|
-
t.ct = e.
|
|
710
|
-
t.St = e.
|
|
711
|
-
e.
|
|
712
|
-
e.
|
|
713
|
-
e.
|
|
735
|
+
t.ct = e.Re;
|
|
736
|
+
t.St = e.nn;
|
|
737
|
+
e.Re = null;
|
|
738
|
+
e.nn = null;
|
|
739
|
+
e.dt = 0;
|
|
714
740
|
}
|
|
715
741
|
|
|
716
742
|
/**
|
|
@@ -718,56 +744,56 @@ function computed(e, t) {
|
|
|
718
744
|
* so V8 sees the full hidden class shape at construction time. Effects always run in lazy
|
|
719
745
|
* mode (recompute is called explicitly by `effect()`), so we hardcode the lazy bits and skip
|
|
720
746
|
* the auto-dispose CONFIG bit (effect() previously cleared it post-construction).
|
|
721
|
-
*/ function createEffectNode(e, t, n, l,
|
|
722
|
-
const
|
|
747
|
+
*/ function createEffectNode(e, t, n, l, E) {
|
|
748
|
+
const s = E?.transparent ?? false;
|
|
723
749
|
// Prod and observe boilerplates — see computed() for why the observe tier
|
|
724
750
|
// gets its `_name` as a literal slot rather than a write after the fact.
|
|
725
751
|
// The default label is the node kind (tracked effects relabel their computed
|
|
726
752
|
// in trackedEffect); the wrappers in signals.ts no longer spread a name into
|
|
727
753
|
// the options to get it.
|
|
728
754
|
const u = {
|
|
729
|
-
id: inheritId(
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
755
|
+
id: inheritId(E, s, context),
|
|
756
|
+
i: (s ? CONFIG_TRANSPARENT : 0) | (E?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (E?.sync ? CONFIG_SYNC : 0) | (E?.Ct ?? 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
|
|
757
|
+
en: false,
|
|
758
|
+
Re: null,
|
|
759
|
+
D: context?.D ?? defaultContext,
|
|
760
|
+
dt: 0,
|
|
761
|
+
ee: e,
|
|
762
|
+
Ae: undefined,
|
|
737
763
|
it: 0,
|
|
738
764
|
Ot: undefined,
|
|
739
765
|
Rt: null,
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
766
|
+
G: null,
|
|
767
|
+
p: null,
|
|
768
|
+
pe: 0,
|
|
743
769
|
t: null,
|
|
744
770
|
Dt: null,
|
|
745
771
|
_parent: context,
|
|
746
|
-
|
|
772
|
+
tn: null,
|
|
747
773
|
Gt: null,
|
|
748
|
-
|
|
774
|
+
nn: null,
|
|
749
775
|
R: REACTIVE_LAZY,
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
776
|
+
S: STATUS_UNINITIALIZED,
|
|
777
|
+
Ie: clock,
|
|
778
|
+
Oe: NOT_PENDING,
|
|
753
779
|
Pt: -1,
|
|
754
|
-
|
|
780
|
+
De: false,
|
|
755
781
|
lt: false,
|
|
756
782
|
Nt: undefined,
|
|
757
783
|
Ut: t,
|
|
758
784
|
Lt: n,
|
|
759
785
|
kt: undefined,
|
|
760
|
-
|
|
786
|
+
u: l,
|
|
761
787
|
m: null,
|
|
762
|
-
_name:
|
|
763
|
-
|
|
788
|
+
_name: E?.name ?? "effect",
|
|
789
|
+
Ze: 0
|
|
764
790
|
};
|
|
765
791
|
// Effects dispatch status through the SHARED notifier (statusNotifierOf,
|
|
766
792
|
// keyed off _type) — storing it per node forced a full NodeExtension
|
|
767
793
|
// allocation on EVERY effect at creation (an alloc + 19 field stores,
|
|
768
794
|
// +23% effect creation, caught by the creation benches). Only genuinely
|
|
769
795
|
// per-node channels (boundaries) live on _x.
|
|
770
|
-
if (
|
|
796
|
+
if (E?.unobserved) ext(u).Ft = E.unobserved;
|
|
771
797
|
setupComputedNode(u, lazyOptions);
|
|
772
798
|
return u;
|
|
773
799
|
}
|
|
@@ -788,7 +814,7 @@ function setEffectStatusNotify(e) {
|
|
|
788
814
|
* 0, and only effect literals carry the field) get the shared notifier.
|
|
789
815
|
* Presence doubles as the "display consumer" membership test in the status
|
|
790
816
|
* walks, exactly as the per-node field did when every effect carried one. */ function statusNotifierOf(e) {
|
|
791
|
-
return e.
|
|
817
|
+
return e.u ? effectStatusNotify ?? undefined : undefined;
|
|
792
818
|
}
|
|
793
819
|
|
|
794
820
|
const lazyOptions = {
|
|
@@ -803,9 +829,9 @@ function setupComputedNode(e, t) {
|
|
|
803
829
|
GlobalQueue.xt?.(e);
|
|
804
830
|
!t?.lazy && recompute(e, true);
|
|
805
831
|
if (snapshotCaptureActive && !t?.lazy) {
|
|
806
|
-
if (!(e.
|
|
807
|
-
ext(e).ot = e.
|
|
808
|
-
e.
|
|
832
|
+
if (!(e.S & STATUS_PENDING) && !(e.i & CONFIG_NO_SNAPSHOT)) {
|
|
833
|
+
ext(e).ot = e.Ae === undefined ? NO_SNAPSHOT : e.Ae;
|
|
834
|
+
e.i |= CONFIG_HAS_SNAPSHOT;
|
|
809
835
|
snapshotSources.add(e);
|
|
810
836
|
}
|
|
811
837
|
}
|
|
@@ -817,22 +843,22 @@ function signal(e, t) {
|
|
|
817
843
|
// createSignal nodes so ownerPath can locate signal subjects; null here,
|
|
818
844
|
// and staying null on internal signals — one shape either way).
|
|
819
845
|
const n = {
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
846
|
+
en: t?.equals ?? isEqual,
|
|
847
|
+
i: (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (t?.ae ? CONFIG_NO_SNAPSHOT : 0),
|
|
848
|
+
Ae: e,
|
|
823
849
|
t: null,
|
|
824
850
|
Dt: null,
|
|
825
|
-
|
|
826
|
-
|
|
851
|
+
Ie: clock,
|
|
852
|
+
Oe: NOT_PENDING,
|
|
827
853
|
Pt: -1,
|
|
828
854
|
m: null,
|
|
829
855
|
_name: t?.name ?? "signal",
|
|
830
|
-
|
|
856
|
+
L: null
|
|
831
857
|
};
|
|
832
858
|
if (t?.unobserved) ext(n).Ft = t.unobserved;
|
|
833
|
-
if (snapshotCaptureActive && !(n.
|
|
859
|
+
if (snapshotCaptureActive && !(n.i & CONFIG_NO_SNAPSHOT)) {
|
|
834
860
|
ext(n).ot = e === undefined ? NO_SNAPSHOT : e;
|
|
835
|
-
n.
|
|
861
|
+
n.i |= CONFIG_HAS_SNAPSHOT;
|
|
836
862
|
snapshotSources.add(n);
|
|
837
863
|
}
|
|
838
864
|
return n;
|
|
@@ -851,26 +877,26 @@ function isEqual(e, t) {
|
|
|
851
877
|
* object, no NodeExtension, no post-construction expandos. `ownedWrite` is
|
|
852
878
|
* baked in: the setter carries the owned-scope guard; node-level writes are
|
|
853
879
|
* the store's notification machinery. The observe literal adds the label
|
|
854
|
-
* slot (the store relabels it `store.<key>` when the engine is installed). */ function slotSignal(e, t, n, l,
|
|
855
|
-
const
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
880
|
+
* slot (the store relabels it `store.<key>` when the engine is installed). */ function slotSignal(e, t, n, l, E) {
|
|
881
|
+
const s = {
|
|
882
|
+
en: t,
|
|
883
|
+
i: CONFIG_OWNED_WRITE | CONFIG_SLOT_NODE,
|
|
884
|
+
Ae: e,
|
|
859
885
|
t: null,
|
|
860
886
|
Dt: null,
|
|
861
|
-
|
|
862
|
-
|
|
887
|
+
Ie: clock,
|
|
888
|
+
Oe: NOT_PENDING,
|
|
863
889
|
Pt: -1,
|
|
864
890
|
m: null,
|
|
865
891
|
yt: n,
|
|
866
892
|
bt: l,
|
|
867
|
-
acc:
|
|
893
|
+
acc: E,
|
|
868
894
|
px: undefined,
|
|
869
895
|
pxv: undefined,
|
|
870
896
|
_name: "signal",
|
|
871
|
-
|
|
897
|
+
L: null
|
|
872
898
|
};
|
|
873
|
-
return
|
|
899
|
+
return s;
|
|
874
900
|
}
|
|
875
901
|
|
|
876
902
|
/**
|
|
@@ -945,13 +971,13 @@ function setVerdict(e) {
|
|
|
945
971
|
// pay-for-use contract. Owner-lifecycle nodes are dead: recomputing would
|
|
946
972
|
// re-run user code in a torn-down tree (and discard manual writes on
|
|
947
973
|
// derived-writable signals), so reads return the last committed value.
|
|
948
|
-
if (e.
|
|
974
|
+
if (e.i & CONFIG_AUTO_DISPOSE) {
|
|
949
975
|
const t = e._parent;
|
|
950
976
|
if (t !== null) {
|
|
951
977
|
// A dormant node was off the chain when its owner died, so the strip
|
|
952
978
|
// in disposeChildren missed it: freeze here instead (#3024).
|
|
953
979
|
if (t.R & REACTIVE_DISPOSED) {
|
|
954
|
-
e.
|
|
980
|
+
e.i &= ~CONFIG_AUTO_DISPOSE;
|
|
955
981
|
return;
|
|
956
982
|
}
|
|
957
983
|
// A zombie never left its chain (the parked frame is drained whole,
|
|
@@ -979,7 +1005,7 @@ function setVerdict(e) {
|
|
|
979
1005
|
* never the graph — A32).
|
|
980
1006
|
* False means the reader derives from the staged value.
|
|
981
1007
|
*/ function readerSeesCommitted(e, t) {
|
|
982
|
-
return !!(!t || e.
|
|
1008
|
+
return !!(!t || e.Oe === NOT_PENDING || t.i & CONFIG_CHILDREN_FORBIDDEN);
|
|
983
1009
|
}
|
|
984
1010
|
|
|
985
1011
|
/** A28 — set when a node is staged (queuePendingNode) OUTSIDE a flush;
|
|
@@ -1000,12 +1026,12 @@ function markUnflushedStaged() {
|
|
|
1000
1026
|
* is unflushed. Exempt: promoted writes (A28 (4): a write issued inside a
|
|
1001
1027
|
* recompute is promoted at that recompute's end — boundary and loading
|
|
1002
1028
|
* machinery, signals declared for in-computation writes). */ function unflushedValue(e) {
|
|
1003
|
-
if (globalQueue.
|
|
1029
|
+
if (globalQueue.X || e.Oe === NOT_PENDING || e.i & CONFIG_PROMOTED) return NOT_PENDING;
|
|
1004
1030
|
// A held node (L2) carries a staged value between flushes too, but it was
|
|
1005
1031
|
// flushed — and parked. Not an unflushed write — unless rewritten since:
|
|
1006
1032
|
// then the flushed staging is what the world sees until the next flush.
|
|
1007
|
-
if (e.
|
|
1008
|
-
return e.
|
|
1033
|
+
if (e.i & CONFIG_HELD) return e.m.Vt === clock ? e.m.ht : NOT_PENDING;
|
|
1034
|
+
return e.Ae;
|
|
1009
1035
|
}
|
|
1010
1036
|
|
|
1011
1037
|
/** A28: a held node rewritten outside a flush keeps the staging the last
|
|
@@ -1013,7 +1039,7 @@ function markUnflushedStaged() {
|
|
|
1013
1039
|
* stash is stamped with the clock, which that flush advances. */ function stashFlushed(e) {
|
|
1014
1040
|
const t = e.m;
|
|
1015
1041
|
if (t.Vt !== clock) {
|
|
1016
|
-
t.ht = e.
|
|
1042
|
+
t.ht = e.Oe;
|
|
1017
1043
|
t.Vt = clock;
|
|
1018
1044
|
unflushedStaged = true;
|
|
1019
1045
|
}
|
|
@@ -1034,7 +1060,7 @@ function resyncUnflushedCompanions() {
|
|
|
1034
1060
|
unflushedStaged = false;
|
|
1035
1061
|
// Length-guarded: the common flush has nothing here and allocates nothing.
|
|
1036
1062
|
if (promotedWrites.length !== 0) {
|
|
1037
|
-
for (const e of promotedWrites) e.
|
|
1063
|
+
for (const e of promotedWrites) e.i &= ~CONFIG_PROMOTED;
|
|
1038
1064
|
promotedWrites.length = 0;
|
|
1039
1065
|
}
|
|
1040
1066
|
}
|
|
@@ -1053,7 +1079,7 @@ function resyncUnflushedCompanions() {
|
|
|
1053
1079
|
// never read the PRE-re-ask value as settled. Self-guarded: a clean
|
|
1054
1080
|
// node no-ops and updateIfNecessary refuses disposed nodes (#2983) —
|
|
1055
1081
|
// a dead target serves its last value, which is already quiescent.
|
|
1056
|
-
else if (t.
|
|
1082
|
+
else if (t.i & CONFIG_FRESH_READ) updateIfNecessary(e);
|
|
1057
1083
|
const n = e.it;
|
|
1058
1084
|
// parent check is shallow, might need to be recursive
|
|
1059
1085
|
if (n >= t.it && e._parent !== t) t.it = n + 1;
|
|
@@ -1068,12 +1094,12 @@ function read(e) {
|
|
|
1068
1094
|
// as the `||` expression was: a signal's `_statusFlags`/`_height` read as
|
|
1069
1095
|
// undefined and mask to 0 on every site below.)
|
|
1070
1096
|
const l = e;
|
|
1071
|
-
if (typeof n.
|
|
1097
|
+
if (typeof n.ee === "function") prepareComputed(e);
|
|
1072
1098
|
// The verdict windows (`latest`, `isPending`; verdict.ts): one cold path,
|
|
1073
1099
|
// off this one, present only while a window is open.
|
|
1074
1100
|
if (verdict !== null) return verdict(e, t);
|
|
1075
|
-
if (!n.
|
|
1076
|
-
!(e.
|
|
1101
|
+
if (!n.ee && e.m?.ot === undefined && !snapshotCaptureActive && (!unflushedStaged || e.Oe === NOT_PENDING) && // A28, see readNodeFast
|
|
1102
|
+
!(e.i & CONFIG_OVERRIDE) && // lanes: the slow path's one arm
|
|
1077
1103
|
true) {
|
|
1078
1104
|
if (t) {
|
|
1079
1105
|
if (tracking) link(e, t);
|
|
@@ -1087,37 +1113,37 @@ function read(e) {
|
|
|
1087
1113
|
// stays out too (rule 3, `frameRead`). The pass remembers it read the
|
|
1088
1114
|
// future (REACTIVE_JOINED): a first pass that did is born held (A29),
|
|
1089
1115
|
// wherever it was created. Lane work sees the screen (`frameRead`).
|
|
1090
|
-
if (e.
|
|
1091
|
-
if (frameRead(t, e)) return e.
|
|
1116
|
+
if (e.i & CONFIG_HELD && !(t.i & CONFIG_CHILDREN_FORBIDDEN)) {
|
|
1117
|
+
if (frameRead(t, e)) return e.Ae;
|
|
1092
1118
|
joinPass(t, e);
|
|
1093
1119
|
}
|
|
1094
1120
|
}
|
|
1095
1121
|
// Committed visibility for untracked and children-forbidden readers.
|
|
1096
|
-
if (!t || e.
|
|
1122
|
+
if (!t || e.Oe === NOT_PENDING || t.i & CONFIG_CHILDREN_FORBIDDEN) return e.Ae;
|
|
1097
1123
|
// (A plain signal is never uninitialized — the carve-out is `serve`'s.)
|
|
1098
|
-
if (t.
|
|
1124
|
+
if (t.i & CONFIG_VERDICT && stagedScreen(t)) return e.Ae;
|
|
1099
1125
|
stagedRead(t);
|
|
1100
|
-
return e.
|
|
1126
|
+
return e.Oe;
|
|
1101
1127
|
}
|
|
1102
1128
|
// Rule 3: this read serves the committed value to a render effect outside a
|
|
1103
1129
|
// parking flush (decided after the pull below — a pull can join).
|
|
1104
|
-
let
|
|
1130
|
+
let E = false;
|
|
1105
1131
|
if (t) {
|
|
1106
1132
|
if (tracking) {
|
|
1107
1133
|
link(e, t);
|
|
1108
|
-
if (l.
|
|
1134
|
+
if (l.ee) pullComputed(l, t);
|
|
1109
1135
|
}
|
|
1110
1136
|
// L2, as in the fast block above (tracked or not). A node born into the
|
|
1111
1137
|
// future (uninitialized) has no committed value: every reader derives
|
|
1112
1138
|
// from the future there, a render effect included (A29: a stale reader
|
|
1113
1139
|
// of it cannot fall back to the committed frame and enters instead).
|
|
1114
|
-
if (e.
|
|
1115
|
-
if (l.
|
|
1140
|
+
if (e.i & CONFIG_HELD && !(e.i & CONFIG_OVERRIDE) && !(t.i & CONFIG_CHILDREN_FORBIDDEN)) {
|
|
1141
|
+
if (l.S & STATUS_UNINITIALIZED || !frameRead(t, e)) joinPass(t, e); else E = true;
|
|
1116
1142
|
}
|
|
1117
1143
|
}
|
|
1118
1144
|
// Lanes: a lane's node (after the pull — the node is current). NOT_PENDING
|
|
1119
1145
|
// falls through: lane work's read of a pending member throws like any.
|
|
1120
|
-
if (e.
|
|
1146
|
+
if (e.i & CONFIG_OVERRIDE) {
|
|
1121
1147
|
const n = GlobalQueue.Zt(t, e);
|
|
1122
1148
|
if (n !== NOT_PENDING) return n;
|
|
1123
1149
|
}
|
|
@@ -1133,53 +1159,54 @@ function read(e) {
|
|
|
1133
1159
|
// effect keeps its DOM by throwing, a memo has no DOM and is handed the
|
|
1134
1160
|
// value. `[isPending(x), x()]` reads `[true, stale]` in either order (A10)
|
|
1135
1161
|
// — unless the flight committed beneath inputs already on screen
|
|
1136
|
-
// (`observeFlight`)
|
|
1137
|
-
|
|
1162
|
+
// (`observeFlight`), or the reader was born into the flight's transaction
|
|
1163
|
+
// (#4010): it has no screen, and waits on the flight with its frame.
|
|
1164
|
+
if (l.S & STATUS_PENDING && !E && !(e.i & CONFIG_OVERRIDE) && !(l.S & STATUS_UNINITIALIZED)) {
|
|
1138
1165
|
// A reader with no committed value yet sees the flight pending: a
|
|
1139
1166
|
// boundary a lane mounts shows its fallback (A29's boundary exemption).
|
|
1140
|
-
if (passLane !== null && !(t?.
|
|
1167
|
+
if (passLane !== null && !(t?.S & STATUS_UNINITIALIZED)) E = true; else if (t !== null && t.i & CONFIG_VERDICT) E = GlobalQueue.vt(t, l);
|
|
1141
1168
|
}
|
|
1142
|
-
if (l.
|
|
1169
|
+
if (l.S & STATUS_PENDING && !E) {
|
|
1143
1170
|
// A reader landing on a pending node throws; an untracked read of an
|
|
1144
1171
|
// UNINITIALIZED node has no committed value to serve and throws too. A
|
|
1145
1172
|
// children-forbidden reader (createTrackedEffect / onSettled) sees the
|
|
1146
1173
|
// frame, not the graph (A32): the committed value, and the NotReady only
|
|
1147
1174
|
// where there is none (uninitialized).
|
|
1148
1175
|
if (t) {
|
|
1149
|
-
if (!(t.
|
|
1176
|
+
if (!(t.i & CONFIG_CHILDREN_FORBIDDEN) || l.S & STATUS_UNINITIALIZED) {
|
|
1150
1177
|
// An untracked read of a pending node still re-runs its reader when
|
|
1151
1178
|
// the node settles — unless nobody is reading (`spectating`, #3528)
|
|
1152
1179
|
// or the reader is the node itself.
|
|
1153
1180
|
if (!tracking && !spectating && e !== t) link(e, t);
|
|
1154
|
-
throw l.m?.
|
|
1181
|
+
throw l.m?.F;
|
|
1155
1182
|
}
|
|
1156
|
-
} else if (l.
|
|
1157
|
-
throw l.m?.
|
|
1183
|
+
} else if (l.S & STATUS_UNINITIALIZED) {
|
|
1184
|
+
throw l.m?.F;
|
|
1158
1185
|
}
|
|
1159
1186
|
}
|
|
1160
1187
|
// An errored derive throws for every late reader instead of silently
|
|
1161
1188
|
// serving node values (memo parity, #2897 ruling).
|
|
1162
|
-
if (l.
|
|
1189
|
+
if (l.ee && l.S & STATUS_ERROR) {
|
|
1163
1190
|
// Only a genuine reactive re-read may retry an errored async source:
|
|
1164
1191
|
// - tracking: owned/tracked scope only (never events / `untrack` / effect side-effect phase)
|
|
1165
1192
|
// - owner._time < clock: only on a later cycle than the one the error was found
|
|
1166
|
-
if (tracking && l.
|
|
1193
|
+
if (tracking && l.Ie < clock) {
|
|
1167
1194
|
recompute(l);
|
|
1168
1195
|
return read(e);
|
|
1169
|
-
} else throw l.m?.
|
|
1196
|
+
} else throw l.m?.F;
|
|
1170
1197
|
}
|
|
1171
|
-
if (snapshotCaptureActive && t && t.
|
|
1198
|
+
if (snapshotCaptureActive && t && t.i & CONFIG_IN_SNAPSHOT_SCOPE) {
|
|
1172
1199
|
const n = e.m?.ot;
|
|
1173
1200
|
if (n !== undefined) {
|
|
1174
1201
|
const l = n === NO_SNAPSHOT ? undefined : n;
|
|
1175
|
-
const
|
|
1176
|
-
if (
|
|
1202
|
+
const E = e.Oe !== NOT_PENDING ? e.Oe : e.Ae;
|
|
1203
|
+
if (E !== l) t.R |= REACTIVE_SNAPSHOT_STALE;
|
|
1177
1204
|
return l;
|
|
1178
1205
|
}
|
|
1179
1206
|
}
|
|
1180
|
-
if (
|
|
1181
|
-
const
|
|
1182
|
-
if (!t && typeof n.
|
|
1207
|
+
if (E) return e.Ae;
|
|
1208
|
+
const s = serve(e, t);
|
|
1209
|
+
if (!t && typeof n.ee === "function" && e.i & CONFIG_AUTO_DISPOSE && !(l.S & STATUS_PENDING) && !e.t) {
|
|
1183
1210
|
// Deferred, not inline (#3078): an inline unobserved() here made untracked
|
|
1184
1211
|
// reads destructive — dispose on this read, full revival recompute on the
|
|
1185
1212
|
// next — so consecutive reads could answer differently with no write in
|
|
@@ -1189,7 +1216,7 @@ function read(e) {
|
|
|
1189
1216
|
dormantNodes.add(e);
|
|
1190
1217
|
schedule();
|
|
1191
1218
|
}
|
|
1192
|
-
return
|
|
1219
|
+
return s;
|
|
1193
1220
|
}
|
|
1194
1221
|
|
|
1195
1222
|
/**
|
|
@@ -1206,16 +1233,16 @@ function read(e) {
|
|
|
1206
1233
|
// an untracked reader has nothing to serve and holds (A19 exception 1) — a
|
|
1207
1234
|
// bookkeeping read (`spectate`) likewise, and a children-forbidden reader
|
|
1208
1235
|
// (A32: the frame, which has nothing here).
|
|
1209
|
-
if (e.
|
|
1236
|
+
if (e.Oe !== NOT_PENDING && e.S & STATUS_UNINITIALIZED && (!t || spectating || t.i & CONFIG_CHILDREN_FORBIDDEN)) throw new NotReadyError(null);
|
|
1210
1237
|
const n = t && unflushedStaged ? unflushedValue(e) : NOT_PENDING;
|
|
1211
1238
|
if (n !== NOT_PENDING) {
|
|
1212
1239
|
markLateLinker(t);
|
|
1213
1240
|
return n;
|
|
1214
1241
|
}
|
|
1215
|
-
if (readerSeesCommitted(e, t)) return e.
|
|
1216
|
-
if (t.
|
|
1242
|
+
if (readerSeesCommitted(e, t)) return e.Ae;
|
|
1243
|
+
if (t.i & CONFIG_VERDICT && !(e.S & STATUS_UNINITIALIZED) && stagedScreen(t)) return e.Ae;
|
|
1217
1244
|
stagedRead(t, e);
|
|
1218
|
-
return e.
|
|
1245
|
+
return e.Oe;
|
|
1219
1246
|
}
|
|
1220
1247
|
|
|
1221
1248
|
/** A pass read a staging of this flush: it derives from what the flush may
|
|
@@ -1227,10 +1254,13 @@ function read(e) {
|
|
|
1227
1254
|
* write never shows through the lane. A verdict lane's work likewise: the
|
|
1228
1255
|
* lane holds verdicts, not the frame's other stagings (#3851) — except a
|
|
1229
1256
|
* verdict reader, which answered for itself (the lane seam, lanes.ts). Not
|
|
1230
|
-
* a read of a node born staged: it has no committed value to
|
|
1231
|
-
* and a re-run would read the same staging and re-queue every
|
|
1232
|
-
|
|
1233
|
-
if (
|
|
1257
|
+
* a read of a node born staged at all: it has no committed value to
|
|
1258
|
+
* re-derive on, and a re-run would read the same staging and re-queue every
|
|
1259
|
+
* round (#3969: nor does it make a later lane read a re-derive, `enterLane`). */ function stagedRead(e, t) {
|
|
1260
|
+
if (!(t?.S & STATUS_UNINITIALIZED)) {
|
|
1261
|
+
e.R |= REACTIVE_STAGED_READ;
|
|
1262
|
+
if (passLane !== null) stagedReaders.push(e);
|
|
1263
|
+
}
|
|
1234
1264
|
}
|
|
1235
1265
|
|
|
1236
1266
|
/** A10 for a staged node: a verdict reader (the pass entered a window) that
|
|
@@ -1240,8 +1270,9 @@ function read(e) {
|
|
|
1240
1270
|
* #3322 — one run, on the committed world), or with the hold. Before any
|
|
1241
1271
|
* transaction the answer is the seam's and the reader watches it
|
|
1242
1272
|
* (verdict.ts). A node born into the future has no screen: its staging is
|
|
1243
|
-
* its only value (A29).
|
|
1244
|
-
|
|
1273
|
+
* its only value (A29). Nor has a reader born into the flush's transaction
|
|
1274
|
+
* (`bornInto`, #4010): its branch exists only there. */ function stagedScreen(e) {
|
|
1275
|
+
if (flushTransaction === null || bornInto(e, flushTransaction)) return false;
|
|
1245
1276
|
staleReader(e, flushTransaction);
|
|
1246
1277
|
return true;
|
|
1247
1278
|
}
|
|
@@ -1249,39 +1280,40 @@ function read(e) {
|
|
|
1249
1280
|
/** A28 (4) — written inside a recompute that runs OUTSIDE a flush (a
|
|
1250
1281
|
* creation-time compute): promoted at that pass's end, visible to the rest of
|
|
1251
1282
|
* the block. Cold: only contextual writes reach here. */ function notePromotedWrite(e) {
|
|
1252
|
-
if (globalQueue.
|
|
1253
|
-
e.
|
|
1283
|
+
if (globalQueue.X || e.i & CONFIG_PROMOTED) return;
|
|
1284
|
+
e.i |= CONFIG_PROMOTED;
|
|
1254
1285
|
promotedWrites.push(e);
|
|
1255
1286
|
}
|
|
1256
1287
|
|
|
1257
|
-
/** Cold half of setSignal's snapshot arm (see there)
|
|
1258
|
-
*
|
|
1259
|
-
*
|
|
1288
|
+
/** Cold half of setSignal's snapshot arm (see there), shared with a store
|
|
1289
|
+
* container's structural notify: first write during capture to a plain
|
|
1290
|
+
* signal without a live snapshot records the pre-write value. Only plain
|
|
1291
|
+
* user signals qualify. Computeds reach setSignal from an
|
|
1260
1292
|
* async landing (asyncWrite), and a value arriving from async during the pass
|
|
1261
1293
|
* REVEALS — the creation-time arm skips pending computeds for the same
|
|
1262
1294
|
* reason. Firewall leaves belong to a projection whose compute is the tree's
|
|
1263
1295
|
* own work, captured (or deliberately not) at creation. */ function captureWriteSnapshot(e, t) {
|
|
1264
|
-
if (e.
|
|
1296
|
+
if (e.i & CONFIG_NO_SNAPSHOT || e.ee !== undefined || e.m?.ot !== undefined) return;
|
|
1265
1297
|
ext(e).ot = t === undefined ? NO_SNAPSHOT : t;
|
|
1266
|
-
e.
|
|
1298
|
+
e.i |= CONFIG_HAS_SNAPSHOT;
|
|
1267
1299
|
snapshotSources.add(e);
|
|
1268
1300
|
}
|
|
1269
1301
|
|
|
1270
1302
|
function setSignal(e, t) {
|
|
1271
|
-
const n = e.
|
|
1303
|
+
const n = e.Oe === NOT_PENDING ? e.Ae : e.Oe;
|
|
1272
1304
|
if (typeof t === "function") t = t(n);
|
|
1273
1305
|
// Lanes: a write landing on a guess is its truth (A18 — an async guess's
|
|
1274
1306
|
// own landing), judged against the guess; one landing on a lane's
|
|
1275
1307
|
// derivation (its flight) is the lane's staging (lanes.ts).
|
|
1276
|
-
if (e.
|
|
1308
|
+
if (e.i & CONFIG_OVERRIDE) return GlobalQueue.Qt(e, t);
|
|
1277
1309
|
// L2 (T4) / A34 (1): a write to a node whose staged state is held is a
|
|
1278
1310
|
// second proposal for the slot — the same value again or another — and
|
|
1279
1311
|
// the two cannot finish at different times: the writer's tick joins the
|
|
1280
1312
|
// hold, before the equality gate.
|
|
1281
|
-
if (e.
|
|
1313
|
+
if (e.i & CONFIG_HELD) joinFuture(txOf(e));
|
|
1282
1314
|
// Uninitialized check first: the first commit has no previous value, so the
|
|
1283
1315
|
// user comparator must not run against `undefined` (matches recompute).
|
|
1284
|
-
const l = !!(e.
|
|
1316
|
+
const l = !!(e.S & STATUS_UNINITIALIZED) || !e.en || !e.en(n, t);
|
|
1285
1317
|
if (!l) return t;
|
|
1286
1318
|
// Attribution hook: this committed write is where a re-run chain begins.
|
|
1287
1319
|
if (attrHooks !== null) attrHooks.write(e, n, t);
|
|
@@ -1295,21 +1327,21 @@ function setSignal(e, t) {
|
|
|
1295
1327
|
// pass reads a value the server never had. Store leaves written here are
|
|
1296
1328
|
// plain signals and qualify the same way.
|
|
1297
1329
|
if (snapshotCaptureActive) captureWriteSnapshot(e, n);
|
|
1298
|
-
const
|
|
1299
|
-
if (!
|
|
1330
|
+
const E = e.Oe !== NOT_PENDING;
|
|
1331
|
+
if (!E) queuePendingNode(e);
|
|
1300
1332
|
// A28: a held node rewritten outside a flush keeps its flushed staging
|
|
1301
1333
|
// for every reader until the flush that carries the rewrite.
|
|
1302
|
-
else if (e.
|
|
1303
|
-
e.
|
|
1334
|
+
else if (e.i & CONFIG_HELD && !globalQueue.X) stashFlushed(e);
|
|
1335
|
+
e.Oe = t;
|
|
1304
1336
|
if (context !== null) notePromotedWrite(e);
|
|
1305
1337
|
// _time is a computed-only slot (§12e): writing it on a signal would fork
|
|
1306
1338
|
// the lean shape. Every read site is computed-typed.
|
|
1307
|
-
if (e.
|
|
1339
|
+
if (e.ee !== undefined) e.Ie = clock;
|
|
1308
1340
|
// Staged-rewrite fast path (§12d): a re-write to a node whose subscribers
|
|
1309
1341
|
// were already walked — and where nothing has recomputed or linked since
|
|
1310
1342
|
// (epoch) — re-stages the value and stops. The walk is idempotent (subs
|
|
1311
1343
|
// marked, heap entries flag-guarded, effects queued once).
|
|
1312
|
-
if (
|
|
1344
|
+
if (E && e.Pt === notifyEpoch) return t;
|
|
1313
1345
|
insertSubs(e);
|
|
1314
1346
|
schedule();
|
|
1315
1347
|
return t;
|
|
@@ -1334,9 +1366,9 @@ function setSignal(e, t) {
|
|
|
1334
1366
|
// of replacing it. A write made under the hold carries the mark and keeps
|
|
1335
1367
|
// (1)'s last-write-wins. Only the user setter asks; an async landing
|
|
1336
1368
|
// writing a held node is the transaction's own work.
|
|
1337
|
-
const n = e.
|
|
1369
|
+
const n = e.i & CONFIG_HELD && !(e.R & REACTIVE_MANUAL_WRITE) && txOf(e) !== flushTransaction;
|
|
1338
1370
|
// The writer read the committed frame; its updater composes on it.
|
|
1339
|
-
if (n && typeof t === "function") t = t(e.
|
|
1371
|
+
if (n && typeof t === "function") t = t(e.Ae);
|
|
1340
1372
|
const l = setSignal(e, t);
|
|
1341
1373
|
if (n) {
|
|
1342
1374
|
// The one write that re-runs: the staging it replaced was derived from
|
|
@@ -1344,7 +1376,7 @@ function setSignal(e, t) {
|
|
|
1344
1376
|
e.R = e.R & ~REACTIVE_CHECK | REACTIVE_DIRTY;
|
|
1345
1377
|
insertIntoHeap(e, dirtyQueue);
|
|
1346
1378
|
schedule();
|
|
1347
|
-
} else if (e.
|
|
1379
|
+
} else if (e.Oe !== NOT_PENDING) e.R |= REACTIVE_MANUAL_WRITE;
|
|
1348
1380
|
return l;
|
|
1349
1381
|
}
|
|
1350
1382
|
|
|
@@ -1392,7 +1424,7 @@ function staleValues(e, t = true) {
|
|
|
1392
1424
|
* recompute as a quiet re-ask and schedules it; no-ops for non-derived or
|
|
1393
1425
|
* disposed targets.
|
|
1394
1426
|
*/ function markRefresh(e) {
|
|
1395
|
-
if (typeof e.
|
|
1427
|
+
if (typeof e.ee === "function" && !(e.R & REACTIVE_DISPOSED)) {
|
|
1396
1428
|
// A manual write never refuses the re-run (A34 rule B): a refresh in the
|
|
1397
1429
|
// write's own frame re-asks with the write as `prev`, like any source
|
|
1398
1430
|
// change (#3026 generalized).
|
|
@@ -1408,4 +1440,4 @@ function staleValues(e, t = true) {
|
|
|
1408
1440
|
}
|
|
1409
1441
|
}
|
|
1410
1442
|
|
|
1411
|
-
export { clearSnapshots, computed, context, createEffectNode, effectStatusNotify, ext, isEqual, markLateLinker, markRefresh, markSnapshotScope, markUnflushedStaged, notePromotedWrite, prepareComputed, pullComputed, read, readerSeesCommitted, recompute, releaseSnapshotScope, resyncUnflushedCompanions, runWithOwner, serve, setEffectStatusNotify, setMemo, setSignal, setSnapshotCapture, setVerdict, signal, slotSignal, snapshotCaptureActive, snapshotSources, spectating, stagedRead, stagedScreen, staleValues, stashFlushed, statusNotifierOf, tracking, unflushedStaged, unflushedValue, untrack, verdict };
|
|
1443
|
+
export { bornInto, captureWriteSnapshot, clearSnapshots, computed, context, createEffectNode, effectStatusNotify, ext, isEqual, joinPass, markLateLinker, markRefresh, markSnapshotScope, markUnflushedStaged, notePromotedWrite, prepareComputed, pullComputed, read, readerSeesCommitted, recompute, releaseSnapshotScope, resyncUnflushedCompanions, runWithOwner, serve, setEffectStatusNotify, setMemo, setSignal, setSnapshotCapture, setVerdict, signal, slotSignal, snapshotCaptureActive, snapshotSources, spectating, stagedRead, stagedScreen, staleValues, stashFlushed, statusNotifierOf, tracking, unflushedStaged, unflushedValue, untrack, verdict };
|