@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
package/dist/prod/core/core.js
CHANGED
|
@@ -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_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_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
|
|
|
@@ -21,10 +21,10 @@ import { schedule, GlobalQueue, clock, txOf, flushTransaction, joinFuture, queue
|
|
|
21
21
|
// directly into the user queue ran in the SAME pass, read the old value, and
|
|
22
22
|
// nothing re-notified it when the value landed (#3291).
|
|
23
23
|
GlobalQueue.Ce = e => {
|
|
24
|
-
if (e.
|
|
24
|
+
if (e.u === EFFECT_TRACKED) {
|
|
25
25
|
deleteFromHeap(e, dirtyQueue);
|
|
26
|
-
e.
|
|
27
|
-
globalQueue.enqueue(EFFECT_USER, e.
|
|
26
|
+
e.Ge = true;
|
|
27
|
+
globalQueue.enqueue(EFFECT_USER, e.M);
|
|
28
28
|
}
|
|
29
29
|
// L2: a zombie's fate is the seam's — its owner's commit disposes it, or
|
|
30
30
|
// the park cancels the pass (the write that dirtied it is held). It runs
|
|
@@ -35,7 +35,7 @@ GlobalQueue.Ce = e => {
|
|
|
35
35
|
else if (e.R & REACTIVE_ZOMBIE && !(e.R & REACTIVE_LANE_DIRTY)) deferZombie(e); else recompute(e);
|
|
36
36
|
};
|
|
37
37
|
|
|
38
|
-
GlobalQueue.
|
|
38
|
+
GlobalQueue.Fe = disposeChildren;
|
|
39
39
|
|
|
40
40
|
let tracking = false;
|
|
41
41
|
|
|
@@ -47,7 +47,7 @@ let snapshotSources = null;
|
|
|
47
47
|
|
|
48
48
|
function ownerInSnapshotScope(e) {
|
|
49
49
|
while (e) {
|
|
50
|
-
if (e.
|
|
50
|
+
if (e.he) return true;
|
|
51
51
|
e = e._parent;
|
|
52
52
|
}
|
|
53
53
|
return false;
|
|
@@ -59,34 +59,34 @@ function setSnapshotCapture(e) {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
function markSnapshotScope(e) {
|
|
62
|
-
e.
|
|
62
|
+
e.he = true;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
function releaseSnapshotScope(e) {
|
|
66
|
-
e.
|
|
66
|
+
e.he = false;
|
|
67
67
|
releaseSubtree(e);
|
|
68
68
|
schedule();
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
function releaseSubtree(e) {
|
|
72
|
-
let t = e.
|
|
72
|
+
let t = e.Ve;
|
|
73
73
|
while (t) {
|
|
74
|
-
if (t.
|
|
75
|
-
t = t.
|
|
74
|
+
if (t.he) {
|
|
75
|
+
t = t.ge;
|
|
76
76
|
continue;
|
|
77
77
|
}
|
|
78
|
-
if (t.
|
|
78
|
+
if (t.ee) {
|
|
79
79
|
const e = t;
|
|
80
|
-
e.
|
|
80
|
+
e.i &= ~CONFIG_IN_SNAPSHOT_SCOPE;
|
|
81
81
|
if (e.R & REACTIVE_SNAPSHOT_STALE) {
|
|
82
82
|
e.R &= ~REACTIVE_SNAPSHOT_STALE;
|
|
83
83
|
e.R |= REACTIVE_DIRTY;
|
|
84
|
-
if (dirtyQueue.
|
|
84
|
+
if (dirtyQueue.Ue > e.me) dirtyQueue.Ue = e.me;
|
|
85
85
|
insertIntoHeap(e, dirtyQueue);
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
releaseSubtree(t);
|
|
89
|
-
t = t.
|
|
89
|
+
t = t.ge;
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
|
|
@@ -98,7 +98,7 @@ function clearSnapshots() {
|
|
|
98
98
|
// `!== undefined` — assign, don't `delete`: deleting a field pushes the
|
|
99
99
|
// object to dictionary mode for every later read of every field.
|
|
100
100
|
const t = e.m;
|
|
101
|
-
if (t != null) t.
|
|
101
|
+
if (t != null) t.Le = undefined;
|
|
102
102
|
}
|
|
103
103
|
snapshotSources = null;
|
|
104
104
|
}
|
|
@@ -108,7 +108,7 @@ function clearSnapshots() {
|
|
|
108
108
|
function recompute(e, t = false) {
|
|
109
109
|
// §12d: any recompute can clean a marked subscriber — invalidate skips.
|
|
110
110
|
bumpNotifyEpoch();
|
|
111
|
-
const n = e.
|
|
111
|
+
const n = e.u;
|
|
112
112
|
// Lanes (plan sec. 28): the seat of a pass is its node's. A node carrying a lane's
|
|
113
113
|
// derived value runs as the lane's whoever dirtied it — a sync write, a
|
|
114
114
|
// boundary reset, a frame rerun — so its children and its result are the
|
|
@@ -125,14 +125,14 @@ function recompute(e, t = false) {
|
|
|
125
125
|
// Restored at the end, after this pass's staging and runs have been
|
|
126
126
|
// routed.
|
|
127
127
|
const E = passLane;
|
|
128
|
-
setPassLane((e.R & REACTIVE_LANE_DIRTY || (e.
|
|
129
|
-
t && E?._parent.
|
|
128
|
+
setPassLane((e.R & REACTIVE_LANE_DIRTY || (e.i & (CONFIG_OVERRIDE | CONFIG_GUESS)) === CONFIG_OVERRIDE) && e.m?.He?.B ? e.m.He : // Loose `!=`: false with no creator lane (undefined == null).
|
|
129
|
+
t && E?._parent.xe != E && (creatorPass(context)?.R ?? 0) & REACTIVE_RECOMPUTING_DEPS ? E : null);
|
|
130
130
|
// CARVE 2: lane posture resolution (OPTIMISTIC_DIRTY, derived-override
|
|
131
131
|
// re-derivation, lane adoption through deps) went with the optimistic engine.
|
|
132
132
|
if (!t) {
|
|
133
133
|
deleteFromHeap(e, dirtyQueue);
|
|
134
134
|
if (e.m !== null) {
|
|
135
|
-
e.m.
|
|
135
|
+
e.m.Te = null;
|
|
136
136
|
// Supersede is where an iterator flight dies (#3122): close it now.
|
|
137
137
|
// Idempotent with the cleanup-channel close.
|
|
138
138
|
releaseFlightTeardown(e);
|
|
@@ -144,23 +144,30 @@ function recompute(e, t = false) {
|
|
|
144
144
|
// joins all the same. Not a render effect (rule 3): it is the frame.
|
|
145
145
|
// Re-run outside its transaction's flush it publishes mainline — the
|
|
146
146
|
// value it staged there is superseded (`frameRead` remembers it for the
|
|
147
|
-
// landing). A frame born into the transaction (
|
|
148
|
-
// A29
|
|
149
|
-
// swap (ruling
|
|
150
|
-
|
|
147
|
+
// landing). A frame born into the transaction (`bornInto`: never run,
|
|
148
|
+
// A29, or mounted by a pass of it still awaiting its commit, #4010) keeps
|
|
149
|
+
// staging: live writes keep it current, it effects at the swap (ruling
|
|
150
|
+
// A).
|
|
151
|
+
// A pending flight is the transition's own work (#3884). A probe on an
|
|
152
|
+
// earlier pass left CONFIG_VERDICT, and a frame read here would serve the
|
|
153
|
+
// committed screen, settle the flight and drop the hold — a superseding
|
|
154
|
+
// write's verdict would depend on that probe. Cleared for this pass; a
|
|
155
|
+
// probe in the body re-stamps it after the plain reads.
|
|
156
|
+
if (e.S & STATUS_PENDING) e.i &= ~CONFIG_VERDICT;
|
|
157
|
+
if (e.i & CONFIG_HELD) {
|
|
151
158
|
const t = txOf(e);
|
|
152
159
|
// Held by a blocked lane: the frame joins nothing — a lane never holds
|
|
153
160
|
// a sync write (#3460). The pass is the lane's if it reads the lane's
|
|
154
161
|
// world (`read`), and has left it otherwise.
|
|
155
|
-
if (t.
|
|
162
|
+
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)) {
|
|
156
163
|
// Published mainline, it is not held: the frame this pass builds is
|
|
157
164
|
// replaced on the spot (#3404) — this flush's commit retires the
|
|
158
165
|
// committed frame the held pass had parked (`commitPendingNode`),
|
|
159
166
|
// after the new frame's first runs, and the held pass's own
|
|
160
167
|
// children die below (`disposeChildren`). The landing re-derives a
|
|
161
168
|
// fresh held frame for the reveal (`_reruns`).
|
|
162
|
-
e.
|
|
163
|
-
if (n === EFFECT_RENDER) e.
|
|
169
|
+
e.Oe = NOT_PENDING;
|
|
170
|
+
if (n === EFFECT_RENDER) e.i &= ~CONFIG_HELD;
|
|
164
171
|
}
|
|
165
172
|
}
|
|
166
173
|
// The previous pass's frame. If that pass is still awaiting its commit
|
|
@@ -168,14 +175,14 @@ function recompute(e, t = false) {
|
|
|
168
175
|
// frame parked before it stays parked. Otherwise they ARE the frame:
|
|
169
176
|
// park them until this pass commits. Tracked effects run after the seam
|
|
170
177
|
// and own nothing to park.
|
|
171
|
-
if (n === EFFECT_TRACKED || e.
|
|
178
|
+
if (n === EFFECT_TRACKED || e.i & CONFIG_STAGED) disposeChildren(e); else if (e.Ve !== null || e.Re !== null) parkChildren(e);
|
|
172
179
|
}
|
|
173
|
-
const u = !!(e.
|
|
180
|
+
const u = !!(e.S & STATUS_UNINITIALIZED);
|
|
174
181
|
// Capture both error and pending status before the compute clears them.
|
|
175
182
|
// A conditional can drop its pending source and recover to an unchanged
|
|
176
183
|
// value, leaving blocked dependents outside that source’s settle walk.
|
|
177
|
-
const s = e.
|
|
178
|
-
const i = (e.
|
|
184
|
+
const s = e.S & STATUS_ERROR ? e.m?.F : undefined;
|
|
185
|
+
const i = (e.S & STATUS_PENDING) !== 0;
|
|
179
186
|
const l = i ? e.m?.Se : undefined;
|
|
180
187
|
// Pending SOURCE-hood, captured before the compute clears status: a node
|
|
181
188
|
// whose own flight parked dependents self-registers in _pendingSources
|
|
@@ -183,60 +190,60 @@ function recompute(e, t = false) {
|
|
|
183
190
|
// settles synchronously, those dependents settle HERE — asyncWrite's
|
|
184
191
|
// settlePendingSource walk never runs for a landing that was preempted
|
|
185
192
|
// (#3181).
|
|
186
|
-
const
|
|
193
|
+
const o = e.m?.Se?.has(e);
|
|
187
194
|
// A19 exc. 2: a `refresh()` re-asks the question already answered — quiet
|
|
188
195
|
// for the verdict — unless the question in flight is a new one, which the
|
|
189
196
|
// re-ask does not launder (a poll during a quiet confirm stays quiet).
|
|
190
|
-
const
|
|
197
|
+
const _ = (e.R & REACTIVE_REASK) !== 0 && !(e.S & STATUS_PENDING && e.m !== null && !e.m.ke);
|
|
191
198
|
const r = context;
|
|
192
199
|
context = e;
|
|
193
|
-
e.
|
|
194
|
-
e.
|
|
200
|
+
e.G = null;
|
|
201
|
+
e.pe++;
|
|
195
202
|
// REACTIVE_ZOMBIE is position: it says the node sits on its owner's parked
|
|
196
203
|
// frame, and the splice/reawaken paths key off it. A zombie reruns for
|
|
197
204
|
// live writes until the commit that disposes it, so every per-pass wipe
|
|
198
205
|
// — here, the finally below, updateIfNecessary — carries it (#3543).
|
|
199
206
|
e.R = REACTIVE_RECOMPUTING_DEPS | e.R & REACTIVE_ZOMBIE;
|
|
200
|
-
e.
|
|
201
|
-
// The pass's previous value: its staging, else
|
|
202
|
-
//
|
|
203
|
-
let I = e.
|
|
204
|
-
let a = e.
|
|
207
|
+
e.Ie = clock;
|
|
208
|
+
// The pass's previous value: its staging, else a lane node's lane value
|
|
209
|
+
// when it has one (lanes.ts), else the committed one.
|
|
210
|
+
let I = e.Oe !== NOT_PENDING ? e.Oe : e.i & CONFIG_OVERRIDE && e.m.B !== NOT_PENDING ? e.m.B : e.Ae;
|
|
211
|
+
let a = e.me;
|
|
205
212
|
let N = false;
|
|
206
213
|
// L2: did this pass read the future (REACTIVE_JOINED, set by read)?
|
|
207
214
|
let f = false;
|
|
208
215
|
let T = tracking;
|
|
209
216
|
tracking = true;
|
|
210
217
|
try {
|
|
211
|
-
if (!false && e.
|
|
212
|
-
I = e.
|
|
213
|
-
if (e.m !== null) e.m.
|
|
214
|
-
e.
|
|
218
|
+
if (!false && e.i & CONFIG_SYNC) {
|
|
219
|
+
I = e.ee(I);
|
|
220
|
+
if (e.m !== null) e.m.Te = null;
|
|
221
|
+
e.De = false;
|
|
215
222
|
} else {
|
|
216
223
|
// A projection's body self-registers its flight through handleAsync
|
|
217
224
|
// (with the commit as setter): its undefined return is not a sync
|
|
218
225
|
// answer, and the outer handleAsync must not clobber the registration.
|
|
219
|
-
const t = e.m?.
|
|
220
|
-
const n = e.
|
|
226
|
+
const t = e.m?.Te;
|
|
227
|
+
const n = e.ee(I);
|
|
221
228
|
const E = typeof n === "object" && n !== null;
|
|
222
|
-
const u = e.m?.
|
|
229
|
+
const u = e.m?.Te !== t;
|
|
223
230
|
I = u || !E ? n : handleAsync(e, n);
|
|
224
231
|
if (!u && !E) {
|
|
225
|
-
if (e.m !== null) e.m.
|
|
232
|
+
if (e.m !== null) e.m.Te = null;
|
|
226
233
|
// A sync (non-object) return is the first real answer; async-shaped
|
|
227
234
|
// results clear inside handleAsync at their own landing points, and a
|
|
228
235
|
// self-registered flight clears when its own handleAsync lands.
|
|
229
|
-
e.
|
|
236
|
+
e.De = false;
|
|
230
237
|
}
|
|
231
238
|
}
|
|
232
239
|
// On a status-free node clearStatus is a guaranteed no-op: every field
|
|
233
240
|
// its body gates on is either _statusFlags or lives in the cold
|
|
234
241
|
// extension — no extension, no status to clear. (_x from an unrelated
|
|
235
242
|
// installer just makes clearStatus a cheap re-verified no-op.)
|
|
236
|
-
if (e.
|
|
243
|
+
if (e.S !== 0 || e.m !== null) clearStatus(e, t);
|
|
237
244
|
} catch (t) {
|
|
238
245
|
const n = t instanceof NotReadyError;
|
|
239
|
-
if (n && e.
|
|
246
|
+
if (n && e.De) {
|
|
240
247
|
// Loading window with an unready sync dependency: register for the
|
|
241
248
|
// source's settle (the settlePendingSource walk runs off
|
|
242
249
|
// _pendingSources + _blocked alone) but take NO read-visible pending
|
|
@@ -246,11 +253,11 @@ function recompute(e, t = false) {
|
|
|
246
253
|
// retry can actually run.
|
|
247
254
|
parkLoadingWindow(e, t);
|
|
248
255
|
} else {
|
|
249
|
-
if (n) ext(e).
|
|
256
|
+
if (n) ext(e).de = true;
|
|
250
257
|
notifyStatus(e, n ? STATUS_PENDING : STATUS_ERROR, t);
|
|
251
258
|
// The replacement source is fully propagated now. If no new flight
|
|
252
259
|
// re-owned self, retire the superseded flight and its dependent copies.
|
|
253
|
-
if (n &&
|
|
260
|
+
if (n && o && !e.m?.Te) settlePendingSource(e);
|
|
254
261
|
// A re-park drops what the earlier pass carried (#3456): a source this
|
|
255
262
|
// pass no longer reaches — its branch switched, or a fresh flight
|
|
256
263
|
// replaced the inputs' pending with its own — stays copied onto
|
|
@@ -272,7 +279,7 @@ function recompute(e, t = false) {
|
|
|
272
279
|
f = (e.R & REACTIVE_JOINED) !== 0;
|
|
273
280
|
// A verdict reader is one for as long as it probes: a pass that entered
|
|
274
281
|
// no window (REACTIVE_PROBED) is an ordinary derivation again.
|
|
275
|
-
if (!(e.R & REACTIVE_PROBED)) e.
|
|
282
|
+
if (!(e.R & REACTIVE_PROBED)) e.i &= ~CONFIG_VERDICT;
|
|
276
283
|
// REACTIVE_DISPOSED survives too (#3621): the pass may have disposed its
|
|
277
284
|
// own owner (a memo calling its root's `dispose()`, a cleanup doing so
|
|
278
285
|
// #3601/#3606), and `disposeChildren` set the flag on this node
|
|
@@ -285,7 +292,7 @@ function recompute(e, t = false) {
|
|
|
285
292
|
// A19 exc. 2: a pass that went pending on a `refresh()` re-asks the
|
|
286
293
|
// question already answered — quiet for the verdict; any other pending
|
|
287
294
|
// pass is a new question. Settled, there is nothing to classify.
|
|
288
|
-
if (e.m !== null) e.m.
|
|
295
|
+
if (e.m !== null) e.m.ke = _ && (e.S & STATUS_PENDING) !== 0;
|
|
289
296
|
}
|
|
290
297
|
// A node that died during its own pass (#3621) is dead at the end of it,
|
|
291
298
|
// and the pass is void. Its owner's teardown already unlinked its deps,
|
|
@@ -300,7 +307,7 @@ function recompute(e, t = false) {
|
|
|
300
307
|
// attribution frame opened at the top is still closed.
|
|
301
308
|
if (e.R & REACTIVE_DISPOSED) {
|
|
302
309
|
clearDeps(e);
|
|
303
|
-
if (e.m !== null) e.m.
|
|
310
|
+
if (e.m !== null) e.m.Te = null;
|
|
304
311
|
setPassLane(E);
|
|
305
312
|
return;
|
|
306
313
|
}
|
|
@@ -312,19 +319,19 @@ function recompute(e, t = false) {
|
|
|
312
319
|
let S = passLane;
|
|
313
320
|
// Listed before its staging, a pending pass included (the lane's own
|
|
314
321
|
// flight is the lane's); false: the pass left the lane (lanes.ts).
|
|
315
|
-
const c = !!e.m?.
|
|
322
|
+
const c = !!e.m?.F;
|
|
316
323
|
if (S !== null) {
|
|
317
|
-
if (GlobalQueue.
|
|
324
|
+
if (GlobalQueue.W(e, S, t, c)) setPassLane(S); else S = null;
|
|
318
325
|
}
|
|
319
|
-
if (!e.m?.
|
|
326
|
+
if (!e.m?.F) {
|
|
320
327
|
// INV-11 (#3330): the equality gate compares against the slot this run
|
|
321
328
|
// publishes to — the staged `_pendingValue` when staged; a lane node's
|
|
322
329
|
// lane value (a node entering the lane this pass has none yet — its
|
|
323
330
|
// committed value, `laneStage` having voided a frame staging).
|
|
324
|
-
const i = e.
|
|
325
|
-
let
|
|
331
|
+
const i = e.Oe !== NOT_PENDING ? e.Oe : e.i & CONFIG_OVERRIDE && e.m.B !== NOT_PENDING ? e.m.B : e.Ae;
|
|
332
|
+
let _ = false;
|
|
326
333
|
try {
|
|
327
|
-
|
|
334
|
+
_ = !n && u || !e.ye || !e.ye(i, I);
|
|
328
335
|
} catch (t) {
|
|
329
336
|
// A throwing user comparator is an error of this node's computation.
|
|
330
337
|
// Route it through the same status path as a compute-phase throw so
|
|
@@ -337,19 +344,19 @@ function recompute(e, t = false) {
|
|
|
337
344
|
// its runner — happen here instead. `!create` matches the previous `initialized`
|
|
338
345
|
// gate: the explicit recompute(node, true) inside effect() does not enqueue, so
|
|
339
346
|
// effect() can call its runner synchronously for the first run.
|
|
340
|
-
if (n &&
|
|
347
|
+
if (n && _ &&
|
|
341
348
|
// A stale reader of a lane (REACTIVE_SCREEN_READ) that computed what it
|
|
342
349
|
// last applied owes no run (lanes.ts).
|
|
343
|
-
!(e.R & REACTIVE_SCREEN_READ && !e.
|
|
350
|
+
!(e.R & REACTIVE_SCREEN_READ && !e.Ge && Object.is(e.be, I)) &&
|
|
344
351
|
// A re-staging pass owes no run: the commit replays the effect (#3802).
|
|
345
|
-
e.
|
|
346
|
-
e.
|
|
352
|
+
e.Oe === NOT_PENDING) {
|
|
353
|
+
e.Ge = !e.m?.F;
|
|
347
354
|
// Reuse one bound runner per effect — runEffect no-ops on a stale
|
|
348
355
|
// `_modified`, so re-enqueueing the same function is harmless.
|
|
349
356
|
// Lane work's run is the lane's (released at its reveal) — a first
|
|
350
357
|
// pass under a lane too: `effect()` skips the synchronous first run of
|
|
351
358
|
// a lane's node.
|
|
352
|
-
if (!t || S !== null) globalQueue.enqueue(n, e.
|
|
359
|
+
if (!t || S !== null) globalQueue.enqueue(n, e.Y ??= GlobalQueue.q.bind(null, e), S);
|
|
353
360
|
}
|
|
354
361
|
// Lanes. A written guess's own pass from the frame: its source
|
|
355
362
|
// recomputed it — the truth (A18: "the source is whatever recomputes the
|
|
@@ -357,7 +364,7 @@ function recompute(e, t = false) {
|
|
|
357
364
|
// confirm is a landing too — the lane's end commits it) and notifies
|
|
358
365
|
// only a correction: an equal truth re-runs nothing (lanes.ts). (As the
|
|
359
366
|
// lane's own work the lane judged it — `laneStage`.)
|
|
360
|
-
if (!t && S === null && e.
|
|
367
|
+
if (!t && S === null && e.i & CONFIG_GUESS && GlobalQueue.We(e, I, c)) ; else if (c) ; else if (_) {
|
|
361
368
|
// L2: a first pass is born held — staged, committed with the
|
|
362
369
|
// transaction, its effect's first run the landing's (A29; `effect()`
|
|
363
370
|
// skips the synchronous first run on a staged value) — when the flush
|
|
@@ -381,8 +388,8 @@ function recompute(e, t = false) {
|
|
|
381
388
|
// effect's value slot is private: the run the lane holds is what
|
|
382
389
|
// shows it — unless the effect was born held, whose first run is the
|
|
383
390
|
// commit's (A29): it re-stages, as below.
|
|
384
|
-
if (n && e.
|
|
385
|
-
} else if (t ? !((flushTransaction !== null || passTx !== null) && (f || E && e.R & REACTIVE_STAGED_READ || (creatorPass(r)?.R ?? 0) & REACTIVE_JOINED)) : n && e.
|
|
391
|
+
if (n && e.Oe === NOT_PENDING) e.Ae = I; else if (n || S.Ze) e.Oe = I; else e.m.B = I;
|
|
392
|
+
} else if (t ? !((flushTransaction !== null || passTx !== null) && (f || E && e.R & REACTIVE_STAGED_READ || (creatorPass(r)?.R ?? 0) & REACTIVE_JOINED)) : n && e.Oe === NOT_PENDING) {
|
|
386
393
|
// A first pass publishes directly. So does an effect: its value slot
|
|
387
394
|
// is private (only its own run reads it), and the staging round-trip
|
|
388
395
|
// (queuePendingNode + commitPendingNodes) paid per effect on the
|
|
@@ -393,9 +400,9 @@ function recompute(e, t = false) {
|
|
|
393
400
|
// sources commit) must see the fresh value while PLAIN reads stay
|
|
394
401
|
// committed until flush (#3009 purity). The pending round-trip is
|
|
395
402
|
// that separation; it cannot be skipped on any path a pull can reach.
|
|
396
|
-
e.pe = I;
|
|
397
|
-
} else {
|
|
398
403
|
e.Ae = I;
|
|
404
|
+
} else {
|
|
405
|
+
e.Oe = I;
|
|
399
406
|
// Born held: in the transaction from birth, so a read of it this
|
|
400
407
|
// tick is a read of its world (the mount-during-a-hold case), not
|
|
401
408
|
// only after the seam parks it.
|
|
@@ -405,7 +412,11 @@ function recompute(e, t = false) {
|
|
|
405
412
|
// (`commitPendingNode` initializes it) — every reader of it
|
|
406
413
|
// derives from the future (`read`), an untracked one throws
|
|
407
414
|
// (A19 exc. 1).
|
|
408
|
-
e.
|
|
415
|
+
e.S |= STATUS_UNINITIALIZED;
|
|
416
|
+
// A boundary that has not shown content catches it: its fallback
|
|
417
|
+
// waits for the commit (#3540). (The pass's own status clear above
|
|
418
|
+
// dropped what the read recorded.)
|
|
419
|
+
GlobalQueue.fe?.(e);
|
|
409
420
|
}
|
|
410
421
|
}
|
|
411
422
|
// insertSubs only walks _subs (no scheduling of its own), so a
|
|
@@ -415,15 +426,15 @@ function recompute(e, t = false) {
|
|
|
415
426
|
// Lane work re-staged: the lane's members re-derive as its work.
|
|
416
427
|
if (S !== null) laneDirty(e, S);
|
|
417
428
|
}
|
|
418
|
-
} else if (e.
|
|
419
|
-
for (let t = e.t; t !== null; t = t.
|
|
429
|
+
} else if (e.me != a) {
|
|
430
|
+
for (let t = e.t; t !== null; t = t.o) insertIntoHeapHeight(t.l, dirtyQueue);
|
|
420
431
|
}
|
|
421
432
|
// Silent recovery: errored → unchanged value fires no notification, but
|
|
422
433
|
// dependents still holding the propagated error consumed their dirty flag
|
|
423
434
|
// in an errored run and may sit on stale commits (#2949). Changed-value
|
|
424
435
|
// recoveries ride insertSubs above; a comparator throw re-errored the node
|
|
425
436
|
// (el._x?._error re-set), so this only runs on a genuinely clean recovery.
|
|
426
|
-
if (!
|
|
437
|
+
if (!_ && !e.m?.F) {
|
|
427
438
|
if (s !== undefined) settleErroredDependents(e, s);
|
|
428
439
|
// Self-registration (this node's own superseded flight) is the #3181
|
|
429
440
|
// sweep's business below — retiring it here too would walk twice.
|
|
@@ -432,14 +443,17 @@ function recompute(e, t = false) {
|
|
|
432
443
|
// #3181: a synchronous settle supersedes the old landing callback, so
|
|
433
444
|
// recompute owns its pending-source sweep. An uninitialized node without
|
|
434
445
|
// a replacement source still has no truth to reveal and must stay parked.
|
|
435
|
-
if (
|
|
446
|
+
if (o && !(e.S & (STATUS_PENDING | STATUS_UNINITIALIZED))) settlePendingSource(e);
|
|
436
447
|
}
|
|
437
448
|
// A first pass that went pending where the born-held arm would have held
|
|
438
449
|
// its value (it read a held node, or a verdict lane's mount read a staging,
|
|
439
|
-
// #3851) is
|
|
440
|
-
//
|
|
441
|
-
//
|
|
442
|
-
if (t && S === null && (f || E && e.R & REACTIVE_STAGED_READ) && e.
|
|
450
|
+
// #3851) is held like it: its answer is the hold's, and the hold waits
|
|
451
|
+
// for it (A29, #3800) — unless a boundary that has not shown content
|
|
452
|
+
// catches it: then it is the boundary's, and the hold does not wait (#3540).
|
|
453
|
+
if (t && S === null && (f || E && e.R & REACTIVE_STAGED_READ) && e.S & STATUS_PENDING) {
|
|
454
|
+
const t = flushTransaction ?? passTx;
|
|
455
|
+
if (t !== null && !t.B && !GlobalQueue.fe?.(e)) holdNode(e, t);
|
|
456
|
+
}
|
|
443
457
|
// Dependencies are the committed frame's until it is replaced (A30, #3410):
|
|
444
458
|
// a pass that staged its value leaves the previous pass's tail linked for
|
|
445
459
|
// `commitPendingNode` to trim, so a write to a dependency the committed
|
|
@@ -457,7 +471,7 @@ function recompute(e, t = false) {
|
|
|
457
471
|
// kept at a park — one spurious recompute at most). A creation pass and a
|
|
458
472
|
// tracked effect trim now: their frames are replaceable like a direct
|
|
459
473
|
// commit, and a tracked effect's spurious run would be user-visible.
|
|
460
|
-
if (!e.m?.
|
|
474
|
+
if (!e.m?.F && e.Oe === NOT_PENDING && !(n && e.Ge)) {
|
|
461
475
|
if (t || n === EFFECT_TRACKED) trimStaleDeps(e); else heldTrim(e);
|
|
462
476
|
}
|
|
463
477
|
// A staged value, a parked frame (L2: the commit retires it), or status the
|
|
@@ -465,8 +479,8 @@ function recompute(e, t = false) {
|
|
|
465
479
|
// node for this flush's commit. A first pass queues only when pending or
|
|
466
480
|
// born held: otherwise its value published directly. A queued pass's
|
|
467
481
|
// children are uncommitted (CONFIG_STAGED) until that commit.
|
|
468
|
-
if (e.
|
|
469
|
-
e.
|
|
482
|
+
if (e.Oe !== NOT_PENDING || e.m !== null && (e.m.Qe !== null || e.m.ve !== null) || (e.S & (STATUS_PENDING | STATUS_UNINITIALIZED)) !== 0 && (!t || (e.S & STATUS_PENDING) !== 0)) {
|
|
483
|
+
e.i |= CONFIG_STAGED;
|
|
470
484
|
// Lane work is the lane's to reveal (its seam), not this flush's commit.
|
|
471
485
|
if (S === null) queuePendingNode(e);
|
|
472
486
|
}
|
|
@@ -491,9 +505,9 @@ function updateIfNecessary(e) {
|
|
|
491
505
|
// would resurrect it (#2983) — readers serve its last value.
|
|
492
506
|
if (e.R & (REACTIVE_RECOMPUTING_DEPS | REACTIVE_DISPOSED)) return;
|
|
493
507
|
if (e.R & REACTIVE_CHECK) {
|
|
494
|
-
for (let t = e.
|
|
508
|
+
for (let t = e.p; t; t = t._) {
|
|
495
509
|
const n = t.I;
|
|
496
|
-
if (n.
|
|
510
|
+
if (n.ee) {
|
|
497
511
|
updateIfNecessary(n);
|
|
498
512
|
}
|
|
499
513
|
if (e.R & REACTIVE_DIRTY) {
|
|
@@ -501,7 +515,7 @@ function updateIfNecessary(e) {
|
|
|
501
515
|
}
|
|
502
516
|
}
|
|
503
517
|
}
|
|
504
|
-
if (e.R & REACTIVE_DIRTY || e.m?.
|
|
518
|
+
if (e.R & REACTIVE_DIRTY || e.m?.F && e.Ie < clock && !e.m?.Te) {
|
|
505
519
|
recompute(e);
|
|
506
520
|
}
|
|
507
521
|
// The guard above refused an already-disposed node; the recompute it just
|
|
@@ -531,38 +545,38 @@ function computed(e, t) {
|
|
|
531
545
|
// plus `_name`.
|
|
532
546
|
const u = {
|
|
533
547
|
id: inheritId(t, n, context),
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
548
|
+
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) | (t?.Me ?? 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
|
|
549
|
+
ye: t?.equals ?? isEqual,
|
|
550
|
+
Re: null,
|
|
551
|
+
D: context?.D ?? defaultContext,
|
|
552
|
+
Be: 0,
|
|
553
|
+
ee: e,
|
|
554
|
+
Ae: E ? t.loadingValue : undefined,
|
|
555
|
+
me: 0,
|
|
542
556
|
je: undefined,
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
557
|
+
we: null,
|
|
558
|
+
p: null,
|
|
559
|
+
G: null,
|
|
560
|
+
pe: 0,
|
|
547
561
|
t: null,
|
|
548
|
-
|
|
562
|
+
Ye: null,
|
|
549
563
|
_parent: context,
|
|
550
|
-
me: null,
|
|
551
|
-
Je: null,
|
|
552
564
|
ge: null,
|
|
565
|
+
Ke: null,
|
|
566
|
+
Ve: null,
|
|
553
567
|
R: t?.lazy ? REACTIVE_LAZY : REACTIVE_NONE,
|
|
554
568
|
// A loadingValue node is born committed: commit #0 is already in _value.
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
569
|
+
S: E ? 0 : STATUS_UNINITIALIZED,
|
|
570
|
+
Ie: clock,
|
|
571
|
+
Oe: NOT_PENDING,
|
|
572
|
+
qe: -1,
|
|
573
|
+
De: E,
|
|
560
574
|
// Cold machinery (async slots) lives one hop away in the lazily-allocated extension — the core literal MUST
|
|
561
575
|
// stay under V8's in-object boundary (§12: past ~39 fields every
|
|
562
576
|
// allocation spills to a backing store and creation cost ~4x's).
|
|
563
577
|
m: null
|
|
564
578
|
};
|
|
565
|
-
if (t?.unobserved) ext(u).
|
|
579
|
+
if (t?.unobserved) ext(u).Je = t.unobserved;
|
|
566
580
|
setupComputedNode(u, t);
|
|
567
581
|
return u;
|
|
568
582
|
}
|
|
@@ -572,22 +586,21 @@ function computed(e, t) {
|
|
|
572
586
|
* this; hot paths read `el._x?._field` gated by the _config presence bits.
|
|
573
587
|
* Never call ext() just to store a field's default. */ function ext(e) {
|
|
574
588
|
return e.m ??= {
|
|
575
|
-
De: null,
|
|
576
589
|
Te: null,
|
|
577
|
-
|
|
578
|
-
|
|
590
|
+
Ne: null,
|
|
591
|
+
F: undefined,
|
|
592
|
+
de: undefined,
|
|
579
593
|
Se: undefined,
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
Z: NOT_PENDING,
|
|
594
|
+
Je: undefined,
|
|
595
|
+
Le: undefined,
|
|
596
|
+
Qe: null,
|
|
597
|
+
ve: null,
|
|
598
|
+
He: null,
|
|
599
|
+
ke: false,
|
|
600
|
+
ze: NOT_PENDING,
|
|
601
|
+
Xe: -1,
|
|
602
|
+
_e: 0,
|
|
603
|
+
B: NOT_PENDING,
|
|
591
604
|
T: 0
|
|
592
605
|
};
|
|
593
606
|
}
|
|
@@ -604,7 +617,12 @@ function computed(e, t) {
|
|
|
604
617
|
* the boundary collects it and shows its fallback, and the pass is the
|
|
605
618
|
* boundary's, not the tick's (A29's boundary exemption, #3540). */ function joinPass(e, t) {
|
|
606
619
|
e.R |= REACTIVE_JOINED;
|
|
607
|
-
|
|
620
|
+
// A new memo's first pass is the hold's work (born held) but not the
|
|
621
|
+
// tick's: a render effect that reads it decides — under a boundary that
|
|
622
|
+
// catches it, the boundary's; otherwise the tick joins the hold (#3540:
|
|
623
|
+
// the reader's position, not where the memo was created). Its catch, if
|
|
624
|
+
// any, is at its staging.
|
|
625
|
+
if (t.S & STATUS_UNINITIALIZED || e.u !== EFFECT_RENDER) joinPassTx(txOf(t), !e.u && e.S & STATUS_UNINITIALIZED || GlobalQueue.fe?.(e));
|
|
608
626
|
}
|
|
609
627
|
|
|
610
628
|
/** A15's stale reader (shared-hole and reveal corollaries): a render effect
|
|
@@ -613,11 +631,11 @@ function computed(e, t) {
|
|
|
613
631
|
* re-derived after that transaction's landing (`_reruns`,
|
|
614
632
|
* REACTIVE_FRAME_READ) so the frame catches up with it once it is the
|
|
615
633
|
* committed world. Not a frame born into that transaction (held and never
|
|
616
|
-
* committed — created by one of its held passes, A29): that
|
|
617
|
-
* transaction's values and keeps staging. Not of a flight
|
|
618
|
-
* already visible (CONFIG_INPUTS_PUBLISHED, #3305): its
|
|
619
|
-
* would tear against them, so the reveal observes the
|
|
620
|
-
* per pass.
|
|
634
|
+
* committed — created by one of its held passes, A29; `bornInto`): that
|
|
635
|
+
* one reads its transaction's values and keeps staging. Not of a flight
|
|
636
|
+
* whose inputs are already visible (CONFIG_INPUTS_PUBLISHED, #3305): its
|
|
637
|
+
* committed value would tear against them, so the reveal observes the
|
|
638
|
+
* flight instead. Once per pass.
|
|
621
639
|
*
|
|
622
640
|
* Lane work is a stale reader too (maintainer, 2026-10-01: a lane sees the
|
|
623
641
|
* screen plus its own guesses). A held write is held because showing it
|
|
@@ -641,20 +659,28 @@ function computed(e, t) {
|
|
|
641
659
|
// read the screen. Lane work with no committed value yet reads a flight
|
|
642
660
|
// as a mount's memo does: it enters, and the boundary it mounts catches
|
|
643
661
|
// the pending (#3540).
|
|
644
|
-
const n = e.
|
|
645
|
-
if ((passLane === null || e.
|
|
662
|
+
const n = e.i & CONFIG_VERDICT;
|
|
663
|
+
if ((passLane === null || e.S & STATUS_UNINITIALIZED && t.S & STATUS_PENDING) && !n && (e.u !== EFFECT_RENDER || t.i & CONFIG_INPUTS_PUBLISHED)) return false;
|
|
646
664
|
const E = txOf(t);
|
|
647
665
|
// Lane work and verdict readers are never the transaction's pass, whichever
|
|
648
666
|
// flush runs it: a node T holds re-enters T when it re-runs (`recompute`'s
|
|
649
667
|
// head), and the pass may then read a guess and become the lane's.
|
|
650
|
-
if (E === flushTransaction && passLane === null && !n ||
|
|
668
|
+
if (E === flushTransaction && passLane === null && !n || bornInto(e, E)) return false;
|
|
651
669
|
staleReader(e, E);
|
|
652
670
|
return true;
|
|
653
671
|
}
|
|
654
672
|
|
|
673
|
+
/** A node born into transaction `t`: held by it and never committed (A29,
|
|
674
|
+
* ruling A) — uninitialized (born held), or a member of the frame one of its
|
|
675
|
+
* held passes built (CONFIG_HELD_FRAME). Its branch exists only in `t`: it
|
|
676
|
+
* has no screen to read, so it reads `t`'s values, whichever flush runs it,
|
|
677
|
+
* and keeps staging (#4010). */ function bornInto(e, t) {
|
|
678
|
+
return (e.S & STATUS_UNINITIALIZED || e.i & CONFIG_HELD_FRAME) && e.i & CONFIG_HELD && txOf(e) === t;
|
|
679
|
+
}
|
|
680
|
+
|
|
655
681
|
/** The computed whose pass a creation under `owner` belongs to (a root's is
|
|
656
682
|
* its nearest computed ancestor's), or null outside every pass. */ function creatorPass(e) {
|
|
657
|
-
return e !== null && e
|
|
683
|
+
return e !== null && e.$e ? e.et : e;
|
|
658
684
|
}
|
|
659
685
|
|
|
660
686
|
/** L2 — park the previous pass's frame for this node's commit: the children
|
|
@@ -663,11 +689,11 @@ function computed(e, t) {
|
|
|
663
689
|
* the frame that replaces them (`commitPendingNode`) or when the owner does. */ function parkChildren(e) {
|
|
664
690
|
markDisposal(e);
|
|
665
691
|
const t = ext(e);
|
|
666
|
-
t.
|
|
667
|
-
t.
|
|
668
|
-
e.
|
|
669
|
-
e.
|
|
670
|
-
e.
|
|
692
|
+
t.ve = e.Re;
|
|
693
|
+
t.Qe = e.Ve;
|
|
694
|
+
e.Re = null;
|
|
695
|
+
e.Ve = null;
|
|
696
|
+
e.Be = 0;
|
|
671
697
|
}
|
|
672
698
|
|
|
673
699
|
/**
|
|
@@ -684,37 +710,37 @@ function computed(e, t) {
|
|
|
684
710
|
// the options to get it.
|
|
685
711
|
const i = {
|
|
686
712
|
id: inheritId(u, s, context),
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
713
|
+
i: (s ? CONFIG_TRANSPARENT : 0) | (u?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (u?.sync ? CONFIG_SYNC : 0) | (u?.Me ?? 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
|
|
714
|
+
ye: false,
|
|
715
|
+
Re: null,
|
|
716
|
+
D: context?.D ?? defaultContext,
|
|
717
|
+
Be: 0,
|
|
718
|
+
ee: e,
|
|
719
|
+
Ae: undefined,
|
|
720
|
+
me: 0,
|
|
695
721
|
je: undefined,
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
722
|
+
we: null,
|
|
723
|
+
p: null,
|
|
724
|
+
G: null,
|
|
725
|
+
pe: 0,
|
|
700
726
|
t: null,
|
|
701
|
-
|
|
727
|
+
Ye: null,
|
|
702
728
|
_parent: context,
|
|
703
|
-
me: null,
|
|
704
|
-
Je: null,
|
|
705
729
|
ge: null,
|
|
730
|
+
Ke: null,
|
|
731
|
+
Ve: null,
|
|
706
732
|
R: REACTIVE_LAZY,
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
733
|
+
S: STATUS_UNINITIALIZED,
|
|
734
|
+
Ie: clock,
|
|
735
|
+
Oe: NOT_PENDING,
|
|
736
|
+
qe: -1,
|
|
737
|
+
De: false,
|
|
738
|
+
Ge: false,
|
|
739
|
+
be: undefined,
|
|
740
|
+
Et: t,
|
|
741
|
+
ut: n,
|
|
742
|
+
st: undefined,
|
|
743
|
+
u: E,
|
|
718
744
|
m: null
|
|
719
745
|
};
|
|
720
746
|
// Effects dispatch status through the SHARED notifier (statusNotifierOf,
|
|
@@ -722,7 +748,7 @@ function computed(e, t) {
|
|
|
722
748
|
// allocation on EVERY effect at creation (an alloc + 19 field stores,
|
|
723
749
|
// +23% effect creation, caught by the creation benches). Only genuinely
|
|
724
750
|
// per-node channels (boundaries) live on _x.
|
|
725
|
-
if (u?.unobserved) ext(i).
|
|
751
|
+
if (u?.unobserved) ext(i).Je = u.unobserved;
|
|
726
752
|
setupComputedNode(i, lazyOptions);
|
|
727
753
|
return i;
|
|
728
754
|
}
|
|
@@ -743,7 +769,7 @@ function setEffectStatusNotify(e) {
|
|
|
743
769
|
* 0, and only effect literals carry the field) get the shared notifier.
|
|
744
770
|
* Presence doubles as the "display consumer" membership test in the status
|
|
745
771
|
* walks, exactly as the per-node field did when every effect carried one. */ function statusNotifierOf(e) {
|
|
746
|
-
return e.
|
|
772
|
+
return e.u ? effectStatusNotify ?? undefined : undefined;
|
|
747
773
|
}
|
|
748
774
|
|
|
749
775
|
const lazyOptions = {
|
|
@@ -751,16 +777,16 @@ const lazyOptions = {
|
|
|
751
777
|
};
|
|
752
778
|
|
|
753
779
|
function setupComputedNode(e, t) {
|
|
754
|
-
e.
|
|
755
|
-
const n = context
|
|
780
|
+
e.we = e;
|
|
781
|
+
const n = context?.$e ? context.et : context;
|
|
756
782
|
if (context) linkChild(context, e);
|
|
757
|
-
if (n) e.
|
|
758
|
-
GlobalQueue.
|
|
783
|
+
if (n) e.me = n.me + 1;
|
|
784
|
+
GlobalQueue.it?.(e);
|
|
759
785
|
!t?.lazy && recompute(e, true);
|
|
760
786
|
if (snapshotCaptureActive && !t?.lazy) {
|
|
761
|
-
if (!(e.
|
|
762
|
-
ext(e).
|
|
763
|
-
e.
|
|
787
|
+
if (!(e.S & STATUS_PENDING) && !(e.i & CONFIG_NO_SNAPSHOT)) {
|
|
788
|
+
ext(e).Le = e.Ae === undefined ? NO_SNAPSHOT : e.Ae;
|
|
789
|
+
e.i |= CONFIG_HAS_SNAPSHOT;
|
|
764
790
|
snapshotSources.add(e);
|
|
765
791
|
}
|
|
766
792
|
}
|
|
@@ -772,25 +798,25 @@ function signal(e, t) {
|
|
|
772
798
|
// createSignal nodes so ownerPath can locate signal subjects; null here,
|
|
773
799
|
// and staying null on internal signals — one shape either way).
|
|
774
800
|
const n = {
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
801
|
+
ye: t?.equals ?? isEqual,
|
|
802
|
+
i: (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (t?.ae ? CONFIG_NO_SNAPSHOT : 0),
|
|
803
|
+
Ae: e,
|
|
778
804
|
t: null,
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
805
|
+
Ye: null,
|
|
806
|
+
Ie: clock,
|
|
807
|
+
Oe: NOT_PENDING,
|
|
782
808
|
// Signal-literal diet (§12e): NO _time/_fn/_statusFlags slots. Stores
|
|
783
809
|
// materialize one signal per touched leaf, so signal bytes are store
|
|
784
810
|
// bytes. _time is write-only on signals (every read site is computed-
|
|
785
811
|
// typed error-retry gating); _fn/_statusFlags read falsy-identically as
|
|
786
812
|
// missing properties on the shared paths (undefined masks to 0).
|
|
787
|
-
|
|
813
|
+
qe: -1,
|
|
788
814
|
m: null
|
|
789
815
|
};
|
|
790
|
-
if (t?.unobserved) ext(n).
|
|
791
|
-
if (snapshotCaptureActive && !(n.
|
|
792
|
-
ext(n).
|
|
793
|
-
n.
|
|
816
|
+
if (t?.unobserved) ext(n).Je = t.unobserved;
|
|
817
|
+
if (snapshotCaptureActive && !(n.i & CONFIG_NO_SNAPSHOT)) {
|
|
818
|
+
ext(n).Le = e === undefined ? NO_SNAPSHOT : e;
|
|
819
|
+
n.i |= CONFIG_HAS_SNAPSHOT;
|
|
794
820
|
snapshotSources.add(n);
|
|
795
821
|
}
|
|
796
822
|
return n;
|
|
@@ -811,17 +837,17 @@ function isEqual(e, t) {
|
|
|
811
837
|
* the store's notification machinery. The observe literal adds the label
|
|
812
838
|
* slot (the store relabels it `store.<key>` when the engine is installed). */ function slotSignal(e, t, n, E, u) {
|
|
813
839
|
const s = {
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
840
|
+
ye: t,
|
|
841
|
+
i: CONFIG_OWNED_WRITE | CONFIG_SLOT_NODE,
|
|
842
|
+
Ae: e,
|
|
817
843
|
t: null,
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
844
|
+
Ye: null,
|
|
845
|
+
Ie: clock,
|
|
846
|
+
Oe: NOT_PENDING,
|
|
847
|
+
qe: -1,
|
|
822
848
|
m: null,
|
|
823
|
-
|
|
824
|
-
|
|
849
|
+
lt: n,
|
|
850
|
+
ot: E,
|
|
825
851
|
acc: u,
|
|
826
852
|
px: undefined,
|
|
827
853
|
pxv: undefined
|
|
@@ -863,11 +889,11 @@ function setVerdict(e) {
|
|
|
863
889
|
* );
|
|
864
890
|
* ```
|
|
865
891
|
*/ function untrack(e, t) {
|
|
866
|
-
if (GlobalQueue.
|
|
892
|
+
if (GlobalQueue._t === null && !tracking && true) return e();
|
|
867
893
|
const n = tracking;
|
|
868
894
|
tracking = false;
|
|
869
895
|
try {
|
|
870
|
-
if (GlobalQueue.
|
|
896
|
+
if (GlobalQueue._t) return GlobalQueue._t(e);
|
|
871
897
|
return e();
|
|
872
898
|
} finally {
|
|
873
899
|
tracking = n;
|
|
@@ -901,13 +927,13 @@ function setVerdict(e) {
|
|
|
901
927
|
// pay-for-use contract. Owner-lifecycle nodes are dead: recomputing would
|
|
902
928
|
// re-run user code in a torn-down tree (and discard manual writes on
|
|
903
929
|
// derived-writable signals), so reads return the last committed value.
|
|
904
|
-
if (e.
|
|
930
|
+
if (e.i & CONFIG_AUTO_DISPOSE) {
|
|
905
931
|
const t = e._parent;
|
|
906
932
|
if (t !== null) {
|
|
907
933
|
// A dormant node was off the chain when its owner died, so the strip
|
|
908
934
|
// in disposeChildren missed it: freeze here instead (#3024).
|
|
909
935
|
if (t.R & REACTIVE_DISPOSED) {
|
|
910
|
-
e.
|
|
936
|
+
e.i &= ~CONFIG_AUTO_DISPOSE;
|
|
911
937
|
return;
|
|
912
938
|
}
|
|
913
939
|
// A zombie never left its chain (the parked frame is drained whole,
|
|
@@ -935,7 +961,7 @@ function setVerdict(e) {
|
|
|
935
961
|
* never the graph — A32).
|
|
936
962
|
* False means the reader derives from the staged value.
|
|
937
963
|
*/ function readerSeesCommitted(e, t) {
|
|
938
|
-
return !!(!t || e.
|
|
964
|
+
return !!(!t || e.Oe === NOT_PENDING || t.i & CONFIG_CHILDREN_FORBIDDEN);
|
|
939
965
|
}
|
|
940
966
|
|
|
941
967
|
/** A28 — set when a node is staged (queuePendingNode) OUTSIDE a flush;
|
|
@@ -956,21 +982,21 @@ function markUnflushedStaged() {
|
|
|
956
982
|
* is unflushed. Exempt: promoted writes (A28 (4): a write issued inside a
|
|
957
983
|
* recompute is promoted at that recompute's end — boundary and loading
|
|
958
984
|
* machinery, signals declared for in-computation writes). */ function unflushedValue(e) {
|
|
959
|
-
if (globalQueue.
|
|
985
|
+
if (globalQueue.X || e.Oe === NOT_PENDING || e.i & CONFIG_PROMOTED) return NOT_PENDING;
|
|
960
986
|
// A held node (L2) carries a staged value between flushes too, but it was
|
|
961
987
|
// flushed — and parked. Not an unflushed write — unless rewritten since:
|
|
962
988
|
// then the flushed staging is what the world sees until the next flush.
|
|
963
|
-
if (e.
|
|
964
|
-
return e.
|
|
989
|
+
if (e.i & CONFIG_HELD) return e.m.Xe === clock ? e.m.ze : NOT_PENDING;
|
|
990
|
+
return e.Ae;
|
|
965
991
|
}
|
|
966
992
|
|
|
967
993
|
/** A28: a held node rewritten outside a flush keeps the staging the last
|
|
968
994
|
* flush left (`_flushed`) until the flush that carries the rewrite — the
|
|
969
995
|
* stash is stamped with the clock, which that flush advances. */ function stashFlushed(e) {
|
|
970
996
|
const t = e.m;
|
|
971
|
-
if (t.
|
|
972
|
-
t
|
|
973
|
-
t.
|
|
997
|
+
if (t.Xe !== clock) {
|
|
998
|
+
t.ze = e.Oe;
|
|
999
|
+
t.Xe = clock;
|
|
974
1000
|
unflushedStaged = true;
|
|
975
1001
|
}
|
|
976
1002
|
}
|
|
@@ -990,7 +1016,7 @@ function resyncUnflushedCompanions() {
|
|
|
990
1016
|
unflushedStaged = false;
|
|
991
1017
|
// Length-guarded: the common flush has nothing here and allocates nothing.
|
|
992
1018
|
if (promotedWrites.length !== 0) {
|
|
993
|
-
for (const e of promotedWrites) e.
|
|
1019
|
+
for (const e of promotedWrites) e.i &= ~CONFIG_PROMOTED;
|
|
994
1020
|
promotedWrites.length = 0;
|
|
995
1021
|
}
|
|
996
1022
|
}
|
|
@@ -998,7 +1024,7 @@ function resyncUnflushedCompanions() {
|
|
|
998
1024
|
/** A tracked read's pull of a computed: bring it current when the flush has
|
|
999
1025
|
* reached its height (or the reader is a fresh-pull waiter), and sit the
|
|
1000
1026
|
* reader above it. */ function pullComputed(e, t) {
|
|
1001
|
-
if (e.
|
|
1027
|
+
if (e.me >= dirtyQueue.Ue) {
|
|
1002
1028
|
markNode(t);
|
|
1003
1029
|
markHeap(dirtyQueue);
|
|
1004
1030
|
updateIfNecessary(e);
|
|
@@ -1009,27 +1035,27 @@ function resyncUnflushedCompanions() {
|
|
|
1009
1035
|
// never read the PRE-re-ask value as settled. Self-guarded: a clean
|
|
1010
1036
|
// node no-ops and updateIfNecessary refuses disposed nodes (#2983) —
|
|
1011
1037
|
// a dead target serves its last value, which is already quiescent.
|
|
1012
|
-
else if (t.
|
|
1013
|
-
const n = e.
|
|
1038
|
+
else if (t.i & CONFIG_FRESH_READ) updateIfNecessary(e);
|
|
1039
|
+
const n = e.me;
|
|
1014
1040
|
// parent check is shallow, might need to be recursive
|
|
1015
|
-
if (n >= t.
|
|
1041
|
+
if (n >= t.me && e._parent !== t) t.me = n + 1;
|
|
1016
1042
|
}
|
|
1017
1043
|
|
|
1018
1044
|
function read(e) {
|
|
1019
1045
|
let t = context;
|
|
1020
|
-
if (t
|
|
1046
|
+
if (t?.$e) t = t.et;
|
|
1021
1047
|
const n = e;
|
|
1022
1048
|
// CARVE 1: `owner` was `el._firewall || el` — a store leaf's status,
|
|
1023
1049
|
// height and error were read off its projection computed. (Typed loosely
|
|
1024
1050
|
// as the `||` expression was: a signal's `_statusFlags`/`_height` read as
|
|
1025
1051
|
// undefined and mask to 0 on every site below.)
|
|
1026
1052
|
const E = e;
|
|
1027
|
-
if (typeof n.
|
|
1053
|
+
if (typeof n.ee === "function") prepareComputed(e);
|
|
1028
1054
|
// The verdict windows (`latest`, `isPending`; verdict.ts): one cold path,
|
|
1029
1055
|
// off this one, present only while a window is open.
|
|
1030
1056
|
if (verdict !== null) return verdict(e, t);
|
|
1031
|
-
if (!n.
|
|
1032
|
-
!(e.
|
|
1057
|
+
if (!n.ee && e.m?.Le === undefined && !snapshotCaptureActive && (!unflushedStaged || e.Oe === NOT_PENDING) && // A28, see readNodeFast
|
|
1058
|
+
!(e.i & CONFIG_OVERRIDE) && // lanes: the slow path's one arm
|
|
1033
1059
|
true) {
|
|
1034
1060
|
if (t) {
|
|
1035
1061
|
if (tracking) link(e, t);
|
|
@@ -1043,17 +1069,17 @@ function read(e) {
|
|
|
1043
1069
|
// stays out too (rule 3, `frameRead`). The pass remembers it read the
|
|
1044
1070
|
// future (REACTIVE_JOINED): a first pass that did is born held (A29),
|
|
1045
1071
|
// wherever it was created. Lane work sees the screen (`frameRead`).
|
|
1046
|
-
if (e.
|
|
1047
|
-
if (frameRead(t, e)) return e.
|
|
1072
|
+
if (e.i & CONFIG_HELD && !(t.i & CONFIG_CHILDREN_FORBIDDEN)) {
|
|
1073
|
+
if (frameRead(t, e)) return e.Ae;
|
|
1048
1074
|
joinPass(t, e);
|
|
1049
1075
|
}
|
|
1050
1076
|
}
|
|
1051
1077
|
// Committed visibility for untracked and children-forbidden readers.
|
|
1052
|
-
if (!t || e.
|
|
1078
|
+
if (!t || e.Oe === NOT_PENDING || t.i & CONFIG_CHILDREN_FORBIDDEN) return e.Ae;
|
|
1053
1079
|
// (A plain signal is never uninitialized — the carve-out is `serve`'s.)
|
|
1054
|
-
if (t.
|
|
1080
|
+
if (t.i & CONFIG_VERDICT && stagedScreen(t)) return e.Ae;
|
|
1055
1081
|
stagedRead(t);
|
|
1056
|
-
return e.
|
|
1082
|
+
return e.Oe;
|
|
1057
1083
|
}
|
|
1058
1084
|
// Rule 3: this read serves the committed value to a render effect outside a
|
|
1059
1085
|
// parking flush (decided after the pull below — a pull can join).
|
|
@@ -1061,20 +1087,20 @@ function read(e) {
|
|
|
1061
1087
|
if (t) {
|
|
1062
1088
|
if (tracking) {
|
|
1063
1089
|
link(e, t);
|
|
1064
|
-
if (E.
|
|
1090
|
+
if (E.ee) pullComputed(E, t);
|
|
1065
1091
|
}
|
|
1066
1092
|
// L2, as in the fast block above (tracked or not). A node born into the
|
|
1067
1093
|
// future (uninitialized) has no committed value: every reader derives
|
|
1068
1094
|
// from the future there, a render effect included (A29: a stale reader
|
|
1069
1095
|
// of it cannot fall back to the committed frame and enters instead).
|
|
1070
|
-
if (e.
|
|
1071
|
-
if (E.
|
|
1096
|
+
if (e.i & CONFIG_HELD && !(e.i & CONFIG_OVERRIDE) && !(t.i & CONFIG_CHILDREN_FORBIDDEN)) {
|
|
1097
|
+
if (E.S & STATUS_UNINITIALIZED || !frameRead(t, e)) joinPass(t, e); else u = true;
|
|
1072
1098
|
}
|
|
1073
1099
|
}
|
|
1074
1100
|
// Lanes: a lane's node (after the pull — the node is current). NOT_PENDING
|
|
1075
1101
|
// falls through: lane work's read of a pending member throws like any.
|
|
1076
|
-
if (e.
|
|
1077
|
-
const n = GlobalQueue.
|
|
1102
|
+
if (e.i & CONFIG_OVERRIDE) {
|
|
1103
|
+
const n = GlobalQueue.rt(t, e);
|
|
1078
1104
|
if (n !== NOT_PENDING) return n;
|
|
1079
1105
|
}
|
|
1080
1106
|
// A stale reader of a held flight (rule 3, A15's reveal corollary) is served
|
|
@@ -1089,53 +1115,54 @@ function read(e) {
|
|
|
1089
1115
|
// effect keeps its DOM by throwing, a memo has no DOM and is handed the
|
|
1090
1116
|
// value. `[isPending(x), x()]` reads `[true, stale]` in either order (A10)
|
|
1091
1117
|
// — unless the flight committed beneath inputs already on screen
|
|
1092
|
-
// (`observeFlight`)
|
|
1093
|
-
|
|
1118
|
+
// (`observeFlight`), or the reader was born into the flight's transaction
|
|
1119
|
+
// (#4010): it has no screen, and waits on the flight with its frame.
|
|
1120
|
+
if (E.S & STATUS_PENDING && !u && !(e.i & CONFIG_OVERRIDE) && !(E.S & STATUS_UNINITIALIZED)) {
|
|
1094
1121
|
// A reader with no committed value yet sees the flight pending: a
|
|
1095
1122
|
// boundary a lane mounts shows its fallback (A29's boundary exemption).
|
|
1096
|
-
if (passLane !== null && !(t?.
|
|
1123
|
+
if (passLane !== null && !(t?.S & STATUS_UNINITIALIZED)) u = true; else if (t !== null && t.i & CONFIG_VERDICT) u = GlobalQueue.It(t, E);
|
|
1097
1124
|
}
|
|
1098
|
-
if (E.
|
|
1125
|
+
if (E.S & STATUS_PENDING && !u) {
|
|
1099
1126
|
// A reader landing on a pending node throws; an untracked read of an
|
|
1100
1127
|
// UNINITIALIZED node has no committed value to serve and throws too. A
|
|
1101
1128
|
// children-forbidden reader (createTrackedEffect / onSettled) sees the
|
|
1102
1129
|
// frame, not the graph (A32): the committed value, and the NotReady only
|
|
1103
1130
|
// where there is none (uninitialized).
|
|
1104
1131
|
if (t) {
|
|
1105
|
-
if (!(t.
|
|
1132
|
+
if (!(t.i & CONFIG_CHILDREN_FORBIDDEN) || E.S & STATUS_UNINITIALIZED) {
|
|
1106
1133
|
// An untracked read of a pending node still re-runs its reader when
|
|
1107
1134
|
// the node settles — unless nobody is reading (`spectating`, #3528)
|
|
1108
1135
|
// or the reader is the node itself.
|
|
1109
1136
|
if (!tracking && !spectating && e !== t) link(e, t);
|
|
1110
|
-
throw E.m?.
|
|
1137
|
+
throw E.m?.F;
|
|
1111
1138
|
}
|
|
1112
|
-
} else if (E.
|
|
1113
|
-
throw E.m?.
|
|
1139
|
+
} else if (E.S & STATUS_UNINITIALIZED) {
|
|
1140
|
+
throw E.m?.F;
|
|
1114
1141
|
}
|
|
1115
1142
|
}
|
|
1116
1143
|
// An errored derive throws for every late reader instead of silently
|
|
1117
1144
|
// serving node values (memo parity, #2897 ruling).
|
|
1118
|
-
if (E.
|
|
1145
|
+
if (E.ee && E.S & STATUS_ERROR) {
|
|
1119
1146
|
// Only a genuine reactive re-read may retry an errored async source:
|
|
1120
1147
|
// - tracking: owned/tracked scope only (never events / `untrack` / effect side-effect phase)
|
|
1121
1148
|
// - owner._time < clock: only on a later cycle than the one the error was found
|
|
1122
|
-
if (tracking && E.
|
|
1149
|
+
if (tracking && E.Ie < clock) {
|
|
1123
1150
|
recompute(E);
|
|
1124
1151
|
return read(e);
|
|
1125
|
-
} else throw E.m?.
|
|
1152
|
+
} else throw E.m?.F;
|
|
1126
1153
|
}
|
|
1127
|
-
if (snapshotCaptureActive && t && t.
|
|
1128
|
-
const n = e.m?.
|
|
1154
|
+
if (snapshotCaptureActive && t && t.i & CONFIG_IN_SNAPSHOT_SCOPE) {
|
|
1155
|
+
const n = e.m?.Le;
|
|
1129
1156
|
if (n !== undefined) {
|
|
1130
1157
|
const E = n === NO_SNAPSHOT ? undefined : n;
|
|
1131
|
-
const u = e.
|
|
1158
|
+
const u = e.Oe !== NOT_PENDING ? e.Oe : e.Ae;
|
|
1132
1159
|
if (u !== E) t.R |= REACTIVE_SNAPSHOT_STALE;
|
|
1133
1160
|
return E;
|
|
1134
1161
|
}
|
|
1135
1162
|
}
|
|
1136
|
-
if (u) return e.
|
|
1163
|
+
if (u) return e.Ae;
|
|
1137
1164
|
const s = serve(e, t);
|
|
1138
|
-
if (!t && typeof n.
|
|
1165
|
+
if (!t && typeof n.ee === "function" && e.i & CONFIG_AUTO_DISPOSE && !(E.S & STATUS_PENDING) && !e.t) {
|
|
1139
1166
|
// Deferred, not inline (#3078): an inline unobserved() here made untracked
|
|
1140
1167
|
// reads destructive — dispose on this read, full revival recompute on the
|
|
1141
1168
|
// next — so consecutive reads could answer differently with no write in
|
|
@@ -1162,16 +1189,16 @@ function read(e) {
|
|
|
1162
1189
|
// an untracked reader has nothing to serve and holds (A19 exception 1) — a
|
|
1163
1190
|
// bookkeeping read (`spectate`) likewise, and a children-forbidden reader
|
|
1164
1191
|
// (A32: the frame, which has nothing here).
|
|
1165
|
-
if (e.
|
|
1192
|
+
if (e.Oe !== NOT_PENDING && e.S & STATUS_UNINITIALIZED && (!t || spectating || t.i & CONFIG_CHILDREN_FORBIDDEN)) throw new NotReadyError(null);
|
|
1166
1193
|
const n = t && unflushedStaged ? unflushedValue(e) : NOT_PENDING;
|
|
1167
1194
|
if (n !== NOT_PENDING) {
|
|
1168
1195
|
markLateLinker(t);
|
|
1169
1196
|
return n;
|
|
1170
1197
|
}
|
|
1171
|
-
if (readerSeesCommitted(e, t)) return e.
|
|
1172
|
-
if (t.
|
|
1198
|
+
if (readerSeesCommitted(e, t)) return e.Ae;
|
|
1199
|
+
if (t.i & CONFIG_VERDICT && !(e.S & STATUS_UNINITIALIZED) && stagedScreen(t)) return e.Ae;
|
|
1173
1200
|
stagedRead(t, e);
|
|
1174
|
-
return e.
|
|
1201
|
+
return e.Oe;
|
|
1175
1202
|
}
|
|
1176
1203
|
|
|
1177
1204
|
/** A pass read a staging of this flush: it derives from what the flush may
|
|
@@ -1183,10 +1210,13 @@ function read(e) {
|
|
|
1183
1210
|
* write never shows through the lane. A verdict lane's work likewise: the
|
|
1184
1211
|
* lane holds verdicts, not the frame's other stagings (#3851) — except a
|
|
1185
1212
|
* verdict reader, which answered for itself (the lane seam, lanes.ts). Not
|
|
1186
|
-
* a read of a node born staged: it has no committed value to
|
|
1187
|
-
* and a re-run would read the same staging and re-queue every
|
|
1188
|
-
|
|
1189
|
-
if (
|
|
1213
|
+
* a read of a node born staged at all: it has no committed value to
|
|
1214
|
+
* re-derive on, and a re-run would read the same staging and re-queue every
|
|
1215
|
+
* round (#3969: nor does it make a later lane read a re-derive, `enterLane`). */ function stagedRead(e, t) {
|
|
1216
|
+
if (!(t?.S & STATUS_UNINITIALIZED)) {
|
|
1217
|
+
e.R |= REACTIVE_STAGED_READ;
|
|
1218
|
+
if (passLane !== null) stagedReaders.push(e);
|
|
1219
|
+
}
|
|
1190
1220
|
}
|
|
1191
1221
|
|
|
1192
1222
|
/** A10 for a staged node: a verdict reader (the pass entered a window) that
|
|
@@ -1196,8 +1226,9 @@ function read(e) {
|
|
|
1196
1226
|
* #3322 — one run, on the committed world), or with the hold. Before any
|
|
1197
1227
|
* transaction the answer is the seam's and the reader watches it
|
|
1198
1228
|
* (verdict.ts). A node born into the future has no screen: its staging is
|
|
1199
|
-
* its only value (A29).
|
|
1200
|
-
|
|
1229
|
+
* its only value (A29). Nor has a reader born into the flush's transaction
|
|
1230
|
+
* (`bornInto`, #4010): its branch exists only there. */ function stagedScreen(e) {
|
|
1231
|
+
if (flushTransaction === null || bornInto(e, flushTransaction)) return false;
|
|
1201
1232
|
staleReader(e, flushTransaction);
|
|
1202
1233
|
return true;
|
|
1203
1234
|
}
|
|
@@ -1205,39 +1236,40 @@ function read(e) {
|
|
|
1205
1236
|
/** A28 (4) — written inside a recompute that runs OUTSIDE a flush (a
|
|
1206
1237
|
* creation-time compute): promoted at that pass's end, visible to the rest of
|
|
1207
1238
|
* the block. Cold: only contextual writes reach here. */ function notePromotedWrite(e) {
|
|
1208
|
-
if (globalQueue.
|
|
1209
|
-
e.
|
|
1239
|
+
if (globalQueue.X || e.i & CONFIG_PROMOTED) return;
|
|
1240
|
+
e.i |= CONFIG_PROMOTED;
|
|
1210
1241
|
promotedWrites.push(e);
|
|
1211
1242
|
}
|
|
1212
1243
|
|
|
1213
|
-
/** Cold half of setSignal's snapshot arm (see there)
|
|
1214
|
-
*
|
|
1215
|
-
*
|
|
1244
|
+
/** Cold half of setSignal's snapshot arm (see there), shared with a store
|
|
1245
|
+
* container's structural notify: first write during capture to a plain
|
|
1246
|
+
* signal without a live snapshot records the pre-write value. Only plain
|
|
1247
|
+
* user signals qualify. Computeds reach setSignal from an
|
|
1216
1248
|
* async landing (asyncWrite), and a value arriving from async during the pass
|
|
1217
1249
|
* REVEALS — the creation-time arm skips pending computeds for the same
|
|
1218
1250
|
* reason. Firewall leaves belong to a projection whose compute is the tree's
|
|
1219
1251
|
* own work, captured (or deliberately not) at creation. */ function captureWriteSnapshot(e, t) {
|
|
1220
|
-
if (e.
|
|
1221
|
-
ext(e).
|
|
1222
|
-
e.
|
|
1252
|
+
if (e.i & CONFIG_NO_SNAPSHOT || e.ee !== undefined || e.m?.Le !== undefined) return;
|
|
1253
|
+
ext(e).Le = t === undefined ? NO_SNAPSHOT : t;
|
|
1254
|
+
e.i |= CONFIG_HAS_SNAPSHOT;
|
|
1223
1255
|
snapshotSources.add(e);
|
|
1224
1256
|
}
|
|
1225
1257
|
|
|
1226
1258
|
function setSignal(e, t) {
|
|
1227
|
-
const n = e.
|
|
1259
|
+
const n = e.Oe === NOT_PENDING ? e.Ae : e.Oe;
|
|
1228
1260
|
if (typeof t === "function") t = t(n);
|
|
1229
1261
|
// Lanes: a write landing on a guess is its truth (A18 — an async guess's
|
|
1230
1262
|
// own landing), judged against the guess; one landing on a lane's
|
|
1231
1263
|
// derivation (its flight) is the lane's staging (lanes.ts).
|
|
1232
|
-
if (e.
|
|
1264
|
+
if (e.i & CONFIG_OVERRIDE) return GlobalQueue.Nt(e, t);
|
|
1233
1265
|
// L2 (T4) / A34 (1): a write to a node whose staged state is held is a
|
|
1234
1266
|
// second proposal for the slot — the same value again or another — and
|
|
1235
1267
|
// the two cannot finish at different times: the writer's tick joins the
|
|
1236
1268
|
// hold, before the equality gate.
|
|
1237
|
-
if (e.
|
|
1269
|
+
if (e.i & CONFIG_HELD) joinFuture(txOf(e));
|
|
1238
1270
|
// Uninitialized check first: the first commit has no previous value, so the
|
|
1239
1271
|
// user comparator must not run against `undefined` (matches recompute).
|
|
1240
|
-
const E = !!(e.
|
|
1272
|
+
const E = !!(e.S & STATUS_UNINITIALIZED) || !e.ye || !e.ye(n, t);
|
|
1241
1273
|
if (!E) return t;
|
|
1242
1274
|
// A write during hydration's snapshot capture to a source that has no
|
|
1243
1275
|
// snapshot — created BEFORE capture began (module-level state: an identity
|
|
@@ -1249,21 +1281,21 @@ function setSignal(e, t) {
|
|
|
1249
1281
|
// pass reads a value the server never had. Store leaves written here are
|
|
1250
1282
|
// plain signals and qualify the same way.
|
|
1251
1283
|
if (snapshotCaptureActive) captureWriteSnapshot(e, n);
|
|
1252
|
-
const u = e.
|
|
1284
|
+
const u = e.Oe !== NOT_PENDING;
|
|
1253
1285
|
if (!u) queuePendingNode(e);
|
|
1254
1286
|
// A28: a held node rewritten outside a flush keeps its flushed staging
|
|
1255
1287
|
// for every reader until the flush that carries the rewrite.
|
|
1256
|
-
else if (e.
|
|
1257
|
-
e.
|
|
1288
|
+
else if (e.i & CONFIG_HELD && !globalQueue.X) stashFlushed(e);
|
|
1289
|
+
e.Oe = t;
|
|
1258
1290
|
if (context !== null) notePromotedWrite(e);
|
|
1259
1291
|
// _time is a computed-only slot (§12e): writing it on a signal would fork
|
|
1260
1292
|
// the lean shape. Every read site is computed-typed.
|
|
1261
|
-
if (e.
|
|
1293
|
+
if (e.ee !== undefined) e.Ie = clock;
|
|
1262
1294
|
// Staged-rewrite fast path (§12d): a re-write to a node whose subscribers
|
|
1263
1295
|
// were already walked — and where nothing has recomputed or linked since
|
|
1264
1296
|
// (epoch) — re-stages the value and stops. The walk is idempotent (subs
|
|
1265
1297
|
// marked, heap entries flag-guarded, effects queued once).
|
|
1266
|
-
if (u && e.
|
|
1298
|
+
if (u && e.qe === notifyEpoch) return t;
|
|
1267
1299
|
insertSubs(e);
|
|
1268
1300
|
schedule();
|
|
1269
1301
|
return t;
|
|
@@ -1288,9 +1320,9 @@ function setSignal(e, t) {
|
|
|
1288
1320
|
// of replacing it. A write made under the hold carries the mark and keeps
|
|
1289
1321
|
// (1)'s last-write-wins. Only the user setter asks; an async landing
|
|
1290
1322
|
// writing a held node is the transaction's own work.
|
|
1291
|
-
const n = e.
|
|
1323
|
+
const n = e.i & CONFIG_HELD && !(e.R & REACTIVE_MANUAL_WRITE) && txOf(e) !== flushTransaction;
|
|
1292
1324
|
// The writer read the committed frame; its updater composes on it.
|
|
1293
|
-
if (n && typeof t === "function") t = t(e.
|
|
1325
|
+
if (n && typeof t === "function") t = t(e.Ae);
|
|
1294
1326
|
const E = setSignal(e, t);
|
|
1295
1327
|
if (n) {
|
|
1296
1328
|
// The one write that re-runs: the staging it replaced was derived from
|
|
@@ -1298,7 +1330,7 @@ function setSignal(e, t) {
|
|
|
1298
1330
|
e.R = e.R & ~REACTIVE_CHECK | REACTIVE_DIRTY;
|
|
1299
1331
|
insertIntoHeap(e, dirtyQueue);
|
|
1300
1332
|
schedule();
|
|
1301
|
-
} else if (e.
|
|
1333
|
+
} else if (e.Oe !== NOT_PENDING) e.R |= REACTIVE_MANUAL_WRITE;
|
|
1302
1334
|
return E;
|
|
1303
1335
|
}
|
|
1304
1336
|
|
|
@@ -1346,7 +1378,7 @@ function staleValues(e, t = true) {
|
|
|
1346
1378
|
* recompute as a quiet re-ask and schedules it; no-ops for non-derived or
|
|
1347
1379
|
* disposed targets.
|
|
1348
1380
|
*/ function markRefresh(e) {
|
|
1349
|
-
if (typeof e.
|
|
1381
|
+
if (typeof e.ee === "function" && !(e.R & REACTIVE_DISPOSED)) {
|
|
1350
1382
|
// A manual write never refuses the re-run (A34 rule B): a refresh in the
|
|
1351
1383
|
// write's own frame re-asks with the write as `prev`, like any source
|
|
1352
1384
|
// change (#3026 generalized).
|
|
@@ -1359,4 +1391,4 @@ function staleValues(e, t = true) {
|
|
|
1359
1391
|
}
|
|
1360
1392
|
}
|
|
1361
1393
|
|
|
1362
|
-
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 };
|
|
1394
|
+
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 };
|