@solidjs/signals 2.0.0-rc.15 → 2.0.0-rc.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/dist/dev-shared.js +785 -367
  2. package/dist/dev.attribution.js +75 -52
  3. package/dist/dev.js +1120 -567
  4. package/dist/observe/affects-marks.js +86 -0
  5. package/dist/observe/affects.js +22 -73
  6. package/dist/observe/boundaries.js +191 -189
  7. package/dist/observe/core/async.js +175 -131
  8. package/dist/observe/core/attribution-queries.js +1 -1
  9. package/dist/observe/core/attribution.js +176 -159
  10. package/dist/observe/core/constants.js +17 -3
  11. package/dist/observe/core/context.js +3 -3
  12. package/dist/observe/core/core.js +503 -363
  13. package/dist/observe/core/dev.js +66 -38
  14. package/dist/observe/core/effect.js +48 -48
  15. package/dist/observe/core/error.js +5 -3
  16. package/dist/observe/core/external.js +5 -6
  17. package/dist/observe/core/graph.js +57 -43
  18. package/dist/observe/core/heap.js +33 -33
  19. package/dist/observe/core/identities.js +62 -0
  20. package/dist/observe/core/lanes.js +226 -259
  21. package/dist/observe/core/owner.js +62 -50
  22. package/dist/observe/core/scheduler.js +319 -171
  23. package/dist/observe/core/truth.js +45 -0
  24. package/dist/observe/core/verdict.js +88 -50
  25. package/dist/observe/deferred.js +184 -0
  26. package/dist/observe/index.js +7 -1
  27. package/dist/observe/map.js +345 -260
  28. package/dist/observe/reveal.js +76 -76
  29. package/dist/observe/signals.js +41 -30
  30. package/dist/observe/store/affects.js +6 -6
  31. package/dist/observe/store/edges.js +39 -0
  32. package/dist/observe/store/optimistic.js +41 -36
  33. package/dist/observe/store/projection.js +68 -55
  34. package/dist/observe/store/store.js +157 -104
  35. package/dist/prod/affects-marks.js +86 -0
  36. package/dist/prod/affects.js +23 -72
  37. package/dist/prod/boundaries.js +155 -153
  38. package/dist/prod/core/async.js +171 -127
  39. package/dist/prod/core/constants.js +17 -3
  40. package/dist/prod/core/context.js +3 -3
  41. package/dist/prod/core/core.js +478 -342
  42. package/dist/prod/core/effect.js +68 -68
  43. package/dist/prod/core/error.js +5 -3
  44. package/dist/prod/core/external.js +5 -6
  45. package/dist/prod/core/graph.js +57 -43
  46. package/dist/prod/core/heap.js +38 -38
  47. package/dist/prod/core/identities.js +62 -0
  48. package/dist/prod/core/lanes.js +217 -250
  49. package/dist/prod/core/owner.js +62 -51
  50. package/dist/prod/core/scheduler.js +310 -162
  51. package/dist/prod/core/truth.js +45 -0
  52. package/dist/prod/core/verdict.js +87 -49
  53. package/dist/prod/deferred.js +182 -0
  54. package/dist/prod/index.js +7 -1
  55. package/dist/prod/map.js +360 -275
  56. package/dist/prod/reveal.js +76 -76
  57. package/dist/prod/signals.js +41 -30
  58. package/dist/prod/store/affects.js +6 -6
  59. package/dist/prod/store/edges.js +39 -0
  60. package/dist/prod/store/optimistic.js +37 -33
  61. package/dist/prod/store/projection.js +25 -19
  62. package/dist/prod/store/store.js +142 -96
  63. package/dist/types/affects-marks.d.ts +14 -0
  64. package/dist/types/boundaries.d.ts +1 -1
  65. package/dist/types/core/async.d.ts +10 -0
  66. package/dist/types/core/constants.d.ts +13 -1
  67. package/dist/types/core/core.d.ts +44 -2
  68. package/dist/types/core/dev.d.ts +43 -13
  69. package/dist/types/core/graph.d.ts +11 -0
  70. package/dist/types/core/identities.d.ts +17 -0
  71. package/dist/types/core/index.d.ts +2 -1
  72. package/dist/types/core/lanes.d.ts +2 -14
  73. package/dist/types/core/owner.d.ts +5 -3
  74. package/dist/types/core/scheduler.d.ts +47 -28
  75. package/dist/types/core/truth.d.ts +16 -0
  76. package/dist/types/core/types.d.ts +21 -0
  77. package/dist/types/core/verdict.d.ts +32 -0
  78. package/dist/types/deferred.d.ts +28 -0
  79. package/dist/types/index.d.ts +6 -0
  80. package/dist/types/map.d.ts +2 -2
  81. package/dist/types/signals.d.ts +28 -7
  82. package/dist/types/store/edges.d.ts +9 -0
  83. package/dist/types/store/index.d.ts +10 -3
  84. package/dist/types/store/optimistic.d.ts +3 -2
  85. package/dist/types/store/projection.d.ts +4 -3
  86. package/dist/types/store/store.d.ts +1 -1
  87. package/dist/types/store/target.d.ts +16 -8
  88. package/dist/types/store/types.d.ts +14 -0
  89. package/package.json +1 -1
@@ -1,10 +1,10 @@
1
- import { releaseFlightTeardown, handleAsync, clearStatus, parkLoadingWindow, notifyStatus, settlePendingSource, settleErroredDependents } from "./async.js";
1
+ import { notifyStatus, releaseFlightTeardown, handleAsync, clearStatus, parkLoadingWindow, stageError, settlePendingSource, releaseIfSettledUnobserved, settleErroredDependents } from "./async.js";
2
2
 
3
- import { STATUS_UNINITIALIZED, REACTIVE_LAZY, REACTIVE_NONE, defaultContext, CONFIG_TRANSPARENT, CONFIG_OWNED_WRITE, CONFIG_AUTO_DISPOSE, CONFIG_SYNC, CONFIG_NO_SNAPSHOT, CONFIG_PLUMBING, CONFIG_IN_SNAPSHOT_SCOPE, NOT_PENDING, 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";
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, STATUS_ERROR, REACTIVE_DIRTY, CONFIG_OVERRIDE, REACTIVE_DISPOSED, REACTIVE_CHECK, REACTIVE_IN_HEAP, REACTIVE_REASK, REACTIVE_SNAPSHOT_STALE, CONFIG_CHILDREN_FORBIDDEN, CONFIG_VERDICT, STATUS_PENDING, REACTIVE_LANE_DIRTY, CONFIG_GUESS, REACTIVE_RECOMPUTING_DEPS, CONFIG_LAGS, EFFECT_RENDER, CONFIG_HELD_FRAME, EFFECT_TRACKED, CONFIG_STAGED, CONFIG_STAGED_ERROR, REACTIVE_ZOMBIE, REACTIVE_MISSED_WAKE, REACTIVE_JOINED, REACTIVE_STAGED_READ, REACTIVE_PROBED, REACTIVE_FRAME_READ, REACTIVE_LANE_READ, REACTIVE_SCREEN_READ, REACTIVE_PROBE_UNANSWERED, CAUGHT_REARMED, CONFIG_PROMOTED, CONFIG_INPUTS_PUBLISHED, CONFIG_FRESH_READ, REACTIVE_IN_HEAP_HEIGHT, CONFIG_SLOT_NODE, EFFECT_USER, STATUS_NONE } from "./constants.js";
4
4
 
5
5
  import { NotReadyError } from "./error.js";
6
6
 
7
- import { clearDeps, trimStaleDeps, link, dormantNodes } from "./graph.js";
7
+ import { link, dormantNodes, clearDeps, trimStaleDeps, deferReleases, deferredReleases } from "./graph.js";
8
8
 
9
9
  import { insertIntoHeap, deleteFromHeap, insertIntoHeapHeight, enqueueSub, markNode, markHeap } from "./heap.js";
10
10
 
@@ -12,9 +12,9 @@ import { GRAPH_SIZE_WARN_AT, noteFanIn } from "./dev.js";
12
12
 
13
13
  import { attrHooks } from "./attribution-hooks.js";
14
14
 
15
- import { inheritId, disposeChildren, linkChild, markDisposal } from "./owner.js";
15
+ import { inheritId, linkChild, disposeChildren, markDisposal } from "./owner.js";
16
16
 
17
- import { schedule, GlobalQueue, clock, txOf, flushTransaction, joinFuture, queuePendingNode, globalQueue, notifyEpoch, insertSubs, dirtyQueue, bumpNotifyEpoch, setPassLane, passLane, joinPassTx, passTx, holdNode, laneDirty, heldTrim, staleReader, stagedReaders, deferZombie } from "./scheduler.js";
17
+ import { schedule, externalUntrackHook, clock, txOf, flushTransaction, laneWriteHook, joinFuture, queuePendingNode, globalQueue, notifyEpoch, insertSubs, dirtyQueue, observeFlightHook, laneReadHook, laneStageHook, passLane, wireExternalSourceHook, bumpNotifyEpoch, setPassLane, joinPassTx, freshHook, GlobalQueue, laneOutcomeHook, passTx, holdNode, laneDirty, heldTrim, staleReader, laneSeamsHook, stagedReaders, deferZombie } from "./scheduler.js";
18
18
 
19
19
  // The heap's per-node step. A tracked effect's heap visit is its compute
20
20
  // phase — empty, like a user effect whose compute reads nothing — and hands
@@ -24,10 +24,10 @@ import { schedule, GlobalQueue, clock, txOf, flushTransaction, joinFuture, queue
24
24
  // render-effect callback stages its value for the next pass, but a wake pushed
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
- GlobalQueue.et = e => {
28
- if (e.u === EFFECT_TRACKED) {
27
+ GlobalQueue.Ze = e => {
28
+ if (e.i === EFFECT_TRACKED) {
29
29
  deleteFromHeap(e, dirtyQueue);
30
- e.lt = true;
30
+ e.Qe = true;
31
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
@@ -36,10 +36,29 @@ GlobalQueue.et = e => {
36
36
  // owner is held in the future). Its lane's own re-staging is a live write
37
37
  // (REACTIVE_LANE_DIRTY — a lane shows ahead of any park): it runs now, as
38
38
  // the lane's work, for the reveal this seam (#3463).
39
- else if (e.R & REACTIVE_ZOMBIE && !(e.R & REACTIVE_LANE_DIRTY)) deferZombie(e); else recompute(e);
39
+ else if (e.R & REACTIVE_ZOMBIE && !(e.R & REACTIVE_LANE_DIRTY)) deferZombie(e);
40
+ // A lane frame's member (parked by a lane pass, #3698) follows mainline
41
+ // writes only: a lane's dirty on it is cancelled — the lane's apply retires
42
+ // it, and the pass that replaced it already decided what that screen shows
43
+ // (#4137: a `<Show>` branch withdrawn by its own lane re-ran on a falsy
44
+ // condition).
45
+ // (Lanes exist only with the lane engine installed: folded away without.)
46
+ else if (laneStageHook && e.R & REACTIVE_ZOMBIE && laneFrameMember(e)) {
47
+ deleteFromHeap(e, dirtyQueue);
48
+ e.R &= -65540;
49
+ } else recompute(e);
40
50
  };
41
51
 
42
- GlobalQueue.Et = disposeChildren;
52
+ /** The zombie's frame was parked by lane work: the nearest non-zombie owner
53
+ * (the one whose pass parked it) carries a lane's derived value — or, an
54
+ * effect (no lane value: `laneStage`), is listed in a lane (an `insert()`
55
+ * hole's render effect). */ function laneFrameMember(e) {
56
+ let t = e._parent;
57
+ while (t.R & REACTIVE_ZOMBIE) t = t._parent;
58
+ return (t.l & (CONFIG_OVERRIDE | CONFIG_GUESS)) === CONFIG_OVERRIDE || t.i && t.T?.Je?.V;
59
+ }
60
+
61
+ GlobalQueue.et = disposeChildren;
43
62
 
44
63
  let tracking = false;
45
64
 
@@ -47,11 +66,15 @@ let context = null;
47
66
 
48
67
  let snapshotCaptureActive = false;
49
68
 
69
+ /** The lane at the root of the running chain of first passes: the lane whose
70
+ * pass created the chain's first node (a mount), inherited by every first
71
+ * pass nested under it, whose seats are mainline (`recompute`, #4166). */ let laneRoot;
72
+
50
73
  let snapshotSources = null;
51
74
 
52
75
  function ownerInSnapshotScope(e) {
53
76
  while (e) {
54
- if (e.st) return true;
77
+ if (e.ot) return true;
55
78
  e = e._parent;
56
79
  }
57
80
  return false;
@@ -63,34 +86,34 @@ function setSnapshotCapture(e) {
63
86
  }
64
87
 
65
88
  function markSnapshotScope(e) {
66
- e.st = true;
89
+ e.ot = true;
67
90
  }
68
91
 
69
92
  function releaseSnapshotScope(e) {
70
- e.st = false;
93
+ e.ot = false;
71
94
  releaseSubtree(e);
72
95
  schedule();
73
96
  }
74
97
 
75
98
  function releaseSubtree(e) {
76
- let t = e.nn;
99
+ let t = e.ze;
77
100
  while (t) {
78
- if (t.st) {
79
- t = t.tn;
101
+ if (t.ot) {
102
+ t = t.Xe;
80
103
  continue;
81
104
  }
82
- if (t.ee) {
105
+ if (t.X) {
83
106
  const e = t;
84
- e.i &= ~CONFIG_IN_SNAPSHOT_SCOPE;
107
+ e.l &= ~CONFIG_IN_SNAPSHOT_SCOPE;
85
108
  if (e.R & REACTIVE_SNAPSHOT_STALE) {
86
109
  e.R &= ~REACTIVE_SNAPSHOT_STALE;
87
110
  e.R |= REACTIVE_DIRTY;
88
- if (dirtyQueue.ut > e.it) dirtyQueue.ut = e.it;
111
+ if (dirtyQueue.st > e.Et) dirtyQueue.st = e.Et;
89
112
  insertIntoHeap(e, dirtyQueue);
90
113
  }
91
114
  }
92
115
  releaseSubtree(t);
93
- t = t.tn;
116
+ t = t.Xe;
94
117
  }
95
118
  }
96
119
 
@@ -101,8 +124,8 @@ function clearSnapshots() {
101
124
  // pre-initialized to undefined (see ext()), and every reader tests
102
125
  // `!== undefined` — assign, don't `delete`: deleting a field pushes the
103
126
  // object to dictionary mode for every later read of every field.
104
- const t = e.m;
105
- if (t != null) t.ot = undefined;
127
+ const t = e.T;
128
+ if (t != null) t.lt = undefined;
106
129
  }
107
130
  snapshotSources = null;
108
131
  }
@@ -112,7 +135,8 @@ function clearSnapshots() {
112
135
  function recompute(e, t = false) {
113
136
  // §12d: any recompute can clean a marked subscriber — invalidate skips.
114
137
  bumpNotifyEpoch();
115
- const n = e.u;
138
+ const n = e.i;
139
+ let o;
116
140
  // Lanes (plan sec. 28): the seat of a pass is its node's. A node carrying a lane's
117
141
  // derived value runs as the lane's whoever dirtied it — a sync write, a
118
142
  // boundary reset, a frame rerun — so its children and its result are the
@@ -128,23 +152,30 @@ function recompute(e, t = false) {
128
152
  // derivation's pass into the lane (`read`); a leaf's never moves.
129
153
  // Restored at the end, after this pass's staging and runs have been
130
154
  // routed.
131
- const l = passLane;
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
- t && l?._parent.rt != l && (creatorPass(context)?.R ?? 0) & REACTIVE_RECOMPUTING_DEPS ? l : null);
155
+ // Lanes exist only with the lane engine installed (`laneStageHook`): a
156
+ // bundle without it seats every pass on mainline, and folds the seat away.
157
+ const s = passLane;
158
+ // The mount a first pass belongs to: its creator's lane, or the one its
159
+ // creator's own mount came from — a render effect a verdict lane's mount's
160
+ // content creates is that mount's too (#4166). Lanes only, like the seat.
161
+ const E = laneRoot;
162
+ const l = laneStageHook && (laneRoot = t && (s || E));
163
+ setPassLane(!laneStageHook ? null : (e.R & REACTIVE_LANE_DIRTY || (e.l & (CONFIG_OVERRIDE | CONFIG_GUESS)) === CONFIG_OVERRIDE) && e.T?.Je?.V ? e.T.Je : // Loose `!=`: false with no creator lane (undefined == null).
164
+ t && s?._parent.it != s && creatorPass(context)?.R & REACTIVE_RECOMPUTING_DEPS ? s : null);
134
165
  // Attribution hook: fired before this run touches the dep list — `_deps`
135
166
  // still holds the previous run's links (the subscriptions that could have
136
167
  // triggered this run, and the baseline for the engine's subscription diff).
137
- let E = false;
168
+ let i = false;
138
169
  {
139
- e.Ze = 0;
170
+ e.Ve = 0;
140
171
  if (attrHooks !== null) attrHooks.recomputeStart(e, t);
141
172
  }
142
173
  // CARVE 2: lane posture resolution (OPTIMISTIC_DIRTY, derived-override
143
174
  // re-derivation, lane adoption through deps) went with the optimistic engine.
144
175
  if (!t) {
145
176
  deleteFromHeap(e, dirtyQueue);
146
- if (e.m !== null) {
147
- e.m.Te = null;
177
+ if (e.T !== null) {
178
+ e.T.Se = null;
148
179
  // Supersede is where an iterator flight dies (#3122): close it now.
149
180
  // Idempotent with the cleanup-channel close.
150
181
  releaseFlightTeardown(e);
@@ -165,21 +196,23 @@ function recompute(e, t = false) {
165
196
  // committed screen, settle the flight and drop the hold — a superseding
166
197
  // write's verdict would depend on that probe. Cleared for this pass; a
167
198
  // 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) {
199
+ if (e.m & STATUS_PENDING) e.l &= ~CONFIG_VERDICT;
200
+ if (e.l & CONFIG_HELD) {
170
201
  const t = txOf(e);
171
202
  // Held by a blocked lane: the frame joins nothing — a lane never holds
172
203
  // a sync write (#3460). The pass is the lane's if it reads the lane's
173
204
  // world (`read`), and has left it otherwise.
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)) {
205
+ // An answered deferred node's re-pass is a lagging question (D-40):
206
+ // it joins nothing, so the write that reached it commits mainline.
207
+ if (t.V || e.l & CONFIG_LAGS) ; else if (n !== EFFECT_RENDER && !(e.l & CONFIG_VERDICT)) joinPassTx(t, freshHook?.(e, e.m & STATUS_UNINITIALIZED)); else if (t !== flushTransaction && !(e.m & STATUS_UNINITIALIZED || e.l & CONFIG_HELD_FRAME)) {
175
208
  // Published mainline, it is not held: the frame this pass builds is
176
209
  // replaced on the spot (#3404) — this flush's commit retires the
177
210
  // committed frame the held pass had parked (`commitPendingNode`),
178
211
  // after the new frame's first runs, and the held pass's own
179
212
  // children die below (`disposeChildren`). The landing re-derives a
180
213
  // fresh held frame for the reveal (`_reruns`).
181
- e.Oe = NOT_PENDING;
182
- if (n === EFFECT_RENDER) e.i &= ~CONFIG_HELD;
214
+ e.ue = NOT_PENDING;
215
+ if (n === EFFECT_RENDER) e.l &= ~CONFIG_HELD;
183
216
  }
184
217
  }
185
218
  // The previous pass's frame. If that pass is still awaiting its commit
@@ -187,75 +220,91 @@ function recompute(e, t = false) {
187
220
  // frame parked before it stays parked. Otherwise they ARE the frame:
188
221
  // park them until this pass commits. Tracked effects run after the seam
189
222
  // and own nothing to park.
190
- if (n === EFFECT_TRACKED || e.i & CONFIG_STAGED) disposeChildren(e); else if (e.nn !== null || e.Re !== null) parkChildren(e);
223
+ if (n === EFFECT_TRACKED || e.l & CONFIG_STAGED) {
224
+ // A lazy node only those children read is released when this pass
225
+ // ends, if the pass doesn't read it again (#4142).
226
+ const t = deferredReleases;
227
+ deferReleases(o = []);
228
+ disposeChildren(e);
229
+ deferReleases(t);
230
+ } else if (e.ze !== null || e.rt !== null) parkChildren(e);
231
+ // An error a pass staged (D-41) is replaced by this pass's answer: a
232
+ // value, a flight, or an error of its own (#4109, #4145).
233
+ if (e.l & CONFIG_STAGED_ERROR) {
234
+ e.ue = NOT_PENDING;
235
+ e.l ^= CONFIG_STAGED_ERROR;
236
+ }
191
237
  }
192
- const s = !!(e.S & STATUS_UNINITIALIZED);
238
+ const r = !!(e.m & STATUS_UNINITIALIZED);
193
239
  // Capture both error and pending status before the compute clears them.
194
240
  // A conditional can drop its pending source and recover to an unchanged
195
241
  // value, leaving blocked dependents outside that source’s settle walk.
196
- const u = e.S & STATUS_ERROR ? e.m?.F : undefined;
197
- const i = (e.S & STATUS_PENDING) !== 0;
198
- const o = i ? e.m?.Se : undefined;
242
+ const u = e.m & STATUS_ERROR ? e.T?.F : undefined;
243
+ const _ = (e.m & STATUS_PENDING) !== 0;
244
+ const a = _ ? e.T?.fe : undefined;
199
245
  // Pending SOURCE-hood, captured before the compute clears status: a node
200
246
  // whose own flight parked dependents self-registers in _pendingSources
201
247
  // (notifyStatus, isSource). If this recompute supersedes that flight and
202
248
  // settles synchronously, those dependents settle HERE — asyncWrite's
203
249
  // settlePendingSource walk never runs for a landing that was preempted
204
250
  // (#3181).
205
- const _ = e.m?.Se?.has(e);
251
+ const I = e.T?.fe?.has(e);
252
+ // D-41: this pass's error is staged, not published (`stageError`).
253
+ let N = false;
206
254
  // A19 exc. 2: a `refresh()` re-asks the question already answered — quiet
207
255
  // for the verdict — unless the question in flight is a new one, which the
208
256
  // re-ask does not launder (a poll during a quiet confirm stays quiet).
209
- const r = (e.R & REACTIVE_REASK) !== 0 && !(e.S & STATUS_PENDING && e.m !== null && !e.m.It);
210
- const a = context;
257
+ const f = (e.R & REACTIVE_REASK) !== 0 && !(e.m & STATUS_PENDING && e.T !== null && !e.T.ut);
258
+ const T = context;
211
259
  context = e;
212
- e.p = null;
213
- e.pe++;
260
+ e._ = null;
261
+ e.Ne++;
214
262
  // REACTIVE_ZOMBIE is position: it says the node sits on its owner's parked
215
263
  // frame, and the splice/reawaken paths key off it. A zombie reruns for
216
264
  // live writes until the commit that disposes it, so every per-pass wipe
217
265
  // — here, the finally below, updateIfNecessary — carries it (#3543).
218
- e.R = REACTIVE_RECOMPUTING_DEPS | e.R & REACTIVE_ZOMBIE;
219
- e.Ie = clock;
266
+ e.R = REACTIVE_RECOMPUTING_DEPS | e.R & (REACTIVE_ZOMBIE | REACTIVE_REASK);
267
+ e.ie = clock;
220
268
  // The pass's previous value: its staging, else a lane node's lane value
221
269
  // 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;
223
- let N = e.it;
224
- let f = false;
225
- // L2: did this pass read the future (REACTIVE_JOINED, set by read)?
226
- let T = false;
227
- let S = tracking;
270
+ let S = e.ue !== NOT_PENDING ? e.ue : e.l & CONFIG_OVERRIDE && e.T.V !== NOT_PENDING ? e.T.V : e.Ie;
271
+ let c = e.Et;
272
+ let A = false;
273
+ // L2: did this pass read the future (REACTIVE_JOINED, set by read) — or,
274
+ // a lane's mount, a staging of this flush (#3851, #4166)?
275
+ let R = false;
276
+ let d = tracking;
228
277
  tracking = true;
229
278
  try {
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;
279
+ if (!false && e.l & CONFIG_SYNC) {
280
+ S = e.X(S);
281
+ if (e.T !== null) e.T.Se = null;
282
+ e.ce = false;
234
283
  } else {
235
284
  // A projection's body self-registers its flight through handleAsync
236
285
  // (with the commit as setter): its undefined return is not a sync
237
286
  // answer, and the outer handleAsync must not clobber the registration.
238
- const t = e.m?.Te;
239
- const n = e.ee(I);
240
- const l = typeof n === "object" && n !== null;
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;
287
+ const t = e.T?.Se;
288
+ const n = e.X(S);
289
+ const o = typeof n === "object" && n !== null;
290
+ const s = e.T?.Se !== t;
291
+ S = s || !o ? n : handleAsync(e, n);
292
+ if (!s && !o) {
293
+ if (e.T !== null) e.T.Se = null;
245
294
  // A sync (non-object) return is the first real answer; async-shaped
246
295
  // results clear inside handleAsync at their own landing points, and a
247
296
  // self-registered flight clears when its own handleAsync lands.
248
- e.De = false;
297
+ e.ce = false;
249
298
  }
250
299
  }
251
300
  // On a status-free node clearStatus is a guaranteed no-op: every field
252
301
  // its body gates on is either _statusFlags or lives in the cold
253
302
  // extension — no extension, no status to clear. (_x from an unrelated
254
303
  // installer just makes clearStatus a cheap re-verified no-op.)
255
- if (e.S !== 0 || e.m !== null) clearStatus(e, t);
304
+ if (e.m !== 0 || e.T !== null) clearStatus(e, t);
256
305
  } catch (t) {
257
306
  const n = t instanceof NotReadyError;
258
- if (n && e.De) {
307
+ if (n && e.ce) {
259
308
  // Loading window with an unready sync dependency: register for the
260
309
  // source's settle (the settlePendingSource walk runs off
261
310
  // _pendingSources + _blocked alone) but take NO read-visible pending
@@ -265,11 +314,15 @@ function recompute(e, t = false) {
265
314
  // retry can actually run.
266
315
  parkLoadingWindow(e, t);
267
316
  } else {
268
- if (n) ext(e).de = true;
269
- notifyStatus(e, n ? STATUS_PENDING : STATUS_ERROR, t);
270
- // The replacement source is fully propagated now. If no new flight
271
- // re-owned self, retire the superseded flight and its dependent copies.
272
- if (n && _ && !e.m?.Te) settlePendingSource(e);
317
+ // D-41: a pass of a held node, or one that read a staging of this
318
+ // flush, stages its error (`stageError`).
319
+ if (n || !(N = stageError(e, t, true))) {
320
+ if (n) ext(e).se = true;
321
+ notifyStatus(e, n ? STATUS_PENDING : STATUS_ERROR, t);
322
+ // The replacement source is fully propagated now. If no new flight
323
+ // re-owned self, retire the superseded flight and its dependent copies.
324
+ if (n && I && !e.T?.Se) settlePendingSource(e);
325
+ }
273
326
  // A re-park drops what the earlier pass carried (#3456): a source this
274
327
  // pass no longer reaches — its branch switched, or a fresh flight
275
328
  // replaced the inputs' pending with its own — stays copied onto
@@ -277,21 +330,24 @@ function recompute(e, t = false) {
277
330
  // stops at this node (nothing left to retire) before it finds them. A
278
331
  // dependent then waits forever on a flight it has no path to. The
279
332
  // re-park twin of the unchanged-value recovery sweep below; dependents
280
- // with another path keep the source (retryReaches).
281
- if (n && o) for (const t of o) if (t !== e && !e.m?.Se?.has(t)) settlePendingSource(e, t);
333
+ // with another path keep the source (retryReaches). A staged error
334
+ // answered what the pass carried: the same.
335
+ if ((n || N) && a) for (const t of a) if (t !== e && !e.T?.fe?.has(t)) settlePendingSource(e, t);
282
336
  }
283
337
  } finally {
284
- tracking = S;
338
+ tracking = d;
339
+ if (laneStageHook) laneRoot = E;
285
340
  // Consume the missed-wake latch (#3037, set by insertSubs): a dep write
286
341
  // landed beneath this pass on a link it had already validated. The wipe
287
342
  // below must not key off DIRTY/CHECK — the read-time pull protocol
288
343
  // (markNode(c) in read()) marks the running node as part of ordinary
289
344
  // bookkeeping, and those marks are correctly discarded here.
290
- f = (e.R & REACTIVE_MISSED_WAKE) !== 0;
291
- T = (e.R & REACTIVE_JOINED) !== 0;
292
- // A verdict reader is one for as long as it probes: a pass that entered
293
- // no window (REACTIVE_PROBED) is an ordinary derivation again.
294
- if (!(e.R & REACTIVE_PROBED)) e.i &= ~CONFIG_VERDICT;
345
+ A = (e.R & REACTIVE_MISSED_WAKE) !== 0;
346
+ R = !!(e.R & (l ? REACTIVE_JOINED | REACTIVE_STAGED_READ : REACTIVE_JOINED));
347
+ // A verdict reader is one for as long as it probes, or reads a memo
348
+ // that does (`read`): a pass that did neither (REACTIVE_PROBED, the same
349
+ // bit) is an ordinary derivation again.
350
+ e.l = e.l & ~CONFIG_VERDICT | e.R & REACTIVE_PROBED;
295
351
  // REACTIVE_DISPOSED survives too (#3621): the pass may have disposed its
296
352
  // own owner (a memo calling its root's `dispose()`, a cleanup doing so
297
353
  // #3601/#3606), and `disposeChildren` set the flag on this node
@@ -300,11 +356,12 @@ function recompute(e, t = false) {
300
356
  // this pass's verdict for the landing; the next pass's wipe at the top
301
357
  // clears it.
302
358
  e.R = e.R & (REACTIVE_ZOMBIE | REACTIVE_DISPOSED | REACTIVE_FRAME_READ | REACTIVE_STAGED_READ | REACTIVE_LANE_READ | REACTIVE_SCREEN_READ | REACTIVE_PROBE_UNANSWERED) | (t ? e.R & REACTIVE_SNAPSHOT_STALE : 0);
303
- context = a;
359
+ context = T;
360
+ if (o) for (const e of o) releaseIfSettledUnobserved(e);
304
361
  // A19 exc. 2: a pass that went pending on a `refresh()` re-asks the
305
362
  // question already answered — quiet for the verdict; any other pending
306
363
  // pass is a new question. Settled, there is nothing to classify.
307
- if (e.m !== null) e.m.It = r && (e.S & STATUS_PENDING) !== 0;
364
+ if (e.T !== null) e.T.ut = f && (e.m & STATUS_PENDING) !== 0;
308
365
  }
309
366
  // A node that died during its own pass (#3621) is dead at the end of it,
310
367
  // and the pass is void. Its owner's teardown already unlinked its deps,
@@ -319,9 +376,9 @@ function recompute(e, t = false) {
319
376
  // attribution frame opened at the top is still closed.
320
377
  if (e.R & REACTIVE_DISPOSED) {
321
378
  clearDeps(e);
322
- if (e.m !== null) e.m.Te = null;
379
+ if (e.T !== null) e.T.Se = null;
323
380
  if (attrHooks !== null) attrHooks.recomputeEnd(e, t, false, false, false, false);
324
- setPassLane(l);
381
+ setPassLane(s);
325
382
  return;
326
383
  }
327
384
  // The lane this pass is work of, if any: its seat (the node's, its
@@ -329,14 +386,14 @@ function recompute(e, t = false) {
329
386
  // seat that read none of the lane's world has left it: its result is the
330
387
  // frame's (a derivation whose branch no longer reaches the guess). A
331
388
  // guess is written, not derived — it never leaves this way.
332
- let c = passLane;
389
+ let C = !laneStageHook ? null : passLane;
333
390
  // Listed before its staging, a pending pass included (the lane's own
334
391
  // flight is the lane's); false: the pass left the lane (lanes.ts).
335
- const A = !!e.m?.F;
336
- if (c !== null) {
337
- if (GlobalQueue.W(e, c, t, A)) setPassLane(c); else c = null;
392
+ const O = !!e.T?.F || N;
393
+ if (laneStageHook && C !== null) {
394
+ if (laneStageHook(e, C, t, O)) setPassLane(C); else C = null;
338
395
  }
339
- if (!e.m?.F) {
396
+ if (!O) {
340
397
  // Observe-tier fan-in (HUGE_FAN_IN): the validated prefix [_deps.._depsTail]
341
398
  // IS this pass's distinct sources — count it here rather than per link. A
342
399
  // begin/end bracket around the pass plus a per-link increment measured
@@ -347,9 +404,9 @@ function recompute(e, t = false) {
347
404
  // this pass, or at its commit — see recompute's tail.)
348
405
  {
349
406
  let t = 0;
350
- for (let n = e.G; n !== null; n = n._) {
407
+ for (let n = e.C; n !== null; n = n.G) {
351
408
  t++;
352
- if (n === e.p) break;
409
+ if (n === e._) break;
353
410
  }
354
411
  if (t >= GRAPH_SIZE_WARN_AT) noteFanIn(e, t);
355
412
  }
@@ -357,41 +414,33 @@ function recompute(e, t = false) {
357
414
  // publishes to — the staged `_pendingValue` when staged; a lane node's
358
415
  // lane value (a node entering the lane this pass has none yet — its
359
416
  // committed value, `laneStage` having voided a frame staging).
360
- const i = e.Oe !== NOT_PENDING ? e.Oe : e.i & CONFIG_OVERRIDE && e.m.B !== NOT_PENDING ? e.m.B : e.Ae;
361
- let r = false;
362
- try {
363
- r = !n && s || !e.en || !e.en(i, I);
364
- } catch (t) {
365
- // A throwing user comparator is an error of this node's computation.
366
- // Route it through the same status path as a compute-phase throw so
367
- // error boundaries contain it; otherwise it unwinds the scheduler
368
- // flush, bypassing every boundary and wedging the queue (#2837).
369
- notifyStatus(e, STATUS_ERROR, t);
370
- }
417
+ const o = e.ue !== NOT_PENDING ? e.ue : e.l & CONFIG_OVERRIDE && e.T.V !== NOT_PENDING ? e.T.V : e.Ie;
418
+ // A throwing comparator is contained inside `isSame` (A3, #2837; #4111).
419
+ const s = !n && r || !!e.T?.de || !isSame(e, o, S);
371
420
  // A committed derived change becomes a cause for this node's subscribers,
372
421
  // chaining their attribution through this node to the root write.
373
422
  if (attrHooks !== null) {
374
- E = r && !e.m?.F;
375
- if (E && !n && !t) attrHooks.derivedChanged(e);
423
+ i = s && !e.T?.F;
424
+ if (i && !n && !t) attrHooks.derivedChanged(e);
376
425
  }
377
426
  // Effects use `_equals: false` (no per-effect closure). The side effects that
378
427
  // the equals closure used to perform — flagging the effect dirty and enqueueing
379
428
  // its runner — happen here instead. `!create` matches the previous `initialized`
380
429
  // gate: the explicit recompute(node, true) inside effect() does not enqueue, so
381
430
  // effect() can call its runner synchronously for the first run.
382
- if (n && r &&
431
+ if (n && s &&
383
432
  // A stale reader of a lane (REACTIVE_SCREEN_READ) that computed what it
384
433
  // last applied owes no run (lanes.ts).
385
- !(e.R & REACTIVE_SCREEN_READ && !e.lt && Object.is(e.Nt, I)) &&
434
+ !(e.R & REACTIVE_SCREEN_READ && !e.Qe && Object.is(e._t, S)) &&
386
435
  // A re-staging pass owes no run: the commit replays the effect (#3802).
387
- e.Oe === NOT_PENDING) {
388
- e.lt = !e.m?.F;
436
+ e.ue === NOT_PENDING) {
437
+ e.Qe = !e.T?.F;
389
438
  // Reuse one bound runner per effect — runEffect no-ops on a stale
390
439
  // `_modified`, so re-enqueueing the same function is harmless.
391
440
  // Lane work's run is the lane's (released at its reveal) — a first
392
441
  // pass under a lane too: `effect()` skips the synchronous first run of
393
442
  // a lane's node.
394
- if (!t || c !== null) globalQueue.enqueue(n, e.Y ??= GlobalQueue.q.bind(null, e), c);
443
+ if (!t || C !== null) globalQueue.enqueue(n, e.W ??= GlobalQueue.K.bind(null, e), C);
395
444
  }
396
445
  // Lanes. A written guess's own pass from the frame: its source
397
446
  // recomputed it — the truth (A18: "the source is whatever recomputes the
@@ -399,7 +448,7 @@ function recompute(e, t = false) {
399
448
  // confirm is a landing too — the lane's end commits it) and notifies
400
449
  // only a correction: an equal truth re-runs nothing (lanes.ts). (As the
401
450
  // lane's own work the lane judged it — `laneStage`.)
402
- if (!t && c === null && e.i & CONFIG_GUESS && GlobalQueue.ft(e, I, A)) ; else if (A) ; else if (r) {
451
+ if (!t && C === null && e.l & CONFIG_GUESS && laneOutcomeHook(e, S, O)) ; else if (O) ; else if (s) {
403
452
  // L2: a first pass is born held — staged, committed with the
404
453
  // transaction, its effect's first run the landing's (A29; `effect()`
405
454
  // skips the synchronous first run on a staged value) — when the flush
@@ -416,15 +465,15 @@ function recompute(e, t = false) {
416
465
  // pass that runs before anything joins: the pass's input, not a verdict
417
466
  // after it. An effect still carrying an uncommitted staged value
418
467
  // re-stages: the commit applies the latest pass, not the born-held one.
419
- if (c !== null) {
468
+ if (C !== null) {
420
469
  // Lane work: the lane's value — into the slot while the lane has not
421
470
  // revealed (its reveal shows it), as its staging once it has (the
422
471
  // next reveal promotes it; the screen keeps the revealed value). An
423
472
  // effect's value slot is private: the run the lane holds is what
424
473
  // shows it — unless the effect was born held, whose first run is the
425
474
  // commit's (A29): it re-stages, as below.
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) {
475
+ if (n && e.ue === NOT_PENDING) e.Ie = S; else if (n || C.It) e.ue = S; else e.T.V = S;
476
+ } else if (t ? !((flushTransaction !== null || passTx !== null) && (R || creatorPass(T)?.R & REACTIVE_JOINED)) : n && e.ue === NOT_PENDING) {
428
477
  // A first pass publishes directly. So does an effect: its value slot
429
478
  // is private (only its own run reads it), and the staging round-trip
430
479
  // (queuePendingNode + commitPendingNodes) paid per effect on the
@@ -435,9 +484,9 @@ function recompute(e, t = false) {
435
484
  // sources commit) must see the fresh value while PLAIN reads stay
436
485
  // committed until flush (#3009 purity). The pending round-trip is
437
486
  // that separation; it cannot be skipped on any path a pull can reach.
438
- e.Ae = I;
487
+ e.Ie = S;
439
488
  } else {
440
- e.Oe = I;
489
+ e.ue = S;
441
490
  // Born held: in the transaction from birth, so a read of it this
442
491
  // tick is a read of its world (the mount-during-a-hold case), not
443
492
  // only after the seam parks it.
@@ -447,47 +496,54 @@ function recompute(e, t = false) {
447
496
  // (`commitPendingNode` initializes it) — every reader of it
448
497
  // derives from the future (`read`), an untracked one throws
449
498
  // (A19 exc. 1).
450
- e.S |= STATUS_UNINITIALIZED;
499
+ e.m |= STATUS_UNINITIALIZED;
451
500
  // A boundary that has not shown content catches it: its fallback
452
501
  // waits for the commit (#3540). (The pass's own status clear above
453
- // dropped what the read recorded.)
454
- GlobalQueue.fe?.(e);
502
+ // dropped what the read recorded.) Not when the flush has joined
503
+ // the hold and the pass is its own (not a verdict lane's mount,
504
+ // #3851): the mount is part of the hold, and the boundary appears
505
+ // at its commit, its fallback never seen (D-13, U-3) — a `<Show>`
506
+ // mounting it over a landing included.
507
+ freshHook?.(e, !l && flushTransaction);
455
508
  }
456
509
  }
457
510
  // insertSubs only walks _subs (no scheduling of its own), so a
458
511
  // subscriber-less node has nothing to notify.
459
- if (e.t !== null) {
512
+ if (e.o !== null) {
460
513
  insertSubs(e);
461
514
  // Lane work re-staged: the lane's members re-derive as its work.
462
- if (c !== null) laneDirty(e, c);
515
+ if (C !== null) laneDirty(e, C);
463
516
  }
464
- } else if (e.it != N) {
465
- for (let t = e.t; t !== null; t = t.o) insertIntoHeapHeight(t.l, dirtyQueue);
517
+ } else if (e.Et != c) {
518
+ for (let t = e.o; t !== null; t = t.t) insertIntoHeapHeight(t.u, dirtyQueue);
466
519
  }
467
520
  // Silent recovery: errored → unchanged value fires no notification, but
468
521
  // dependents still holding the propagated error consumed their dirty flag
469
522
  // in an errored run and may sit on stale commits (#2949). Changed-value
470
523
  // recoveries ride insertSubs above; a comparator throw re-errored the node
471
524
  // (el._x?._error re-set), so this only runs on a genuinely clean recovery.
472
- if (!r && !e.m?.F) {
525
+ if (!s && !e.T?.F) {
473
526
  if (u !== undefined) settleErroredDependents(e, u);
474
527
  // Self-registration (this node's own superseded flight) is the #3181
475
528
  // sweep's business below — retiring it here too would walk twice.
476
- if (o) for (const t of o) if (t !== e) settlePendingSource(e, t);
529
+ if (a) for (const t of a) if (t !== e) settlePendingSource(e, t);
477
530
  }
478
531
  // #3181: a synchronous settle supersedes the old landing callback, so
479
532
  // recompute owns its pending-source sweep. An uninitialized node without
480
533
  // a replacement source still has no truth to reveal and must stay parked.
481
- if (_ && !(e.S & (STATUS_PENDING | STATUS_UNINITIALIZED))) settlePendingSource(e);
534
+ if (I && !(e.m & (STATUS_PENDING | STATUS_UNINITIALIZED))) settlePendingSource(e);
482
535
  }
483
536
  // A first pass that went pending where the born-held arm would have held
484
537
  // its value (it read a held node, or a verdict lane's mount read a staging,
485
538
  // #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) {
539
+ // for it (A29, #3800). A boundary that has not shown content catches it
540
+ // and it is held all the same — its answer reveals no earlier than the
541
+ // hold's commit (D-12) — but the hold does not wait for it: its only
542
+ // readers are behind the fallback (`_hidden`; F-4, #3540). A boundary
543
+ // re-armed by `on` owns what it collects outright (D-14): not held.
544
+ if (t && C === null && R && e.m & STATUS_PENDING) {
489
545
  const t = flushTransaction ?? passTx;
490
- if (t !== null && !t.B && !GlobalQueue.fe?.(e)) holdNode(e, t);
546
+ if (t && !t.V && freshHook?.(e) !== CAUGHT_REARMED) holdNode(e, t);
491
547
  }
492
548
  // Dependencies are the committed frame's until it is replaced (A30, #3410):
493
549
  // a pass that staged its value leaves the previous pass's tail linked for
@@ -506,29 +562,29 @@ function recompute(e, t = false) {
506
562
  // kept at a park — one spurious recompute at most). A creation pass and a
507
563
  // tracked effect trim now: their frames are replaceable like a direct
508
564
  // commit, and a tracked effect's spurious run would be user-visible.
509
- if (!e.m?.F && e.Oe === NOT_PENDING && !(n && e.lt)) {
565
+ if (!e.T?.F && e.ue === NOT_PENDING && !(n && e.Qe)) {
510
566
  if (t || n === EFFECT_TRACKED) trimStaleDeps(e); else heldTrim(e);
511
567
  }
512
568
  // Observe: the posture the run executed under — lane work (optimistic:
513
569
  // overlay, never waste), a transaction's pass (held), or plain.
514
- if (attrHooks !== null) attrHooks.recomputeEnd(e, t, E, c !== null, flushTransaction !== null || passTx !== null || (e.i & CONFIG_HELD) !== 0, e.Oe !== NOT_PENDING);
570
+ if (attrHooks !== null) attrHooks.recomputeEnd(e, t, i, C !== null, flushTransaction !== null || passTx !== null || (e.l & CONFIG_HELD) !== 0, e.ue !== NOT_PENDING);
515
571
  // A staged value, a parked frame (L2: the commit retires it), or status the
516
572
  // commit sweep must settle (a pending or uninitialized pass), queues the
517
573
  // node for this flush's commit. A first pass queues only when pending or
518
574
  // born held: otherwise its value published directly. A queued pass's
519
575
  // children are uncommitted (CONFIG_STAGED) until that commit.
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;
576
+ if (e.ue !== NOT_PENDING || e.T !== null && (e.T.Nt !== null || e.T.ft !== null) || (e.m & (STATUS_PENDING | STATUS_UNINITIALIZED)) !== 0 && (!t || (e.m & STATUS_PENDING) !== 0)) {
577
+ e.l |= CONFIG_STAGED;
522
578
  // Lane work is the lane's to reveal (its seam), not this flush's commit.
523
- if (c === null) queuePendingNode(e);
579
+ if (C === null) queuePendingNode(e);
524
580
  }
525
- setPassLane(l);
581
+ setPassLane(s);
526
582
  // Missed-wake reschedule (see the finally above): values this pass read
527
583
  // before the nested commit are stale, so run again now that the heap will
528
584
  // accept the node. Equality gates stop same-value landings from cascading,
529
585
  // and a re-run only latches again if another nested commit changes a dep
530
586
  // beneath it — convergent unless deps genuinely keep changing.
531
- if (f) {
587
+ if (A) {
532
588
  enqueueSub(e);
533
589
  schedule();
534
590
  }
@@ -543,9 +599,9 @@ function updateIfNecessary(e) {
543
599
  // would resurrect it (#2983) — readers serve its last value.
544
600
  if (e.R & (REACTIVE_RECOMPUTING_DEPS | REACTIVE_DISPOSED)) return;
545
601
  if (e.R & REACTIVE_CHECK) {
546
- for (let t = e.G; t; t = t._) {
547
- const n = t.I;
548
- if (n.ee) {
602
+ for (let t = e.C; t; t = t.G) {
603
+ const n = t.S;
604
+ if (n.X) {
549
605
  updateIfNecessary(n);
550
606
  }
551
607
  if (e.R & REACTIVE_DIRTY) {
@@ -553,7 +609,7 @@ function updateIfNecessary(e) {
553
609
  }
554
610
  }
555
611
  }
556
- if (e.R & REACTIVE_DIRTY || e.m?.F && e.Ie < clock && !e.m?.Te) {
612
+ if (e.R & REACTIVE_DIRTY || e.T?.F && e.ie < clock && !e.T?.Se) {
557
613
  recompute(e);
558
614
  }
559
615
  // The guard above refused an already-disposed node; the recompute it just
@@ -572,79 +628,81 @@ function computed(e, t) {
572
628
  // `in` (not `!== undefined`): an explicit `loadingValue: undefined` on a
573
629
  // `T | undefined` node is a real commit #0. The typeof guard tolerates
574
630
  // non-object option values that older call shapes force through `as any`.
575
- const l = t !== null && typeof t === "object" && "loadingValue" in t;
631
+ const o = t !== null && typeof t === "object" && "loadingValue" in t;
576
632
  // Two literals, one per tier, selected at build time (the observe flag is
577
633
  // a literal after replacement; the untaken branch is dead code). The observe
578
- // literal is the prod literal plus its `_name` slot — a slot in the
579
- // boilerplate, because a post-construction `self._name = …` forces a
634
+ // literal is the prod literal plus its `_name` and `_loc` slots — slots in
635
+ // the boilerplate, because a post-construction `self._name = …` forces a
580
636
  // hidden-class transition and an out-of-object property store on EVERY node
581
637
  // (measured: the whole of the observe tier's creation overhead). Keep the
582
638
  // two in sync — the dist artifact test pins observe's key set to prod's
583
- // plus `_name`.
584
- const E = {
639
+ // plus the slots.
640
+ const s = {
585
641
  id: inheritId(t, n, context),
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) | (
642
+ l: (n ? CONFIG_TRANSPARENT : 0) | (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (!context || t?.lazy ? CONFIG_AUTO_DISPOSE : 0) | (t?.sync ? CONFIG_SYNC : 0) | (t?.le ? CONFIG_NO_SNAPSHOT : 0) | (
587
643
  // Plumbing is an observe-tier notion (a name and records to
588
644
  // withhold); the prod literal never carries the bit.
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,
596
- it: 0,
597
- Ot: undefined,
645
+ t?.Tt ? CONFIG_PLUMBING : 0) | (t?.St ?? 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
646
+ Ye: t?.equals ?? isEqual,
647
+ rt: null,
648
+ A: context?.A ?? defaultContext,
649
+ ct: 0,
650
+ X: e,
651
+ Ie: o ? t.loadingValue : undefined,
652
+ Et: 0,
653
+ At: undefined,
598
654
  Rt: null,
599
- G: null,
600
- p: null,
601
- pe: 0,
602
- t: null,
603
- Dt: null,
655
+ C: null,
656
+ _: null,
657
+ Ne: 0,
658
+ o: null,
659
+ dt: null,
604
660
  _parent: context,
605
- tn: null,
606
- Gt: null,
607
- nn: null,
661
+ Xe: null,
662
+ Ct: null,
663
+ ze: null,
608
664
  R: t?.lazy ? REACTIVE_LAZY : REACTIVE_NONE,
609
- S: l ? 0 : STATUS_UNINITIALIZED,
610
- Ie: clock,
611
- Oe: NOT_PENDING,
612
- Pt: -1,
613
- De: l,
614
- m: null,
665
+ m: o ? 0 : STATUS_UNINITIALIZED,
666
+ ie: clock,
667
+ ue: NOT_PENDING,
668
+ Ot: -1,
669
+ ce: o,
670
+ T: null,
615
671
  // The slot is always present (hidden class); plumbing leaves it
616
672
  // unset, which `ownerPath` skips.
617
- _name: t?.At ? undefined : t?.name ?? "computed",
673
+ _name: t?.Tt ? undefined : t?.name ?? "computed",
674
+ _loc: t?.loc,
618
675
  // Observe: the verdict windows this pass entered (verdict.ts) — a
619
676
  // slot, so the stamp is never a write after construction.
620
- Ze: 0
677
+ Ve: 0
621
678
  };
622
- if (t?.unobserved) ext(E).Ft = t.unobserved;
623
- setupComputedNode(E, t);
624
- return E;
679
+ if (t?.unobserved) ext(s).Dt = t.unobserved;
680
+ setupComputedNode(s, t);
681
+ return s;
625
682
  }
626
683
 
627
684
  /** Lazily allocate a node's cold extension (ONE shape for signals and
628
685
  * computeds — `_x` access stays monomorphic). Installers write through
629
686
  * this; hot paths read `el._x?._field` gated by the _config presence bits.
630
687
  * Never call ext() just to store a field's default. */ function ext(e) {
631
- return e.m ??= {
632
- Te: null,
633
- Ne: null,
688
+ return e.T ??= {
689
+ Se: null,
690
+ ae: null,
634
691
  F: undefined,
635
- de: undefined,
636
- Se: undefined,
637
- Ft: undefined,
638
- ot: undefined,
639
- St: null,
640
- ct: null,
641
- _t: null,
642
- It: false,
643
- ht: NOT_PENDING,
644
- Vt: -1,
645
- _e: 0,
646
- B: NOT_PENDING,
647
- T: 0
692
+ se: undefined,
693
+ fe: undefined,
694
+ Dt: undefined,
695
+ lt: undefined,
696
+ Nt: null,
697
+ ft: null,
698
+ Je: null,
699
+ ut: false,
700
+ Gt: NOT_PENDING,
701
+ Ft: -1,
702
+ Ee: 0,
703
+ V: NOT_PENDING,
704
+ I: 0,
705
+ de: undefined
648
706
  };
649
707
  }
650
708
 
@@ -665,7 +723,7 @@ function computed(e, t) {
665
723
  // catches it, the boundary's; otherwise the tick joins the hold (#3540:
666
724
  // the reader's position, not where the memo was created). Its catch, if
667
725
  // 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));
726
+ if (t.m & STATUS_UNINITIALIZED || e.i !== EFFECT_RENDER) joinPassTx(txOf(t), !e.i && e.m & STATUS_UNINITIALIZED || freshHook?.(e));
669
727
  }
670
728
 
671
729
  /** A15's stale reader (shared-hole and reveal corollaries): a render effect
@@ -702,14 +760,16 @@ function computed(e, t) {
702
760
  // read the screen. Lane work with no committed value yet reads a flight
703
761
  // as a mount's memo does: it enters, and the boundary it mounts catches
704
762
  // the pending (#3540).
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;
707
- const l = txOf(t);
763
+ // (Verdict readers and lanes exist only with their engines installed.)
764
+ const n = observeFlightHook && e.l & CONFIG_VERDICT;
765
+ const o = laneStageHook && passLane !== null;
766
+ if ((!o || e.m & STATUS_UNINITIALIZED && t.m & STATUS_PENDING) && !n && (e.i !== EFFECT_RENDER || t.l & CONFIG_INPUTS_PUBLISHED)) return false;
767
+ const s = txOf(t);
708
768
  // Lane work and verdict readers are never the transaction's pass, whichever
709
769
  // flush runs it: a node T holds re-enters T when it re-runs (`recompute`'s
710
770
  // head), and the pass may then read a guess and become the lane's.
711
- if (l === flushTransaction && passLane === null && !n || bornInto(e, l)) return false;
712
- staleReader(e, l);
771
+ if (s === flushTransaction && !o && !n || bornInto(e, s)) return false;
772
+ staleReader(e, s);
713
773
  return true;
714
774
  }
715
775
 
@@ -718,12 +778,12 @@ function computed(e, t) {
718
778
  * held passes built (CONFIG_HELD_FRAME). Its branch exists only in `t`: it
719
779
  * has no screen to read, so it reads `t`'s values, whichever flush runs it,
720
780
  * 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;
781
+ return (e.m & STATUS_UNINITIALIZED || e.l & CONFIG_HELD_FRAME) && e.l & CONFIG_HELD && txOf(e) === t;
722
782
  }
723
783
 
724
784
  /** The computed whose pass a creation under `owner` belongs to (a root's is
725
785
  * its nearest computed ancestor's), or null outside every pass. */ function creatorPass(e) {
726
- return e !== null && e.Ht ? e.gt : e;
786
+ return e !== null && e.Pt ? e.Ht : e;
727
787
  }
728
788
 
729
789
  /** L2 — park the previous pass's frame for this node's commit: the children
@@ -732,11 +792,11 @@ function computed(e, t) {
732
792
  * the frame that replaces them (`commitPendingNode`) or when the owner does. */ function parkChildren(e) {
733
793
  markDisposal(e);
734
794
  const t = ext(e);
735
- t.ct = e.Re;
736
- t.St = e.nn;
737
- e.Re = null;
738
- e.nn = null;
739
- e.dt = 0;
795
+ t.ft = e.rt;
796
+ t.Nt = e.ze;
797
+ e.rt = null;
798
+ e.ze = null;
799
+ e.ct = 0;
740
800
  }
741
801
 
742
802
  /**
@@ -744,58 +804,59 @@ function computed(e, t) {
744
804
  * so V8 sees the full hidden class shape at construction time. Effects always run in lazy
745
805
  * mode (recompute is called explicitly by `effect()`), so we hardcode the lazy bits and skip
746
806
  * the auto-dispose CONFIG bit (effect() previously cleared it post-construction).
747
- */ function createEffectNode(e, t, n, l, E) {
748
- const s = E?.transparent ?? false;
807
+ */ function createEffectNode(e, t, n, o, s) {
808
+ const E = s?.transparent ?? false;
749
809
  // Prod and observe boilerplates — see computed() for why the observe tier
750
810
  // gets its `_name` as a literal slot rather than a write after the fact.
751
811
  // The default label is the node kind (tracked effects relabel their computed
752
812
  // in trackedEffect); the wrappers in signals.ts no longer spread a name into
753
813
  // the options to get it.
754
- const u = {
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,
763
- it: 0,
764
- Ot: undefined,
814
+ const l = {
815
+ id: inheritId(s, E, context),
816
+ l: (E ? CONFIG_TRANSPARENT : 0) | (s?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (s?.sync ? CONFIG_SYNC : 0) | (s?.St ?? 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
817
+ Ye: false,
818
+ rt: null,
819
+ A: context?.A ?? defaultContext,
820
+ ct: 0,
821
+ X: e,
822
+ Ie: undefined,
823
+ Et: 0,
824
+ At: undefined,
765
825
  Rt: null,
766
- G: null,
767
- p: null,
768
- pe: 0,
769
- t: null,
770
- Dt: null,
826
+ C: null,
827
+ _: null,
828
+ Ne: 0,
829
+ o: null,
830
+ dt: null,
771
831
  _parent: context,
772
- tn: null,
773
- Gt: null,
774
- nn: null,
832
+ Xe: null,
833
+ Ct: null,
834
+ ze: null,
775
835
  R: REACTIVE_LAZY,
776
- S: STATUS_UNINITIALIZED,
777
- Ie: clock,
778
- Oe: NOT_PENDING,
779
- Pt: -1,
780
- De: false,
781
- lt: false,
782
- Nt: undefined,
783
- Ut: t,
784
- Lt: n,
836
+ m: STATUS_UNINITIALIZED,
837
+ ie: clock,
838
+ ue: NOT_PENDING,
839
+ Ot: -1,
840
+ ce: false,
841
+ Qe: false,
842
+ _t: undefined,
843
+ Ge: t,
844
+ ht: n,
785
845
  kt: undefined,
786
- u: l,
787
- m: null,
788
- _name: E?.name ?? "effect",
789
- Ze: 0
846
+ i: o,
847
+ T: null,
848
+ _name: s?.name ?? "effect",
849
+ _loc: s?.loc,
850
+ Ve: 0
790
851
  };
791
852
  // Effects dispatch status through the SHARED notifier (statusNotifierOf,
792
853
  // keyed off _type) — storing it per node forced a full NodeExtension
793
854
  // allocation on EVERY effect at creation (an alloc + 19 field stores,
794
855
  // +23% effect creation, caught by the creation benches). Only genuinely
795
856
  // per-node channels (boundaries) live on _x.
796
- if (E?.unobserved) ext(u).Ft = E.unobserved;
797
- setupComputedNode(u, lazyOptions);
798
- return u;
857
+ if (s?.unobserved) ext(l).Dt = s.unobserved;
858
+ setupComputedNode(l, lazyOptions);
859
+ return l;
799
860
  }
800
861
 
801
862
  /**
@@ -814,7 +875,7 @@ function setEffectStatusNotify(e) {
814
875
  * 0, and only effect literals carry the field) get the shared notifier.
815
876
  * Presence doubles as the "display consumer" membership test in the status
816
877
  * walks, exactly as the per-node field did when every effect carried one. */ function statusNotifierOf(e) {
817
- return e.u ? effectStatusNotify ?? undefined : undefined;
878
+ return e.i ? effectStatusNotify ?? undefined : undefined;
818
879
  }
819
880
 
820
881
  const lazyOptions = {
@@ -823,15 +884,15 @@ const lazyOptions = {
823
884
 
824
885
  function setupComputedNode(e, t) {
825
886
  e.Rt = e;
826
- const n = context?.Ht ? context.gt : context;
887
+ const n = context?.Pt ? context.Ht : context;
827
888
  if (context) linkChild(context, e);
828
- if (n) e.it = n.it + 1;
829
- GlobalQueue.xt?.(e);
889
+ if (n) e.Et = n.Et + 1;
890
+ wireExternalSourceHook?.(e);
830
891
  !t?.lazy && recompute(e, true);
831
892
  if (snapshotCaptureActive && !t?.lazy) {
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;
893
+ if (!(e.m & STATUS_PENDING) && !(e.l & CONFIG_NO_SNAPSHOT)) {
894
+ ext(e).lt = e.Ie === undefined ? NO_SNAPSHOT : e.Ie;
895
+ e.l |= CONFIG_HAS_SNAPSHOT;
835
896
  snapshotSources.add(e);
836
897
  }
837
898
  }
@@ -839,26 +900,27 @@ function setupComputedNode(e, t) {
839
900
 
840
901
  function signal(e, t) {
841
902
  // Prod and observe boilerplates — see computed(). The observe literal adds
842
- // `_name` and `_owner` (the creating owner, stamped by registerGraph for
903
+ // `_name`, `_loc` and `_owner` (the creating owner, stamped by registerGraph for
843
904
  // createSignal nodes so ownerPath can locate signal subjects; null here,
844
905
  // and staying null on internal signals — one shape either way).
845
906
  const n = {
846
- en: t?.equals ?? isEqual,
847
- i: (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (t?.ae ? CONFIG_NO_SNAPSHOT : 0),
848
- Ae: e,
849
- t: null,
850
- Dt: null,
851
- Ie: clock,
852
- Oe: NOT_PENDING,
853
- Pt: -1,
854
- m: null,
907
+ Ye: t?.equals ?? isEqual,
908
+ l: (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (t?.le ? CONFIG_NO_SNAPSHOT : 0),
909
+ Ie: e,
910
+ o: null,
911
+ dt: null,
912
+ ie: clock,
913
+ ue: NOT_PENDING,
914
+ Ot: -1,
915
+ T: null,
855
916
  _name: t?.name ?? "signal",
856
- L: null
917
+ _loc: t?.loc,
918
+ H: null
857
919
  };
858
- if (t?.unobserved) ext(n).Ft = t.unobserved;
859
- if (snapshotCaptureActive && !(n.i & CONFIG_NO_SNAPSHOT)) {
860
- ext(n).ot = e === undefined ? NO_SNAPSHOT : e;
861
- n.i |= CONFIG_HAS_SNAPSHOT;
920
+ if (t?.unobserved) ext(n).Dt = t.unobserved;
921
+ if (snapshotCaptureActive && !(n.l & CONFIG_NO_SNAPSHOT)) {
922
+ ext(n).lt = e === undefined ? NO_SNAPSHOT : e;
923
+ n.l |= CONFIG_HAS_SNAPSHOT;
862
924
  snapshotSources.add(n);
863
925
  }
864
926
  return n;
@@ -877,26 +939,28 @@ function isEqual(e, t) {
877
939
  * object, no NodeExtension, no post-construction expandos. `ownedWrite` is
878
940
  * baked in: the setter carries the owned-scope guard; node-level writes are
879
941
  * the store's notification machinery. The observe literal adds the label
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,
885
- t: null,
886
- Dt: null,
887
- Ie: clock,
888
- Oe: NOT_PENDING,
889
- Pt: -1,
890
- m: null,
891
- yt: n,
892
- bt: l,
893
- acc: E,
942
+ * and location slots (the store relabels `store.<key>` and stamps the
943
+ * store's `loc` when the engine is installed). */ function slotSignal(e, t, n, o, s) {
944
+ const E = {
945
+ Ye: t,
946
+ l: CONFIG_OWNED_WRITE | CONFIG_SLOT_NODE,
947
+ Ie: e,
948
+ o: null,
949
+ dt: null,
950
+ ie: clock,
951
+ ue: NOT_PENDING,
952
+ Ot: -1,
953
+ T: null,
954
+ O: n,
955
+ Vt: o,
956
+ acc: s,
894
957
  px: undefined,
895
958
  pxv: undefined,
896
959
  _name: "signal",
897
- L: null
960
+ _loc: undefined,
961
+ H: null
898
962
  };
899
- return s;
963
+ return E;
900
964
  }
901
965
 
902
966
  /**
@@ -918,6 +982,16 @@ function setVerdict(e) {
918
982
  * Use `untrack` inside a memo or effect when you need to read a signal once
919
983
  * without making the surrounding computation depend on its future changes.
920
984
  *
985
+ * `untrack` stops dependency tracking, not async settlement. Reading an
986
+ * uninitialized async source still suspends the owning computation; it runs
987
+ * again when that source first resolves. Afterward, the untracked read is a
988
+ * snapshot and does not subscribe the computation to later changes.
989
+ *
990
+ * If the first read must return immediately, give the async source a
991
+ * `loadingValue` before reading it with `untrack`. `isPending` and `latest`
992
+ * inspect updates to an existing value; neither supplies a missing initial
993
+ * value.
994
+ *
921
995
  * Pass a `strictReadLabel` string to enable a dev-mode warning: any reactive
922
996
  * read inside `fn` that isn't inside a nested tracking scope will log a
923
997
  * warning naming the label.
@@ -931,13 +1005,15 @@ function setVerdict(e) {
931
1005
  * log(snapshot);
932
1006
  * }
933
1007
  * );
1008
+ *
1009
+ * const color = createMemo<string>(() => fetchColor(), { loadingValue: "gray" });
1010
+ * const initialColor = untrack(color); // "gray" while the fetch is unresolved
934
1011
  * ```
935
1012
  */ function untrack(e, t) {
936
- if (GlobalQueue.Wt === null && !tracking && true) return e();
937
1013
  const n = tracking;
938
1014
  tracking = false;
939
1015
  try {
940
- if (GlobalQueue.Wt) return GlobalQueue.Wt(e);
1016
+ if (externalUntrackHook) return externalUntrackHook(e);
941
1017
  return e();
942
1018
  } finally {
943
1019
  tracking = n;
@@ -971,13 +1047,13 @@ function setVerdict(e) {
971
1047
  // pay-for-use contract. Owner-lifecycle nodes are dead: recomputing would
972
1048
  // re-run user code in a torn-down tree (and discard manual writes on
973
1049
  // derived-writable signals), so reads return the last committed value.
974
- if (e.i & CONFIG_AUTO_DISPOSE) {
1050
+ if (e.l & CONFIG_AUTO_DISPOSE) {
975
1051
  const t = e._parent;
976
1052
  if (t !== null) {
977
1053
  // A dormant node was off the chain when its owner died, so the strip
978
1054
  // in disposeChildren missed it: freeze here instead (#3024).
979
1055
  if (t.R & REACTIVE_DISPOSED) {
980
- e.i &= ~CONFIG_AUTO_DISPOSE;
1056
+ e.l &= ~CONFIG_AUTO_DISPOSE;
981
1057
  return;
982
1058
  }
983
1059
  // A zombie never left its chain (the parked frame is drained whole,
@@ -1005,7 +1081,7 @@ function setVerdict(e) {
1005
1081
  * never the graph — A32).
1006
1082
  * False means the reader derives from the staged value.
1007
1083
  */ function readerSeesCommitted(e, t) {
1008
- return !!(!t || e.Oe === NOT_PENDING || t.i & CONFIG_CHILDREN_FORBIDDEN);
1084
+ return !t || e.ue === NOT_PENDING || t.l & CONFIG_CHILDREN_FORBIDDEN;
1009
1085
  }
1010
1086
 
1011
1087
  /** A28 — set when a node is staged (queuePendingNode) OUTSIDE a flush;
@@ -1026,21 +1102,21 @@ function markUnflushedStaged() {
1026
1102
  * is unflushed. Exempt: promoted writes (A28 (4): a write issued inside a
1027
1103
  * recompute is promoted at that recompute's end — boundary and loading
1028
1104
  * machinery, signals declared for in-computation writes). */ function unflushedValue(e) {
1029
- if (globalQueue.X || e.Oe === NOT_PENDING || e.i & CONFIG_PROMOTED) return NOT_PENDING;
1105
+ if (globalQueue.q || e.ue === NOT_PENDING || e.l & CONFIG_PROMOTED) return NOT_PENDING;
1030
1106
  // A held node (L2) carries a staged value between flushes too, but it was
1031
1107
  // flushed — and parked. Not an unflushed write — unless rewritten since:
1032
1108
  // then the flushed staging is what the world sees until the next flush.
1033
- if (e.i & CONFIG_HELD) return e.m.Vt === clock ? e.m.ht : NOT_PENDING;
1034
- return e.Ae;
1109
+ if (e.l & CONFIG_HELD) return e.T.Ft === clock ? e.T.Gt : NOT_PENDING;
1110
+ return e.Ie;
1035
1111
  }
1036
1112
 
1037
1113
  /** A28: a held node rewritten outside a flush keeps the staging the last
1038
1114
  * flush left (`_flushed`) until the flush that carries the rewrite — the
1039
1115
  * stash is stamped with the clock, which that flush advances. */ function stashFlushed(e) {
1040
- const t = e.m;
1041
- if (t.Vt !== clock) {
1042
- t.ht = e.Oe;
1043
- t.Vt = clock;
1116
+ const t = e.T;
1117
+ if (t.Ft !== clock) {
1118
+ t.Gt = e.ue;
1119
+ t.Ft = clock;
1044
1120
  unflushedStaged = true;
1045
1121
  }
1046
1122
  }
@@ -1060,7 +1136,7 @@ function resyncUnflushedCompanions() {
1060
1136
  unflushedStaged = false;
1061
1137
  // Length-guarded: the common flush has nothing here and allocates nothing.
1062
1138
  if (promotedWrites.length !== 0) {
1063
- for (const e of promotedWrites) e.i &= ~CONFIG_PROMOTED;
1139
+ for (const e of promotedWrites) e.l &= ~CONFIG_PROMOTED;
1064
1140
  promotedWrites.length = 0;
1065
1141
  }
1066
1142
  }
@@ -1068,7 +1144,7 @@ function resyncUnflushedCompanions() {
1068
1144
  /** A tracked read's pull of a computed: bring it current when the flush has
1069
1145
  * reached its height (or the reader is a fresh-pull waiter), and sit the
1070
1146
  * reader above it. */ function pullComputed(e, t) {
1071
- if (e.it >= dirtyQueue.ut) {
1147
+ if (e.Et >= dirtyQueue.st) {
1072
1148
  markNode(t);
1073
1149
  markHeap(dirtyQueue);
1074
1150
  updateIfNecessary(e);
@@ -1079,27 +1155,27 @@ function resyncUnflushedCompanions() {
1079
1155
  // never read the PRE-re-ask value as settled. Self-guarded: a clean
1080
1156
  // node no-ops and updateIfNecessary refuses disposed nodes (#2983) —
1081
1157
  // a dead target serves its last value, which is already quiescent.
1082
- else if (t.i & CONFIG_FRESH_READ) updateIfNecessary(e);
1083
- const n = e.it;
1158
+ else if (t.l & CONFIG_FRESH_READ) updateIfNecessary(e);
1159
+ const n = e.Et;
1084
1160
  // parent check is shallow, might need to be recursive
1085
- if (n >= t.it && e._parent !== t) t.it = n + 1;
1161
+ if (n >= t.Et && e._parent !== t) t.Et = n + 1;
1086
1162
  }
1087
1163
 
1088
1164
  function read(e) {
1089
1165
  let t = context;
1090
- if (t?.Ht) t = t.gt;
1166
+ if (t?.Pt) t = t.Ht;
1091
1167
  const n = e;
1092
1168
  // CARVE 1: `owner` was `el._firewall || el` — a store leaf's status,
1093
1169
  // height and error were read off its projection computed. (Typed loosely
1094
1170
  // as the `||` expression was: a signal's `_statusFlags`/`_height` read as
1095
1171
  // undefined and mask to 0 on every site below.)
1096
- const l = e;
1097
- if (typeof n.ee === "function") prepareComputed(e);
1172
+ const o = e;
1173
+ if (typeof n.X === "function") prepareComputed(e);
1098
1174
  // The verdict windows (`latest`, `isPending`; verdict.ts): one cold path,
1099
1175
  // off this one, present only while a window is open.
1100
1176
  if (verdict !== null) return verdict(e, t);
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
1177
+ if (!n.X && e.T?.lt === undefined && !snapshotCaptureActive && (!unflushedStaged || e.ue === NOT_PENDING) && // A28, see readNodeFast
1178
+ !(e.l & CONFIG_OVERRIDE) && // lanes: the slow path's one arm
1103
1179
  true) {
1104
1180
  if (t) {
1105
1181
  if (tracking) link(e, t);
@@ -1113,38 +1189,47 @@ function read(e) {
1113
1189
  // stays out too (rule 3, `frameRead`). The pass remembers it read the
1114
1190
  // future (REACTIVE_JOINED): a first pass that did is born held (A29),
1115
1191
  // wherever it was created. Lane work sees the screen (`frameRead`).
1116
- if (e.i & CONFIG_HELD && !(t.i & CONFIG_CHILDREN_FORBIDDEN)) {
1117
- if (frameRead(t, e)) return e.Ae;
1192
+ if (e.l & CONFIG_HELD && !(t.l & CONFIG_CHILDREN_FORBIDDEN)) {
1193
+ if (frameRead(t, e)) return e.Ie;
1118
1194
  joinPass(t, e);
1119
1195
  }
1120
1196
  }
1121
1197
  // Committed visibility for untracked and children-forbidden readers.
1122
- if (!t || e.Oe === NOT_PENDING || t.i & CONFIG_CHILDREN_FORBIDDEN) return e.Ae;
1198
+ if (!t || e.ue === NOT_PENDING || t.l & CONFIG_CHILDREN_FORBIDDEN) return e.Ie;
1123
1199
  // (A plain signal is never uninitialized — the carve-out is `serve`'s.)
1124
- if (t.i & CONFIG_VERDICT && stagedScreen(t)) return e.Ae;
1200
+ if (observeFlightHook && t.l & CONFIG_VERDICT && stagedScreen(t)) return e.Ie;
1125
1201
  stagedRead(t);
1126
- return e.Oe;
1202
+ return e.ue;
1127
1203
  }
1128
1204
  // Rule 3: this read serves the committed value to a render effect outside a
1129
1205
  // parking flush (decided after the pull below — a pull can join).
1130
- let E = false;
1206
+ let s = false;
1131
1207
  if (t) {
1132
1208
  if (tracking) {
1133
1209
  link(e, t);
1134
- if (l.ee) pullComputed(l, t);
1210
+ if (o.X) {
1211
+ pullComputed(o, t);
1212
+ // F-6 (#4050): a memo over a verdict reader's value derives from a
1213
+ // display-ahead read — a verdict reader from this pass's end, as if
1214
+ // it probed inline: it reads held values as the screen and shows the
1215
+ // verdict now. Not an effect: a render effect is a frame reader
1216
+ // already, and a user effect's posture is its own reads'.
1217
+ if (!t.i) t.R |= o.l & CONFIG_VERDICT;
1218
+ }
1135
1219
  }
1136
1220
  // L2, as in the fast block above (tracked or not). A node born into the
1137
1221
  // future (uninitialized) has no committed value: every reader derives
1138
1222
  // from the future there, a render effect included (A29: a stale reader
1139
1223
  // of it cannot fall back to the committed frame and enters instead).
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;
1224
+ if (e.l & CONFIG_HELD && !(e.l & CONFIG_OVERRIDE) && !(t.l & CONFIG_CHILDREN_FORBIDDEN)) {
1225
+ if (o.m & STATUS_UNINITIALIZED || !frameRead(t, e)) joinPass(t, e); else s = true;
1142
1226
  }
1143
1227
  }
1144
1228
  // Lanes: a lane's node (after the pull — the node is current). NOT_PENDING
1145
1229
  // falls through: lane work's read of a pending member throws like any.
1146
- if (e.i & CONFIG_OVERRIDE) {
1147
- const n = GlobalQueue.Zt(t, e);
1230
+ // (CONFIG_OVERRIDE is the lane engine's: no engine, no lane's node.)
1231
+ if (laneReadHook && e.l & CONFIG_OVERRIDE) {
1232
+ const n = laneReadHook(t, e);
1148
1233
  if (n !== NOT_PENDING) return n;
1149
1234
  }
1150
1235
  // A stale reader of a held flight (rule 3, A15's reveal corollary) is served
@@ -1161,52 +1246,56 @@ function read(e) {
1161
1246
  // — unless the flight committed beneath inputs already on screen
1162
1247
  // (`observeFlight`), or the reader was born into the flight's transaction
1163
1248
  // (#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)) {
1249
+ if (o.m & STATUS_PENDING && !s && !(e.l & CONFIG_OVERRIDE) && !(o.m & STATUS_UNINITIALIZED)) {
1165
1250
  // A reader with no committed value yet sees the flight pending: a
1166
1251
  // boundary a lane mounts shows its fallback (A29's boundary exemption).
1167
- if (passLane !== null && !(t?.S & STATUS_UNINITIALIZED)) E = true; else if (t !== null && t.i & CONFIG_VERDICT) E = GlobalQueue.vt(t, l);
1252
+ // (A lane, and a verdict reader, exist only with their engines installed.)
1253
+ if (laneStageHook && passLane !== null && !(t?.m & STATUS_UNINITIALIZED)) s = true; else if (observeFlightHook && t !== null && t.l & CONFIG_VERDICT) s = observeFlightHook(t, o);
1168
1254
  }
1169
- if (l.S & STATUS_PENDING && !E) {
1255
+ if (o.m & STATUS_PENDING && !s) {
1170
1256
  // A reader landing on a pending node throws; an untracked read of an
1171
1257
  // UNINITIALIZED node has no committed value to serve and throws too. A
1172
1258
  // children-forbidden reader (createTrackedEffect / onSettled) sees the
1173
1259
  // frame, not the graph (A32): the committed value, and the NotReady only
1174
1260
  // where there is none (uninitialized).
1175
1261
  if (t) {
1176
- if (!(t.i & CONFIG_CHILDREN_FORBIDDEN) || l.S & STATUS_UNINITIALIZED) {
1262
+ if (!(t.l & CONFIG_CHILDREN_FORBIDDEN) || o.m & STATUS_UNINITIALIZED) {
1177
1263
  // An untracked read of a pending node still re-runs its reader when
1178
1264
  // the node settles — unless nobody is reading (`spectating`, #3528)
1179
1265
  // or the reader is the node itself.
1180
1266
  if (!tracking && !spectating && e !== t) link(e, t);
1181
- throw l.m?.F;
1267
+ throw o.T?.F;
1182
1268
  }
1183
- } else if (l.S & STATUS_UNINITIALIZED) {
1184
- throw l.m?.F;
1269
+ } else if (o.m & STATUS_UNINITIALIZED) {
1270
+ throw o.T?.F;
1185
1271
  }
1186
1272
  }
1187
1273
  // An errored derive throws for every late reader instead of silently
1188
1274
  // serving node values (memo parity, #2897 ruling).
1189
- if (l.ee && l.S & STATUS_ERROR) {
1275
+ if (o.X && o.m & STATUS_ERROR) {
1190
1276
  // Only a genuine reactive re-read may retry an errored async source:
1191
1277
  // - tracking: owned/tracked scope only (never events / `untrack` / effect side-effect phase)
1192
1278
  // - owner._time < clock: only on a later cycle than the one the error was found
1193
- if (tracking && l.Ie < clock) {
1194
- recompute(l);
1279
+ if (tracking && o.ie < clock) {
1280
+ recompute(o);
1195
1281
  return read(e);
1196
- } else throw l.m?.F;
1282
+ } else throw o.T?.F;
1197
1283
  }
1198
- if (snapshotCaptureActive && t && t.i & CONFIG_IN_SNAPSHOT_SCOPE) {
1199
- const n = e.m?.ot;
1284
+ if (snapshotCaptureActive && t && t.l & CONFIG_IN_SNAPSHOT_SCOPE) {
1285
+ const n = e.T?.lt;
1200
1286
  if (n !== undefined) {
1201
- const l = n === NO_SNAPSHOT ? undefined : n;
1202
- const E = e.Oe !== NOT_PENDING ? e.Oe : e.Ae;
1203
- if (E !== l) t.R |= REACTIVE_SNAPSHOT_STALE;
1204
- return l;
1287
+ const o = n === NO_SNAPSHOT ? undefined : n;
1288
+ const s = e.ue !== NOT_PENDING ? e.ue : e.Ie;
1289
+ if (s !== o) t.R |= REACTIVE_SNAPSHOT_STALE;
1290
+ return o;
1205
1291
  }
1206
1292
  }
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) {
1293
+ if (s) return e.Ie;
1294
+ // D-41: an untracked read reads the screen, and a shown error is on it
1295
+ // until the answer that replaces it commits.
1296
+ if (!t && e.T?.de) throw e.T.de;
1297
+ const E = serve(e, t);
1298
+ if (!t && typeof n.X === "function" && e.l & CONFIG_AUTO_DISPOSE && !(o.m & STATUS_PENDING) && !e.o) {
1210
1299
  // Deferred, not inline (#3078): an inline unobserved() here made untracked
1211
1300
  // reads destructive — dispose on this read, full revival recompute on the
1212
1301
  // next — so consecutive reads could answer differently with no write in
@@ -1216,7 +1305,7 @@ function read(e) {
1216
1305
  dormantNodes.add(e);
1217
1306
  schedule();
1218
1307
  }
1219
- return s;
1308
+ return E;
1220
1309
  }
1221
1310
 
1222
1311
  /**
@@ -1233,16 +1322,18 @@ function read(e) {
1233
1322
  // an untracked reader has nothing to serve and holds (A19 exception 1) — a
1234
1323
  // bookkeeping read (`spectate`) likewise, and a children-forbidden reader
1235
1324
  // (A32: the frame, which has nothing here).
1236
- if (e.Oe !== NOT_PENDING && e.S & STATUS_UNINITIALIZED && (!t || spectating || t.i & CONFIG_CHILDREN_FORBIDDEN)) throw new NotReadyError(null);
1325
+ if (e.ue !== NOT_PENDING && e.m & STATUS_UNINITIALIZED && (!t || spectating || t.l & CONFIG_CHILDREN_FORBIDDEN)) throw new NotReadyError(null);
1237
1326
  const n = t && unflushedStaged ? unflushedValue(e) : NOT_PENDING;
1238
1327
  if (n !== NOT_PENDING) {
1239
1328
  markLateLinker(t);
1240
1329
  return n;
1241
1330
  }
1242
- if (readerSeesCommitted(e, t)) return e.Ae;
1243
- if (t.i & CONFIG_VERDICT && !(e.S & STATUS_UNINITIALIZED) && stagedScreen(t)) return e.Ae;
1331
+ if (readerSeesCommitted(e, t)) return e.Ie;
1332
+ if (observeFlightHook && t.l & CONFIG_VERDICT && !(e.m & STATUS_UNINITIALIZED) && stagedScreen(t)) return e.Ie;
1244
1333
  stagedRead(t, e);
1245
- return e.Oe;
1334
+ // D-41: the staged answer is an error (`stageError`).
1335
+ if (e.l & CONFIG_STAGED_ERROR) throw e.ue;
1336
+ return e.ue;
1246
1337
  }
1247
1338
 
1248
1339
  /** A pass read a staging of this flush: it derives from what the flush may
@@ -1257,9 +1348,9 @@ function read(e) {
1257
1348
  * a read of a node born staged at all: it has no committed value to
1258
1349
  * re-derive on, and a re-run would read the same staging and re-queue every
1259
1350
  * round (#3969: nor does it make a later lane read a re-derive, `enterLane`). */ function stagedRead(e, t) {
1260
- if (!(t?.S & STATUS_UNINITIALIZED)) {
1351
+ if (!(t?.m & STATUS_UNINITIALIZED)) {
1261
1352
  e.R |= REACTIVE_STAGED_READ;
1262
- if (passLane !== null) stagedReaders.push(e);
1353
+ if (laneSeamsHook && passLane !== null) stagedReaders.push(e);
1263
1354
  }
1264
1355
  }
1265
1356
 
@@ -1280,8 +1371,8 @@ function read(e) {
1280
1371
  /** A28 (4) — written inside a recompute that runs OUTSIDE a flush (a
1281
1372
  * creation-time compute): promoted at that pass's end, visible to the rest of
1282
1373
  * the block. Cold: only contextual writes reach here. */ function notePromotedWrite(e) {
1283
- if (globalQueue.X || e.i & CONFIG_PROMOTED) return;
1284
- e.i |= CONFIG_PROMOTED;
1374
+ if (globalQueue.q || e.l & CONFIG_PROMOTED) return;
1375
+ e.l |= CONFIG_PROMOTED;
1285
1376
  promotedWrites.push(e);
1286
1377
  }
1287
1378
 
@@ -1293,28 +1384,64 @@ function read(e) {
1293
1384
  * REVEALS — the creation-time arm skips pending computeds for the same
1294
1385
  * reason. Firewall leaves belong to a projection whose compute is the tree's
1295
1386
  * own work, captured (or deliberately not) at creation. */ function captureWriteSnapshot(e, t) {
1296
- if (e.i & CONFIG_NO_SNAPSHOT || e.ee !== undefined || e.m?.ot !== undefined) return;
1297
- ext(e).ot = t === undefined ? NO_SNAPSHOT : t;
1298
- e.i |= CONFIG_HAS_SNAPSHOT;
1387
+ if (e.l & CONFIG_NO_SNAPSHOT || e.X !== undefined || e.T?.lt !== undefined) return;
1388
+ ext(e).lt = t === undefined ? NO_SNAPSHOT : t;
1389
+ e.l |= CONFIG_HAS_SNAPSHOT;
1299
1390
  snapshotSources.add(e);
1300
1391
  }
1301
1392
 
1393
+ /**
1394
+ * A callback run before every signal or store write lands, while set, with
1395
+ * the written signal or the store proxy whose setter runs. The
1396
+ * server-components client sets it while slot occurrences are left
1397
+ * unmounted after hydration (deferred hydration): a write may be one an
1398
+ * unmounted component would have observed, so the client mounts what is
1399
+ * left before the write applies, and clears the hook. `null` otherwise.
1400
+ */ let writeHook = null;
1401
+
1402
+ /** @internal */ function setWriteHook(e) {
1403
+ writeHook = e;
1404
+ }
1405
+
1406
+ /**
1407
+ * `el`'s comparator verdict (`_equals`), the one guarded call for recompute,
1408
+ * setSignal (user setters and async landings) and the seam's
1409
+ * staged-equals-committed check.
1410
+ * - A comparator that read an unresolved async source has not shown the
1411
+ * values equal (#4111): falsy, a change, whose readers suspend in their own
1412
+ * tracked context.
1413
+ * - Any other throw is an error of `el`'s computation (A3, #2837): routed
1414
+ * through the status path so error boundaries contain it, instead of
1415
+ * unwinding a flush or a setter; answered "same" so nothing commits.
1416
+ * @internal
1417
+ */ function isSame(e, t, n) {
1418
+ if (e.Ye === isEqual) return t === n;
1419
+ try {
1420
+ return e.Ye && e.Ye(t, n);
1421
+ } catch (t) {
1422
+ if (!(t instanceof NotReadyError)) return notifyStatus(e, STATUS_ERROR, t), true;
1423
+ }
1424
+ }
1425
+
1302
1426
  function setSignal(e, t) {
1303
- const n = e.Oe === NOT_PENDING ? e.Ae : e.Oe;
1427
+ if (writeHook !== null) writeHook(e);
1428
+ const n = e.ue === NOT_PENDING ? e.Ie : e.ue;
1304
1429
  if (typeof t === "function") t = t(n);
1305
1430
  // Lanes: a write landing on a guess is its truth (A18 — an async guess's
1306
1431
  // own landing), judged against the guess; one landing on a lane's
1307
1432
  // derivation (its flight) is the lane's staging (lanes.ts).
1308
- if (e.i & CONFIG_OVERRIDE) return GlobalQueue.Qt(e, t);
1433
+ if (laneWriteHook && e.l & CONFIG_OVERRIDE) return laneWriteHook(e, t);
1309
1434
  // L2 (T4) / A34 (1): a write to a node whose staged state is held is a
1310
1435
  // second proposal for the slot — the same value again or another — and
1311
1436
  // the two cannot finish at different times: the writer's tick joins the
1312
1437
  // hold, before the equality gate.
1313
- if (e.i & CONFIG_HELD) joinFuture(txOf(e));
1438
+ if (e.l & CONFIG_HELD) joinFuture(txOf(e));
1314
1439
  // Uninitialized check first: the first commit has no previous value, so the
1315
1440
  // user comparator must not run against `undefined` (matches recompute).
1316
- const l = !!(e.S & STATUS_UNINITIALIZED) || !e.en || !e.en(n, t);
1317
- if (!l) return t;
1441
+ // D-41: a write over a shown error is a change whatever the last good
1442
+ // value was — the error leaves at its commit (`commitPendingNode`).
1443
+ const o = !!(e.m & STATUS_UNINITIALIZED) || !!e.T?.de || !isSame(e, n, t);
1444
+ if (!o) return t;
1318
1445
  // Attribution hook: this committed write is where a re-run chain begins.
1319
1446
  if (attrHooks !== null) attrHooks.write(e, n, t);
1320
1447
  // A write during hydration's snapshot capture to a source that has no
@@ -1327,21 +1454,21 @@ function setSignal(e, t) {
1327
1454
  // pass reads a value the server never had. Store leaves written here are
1328
1455
  // plain signals and qualify the same way.
1329
1456
  if (snapshotCaptureActive) captureWriteSnapshot(e, n);
1330
- const E = e.Oe !== NOT_PENDING;
1331
- if (!E) queuePendingNode(e);
1457
+ const s = e.ue !== NOT_PENDING;
1458
+ if (!s) queuePendingNode(e);
1332
1459
  // A28: a held node rewritten outside a flush keeps its flushed staging
1333
1460
  // for every reader until the flush that carries the rewrite.
1334
- else if (e.i & CONFIG_HELD && !globalQueue.X) stashFlushed(e);
1335
- e.Oe = t;
1461
+ else if (e.l & CONFIG_HELD && !globalQueue.q) stashFlushed(e);
1462
+ e.ue = t;
1336
1463
  if (context !== null) notePromotedWrite(e);
1337
1464
  // _time is a computed-only slot (§12e): writing it on a signal would fork
1338
1465
  // the lean shape. Every read site is computed-typed.
1339
- if (e.ee !== undefined) e.Ie = clock;
1466
+ if (e.X !== undefined) e.ie = clock;
1340
1467
  // Staged-rewrite fast path (§12d): a re-write to a node whose subscribers
1341
1468
  // were already walked — and where nothing has recomputed or linked since
1342
1469
  // (epoch) — re-stages the value and stops. The walk is idempotent (subs
1343
1470
  // marked, heap entries flag-guarded, effects queued once).
1344
- if (E && e.Pt === notifyEpoch) return t;
1471
+ if (s && e.Ot === notifyEpoch) return t;
1345
1472
  insertSubs(e);
1346
1473
  schedule();
1347
1474
  return t;
@@ -1366,18 +1493,31 @@ function setSignal(e, t) {
1366
1493
  // of replacing it. A write made under the hold carries the mark and keeps
1367
1494
  // (1)'s last-write-wins. Only the user setter asks; an async landing
1368
1495
  // writing a held node is the transaction's own work.
1369
- const n = e.i & CONFIG_HELD && !(e.R & REACTIVE_MANUAL_WRITE) && txOf(e) !== flushTransaction;
1496
+ const n = e.l & CONFIG_HELD && !(e.R & REACTIVE_MANUAL_WRITE) && txOf(e) !== flushTransaction;
1370
1497
  // The writer read the committed frame; its updater composes on it.
1371
- if (n && typeof t === "function") t = t(e.Ae);
1372
- const l = setSignal(e, t);
1498
+ if (n && typeof t === "function") {
1499
+ const n = t(e.Ie);
1500
+ t = () => n;
1501
+ }
1502
+ // #4110: the write replaces a failed derivation's outcome. It lands without
1503
+ // re-running `_fn`, and nothing else clears STATUS_ERROR, so without this
1504
+ // every later read keeps throwing the error the derivation left behind.
1505
+ // The error stays the display until the write commits (D-41).
1506
+ if (e.m & STATUS_ERROR) {
1507
+ e.T.de = e.T.F;
1508
+ notifyStatus(e, STATUS_NONE, undefined);
1509
+ }
1510
+ t = setSignal(e, t);
1511
+ // A write replaces an error a pass staged (D-41): it is a value again.
1512
+ e.l &= ~CONFIG_STAGED_ERROR;
1373
1513
  if (n) {
1374
1514
  // The one write that re-runs: the staging it replaced was derived from
1375
1515
  // inputs the writer never saw, so the hold re-derives over the write.
1376
1516
  e.R = e.R & ~REACTIVE_CHECK | REACTIVE_DIRTY;
1377
1517
  insertIntoHeap(e, dirtyQueue);
1378
1518
  schedule();
1379
- } else if (e.Oe !== NOT_PENDING) e.R |= REACTIVE_MANUAL_WRITE;
1380
- return l;
1519
+ } else if (e.ue !== NOT_PENDING) e.R |= REACTIVE_MANUAL_WRITE;
1520
+ return t;
1381
1521
  }
1382
1522
 
1383
1523
  /**
@@ -1400,14 +1540,14 @@ function setSignal(e, t) {
1400
1540
  * ```
1401
1541
  */ function runWithOwner(e, t) {
1402
1542
  const n = context;
1403
- const l = tracking;
1543
+ const o = tracking;
1404
1544
  context = e;
1405
1545
  tracking = false;
1406
1546
  try {
1407
1547
  return t();
1408
1548
  } finally {
1409
1549
  context = n;
1410
- tracking = l;
1550
+ tracking = o;
1411
1551
  }
1412
1552
  }
1413
1553
 
@@ -1424,7 +1564,7 @@ function staleValues(e, t = true) {
1424
1564
  * recompute as a quiet re-ask and schedules it; no-ops for non-derived or
1425
1565
  * disposed targets.
1426
1566
  */ function markRefresh(e) {
1427
- if (typeof e.ee === "function" && !(e.R & REACTIVE_DISPOSED)) {
1567
+ if (typeof e.X === "function" && !(e.R & REACTIVE_DISPOSED)) {
1428
1568
  // A manual write never refuses the re-run (A34 rule B): a refresh in the
1429
1569
  // write's own frame re-asks with the write as `prev`, like any source
1430
1570
  // change (#3026 generalized).
@@ -1440,4 +1580,4 @@ function staleValues(e, t = true) {
1440
1580
  }
1441
1581
  }
1442
1582
 
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 };
1583
+ export { bornInto, captureWriteSnapshot, clearSnapshots, computed, context, createEffectNode, effectStatusNotify, ext, isEqual, isSame, joinPass, markLateLinker, markRefresh, markSnapshotScope, markUnflushedStaged, notePromotedWrite, prepareComputed, pullComputed, read, readerSeesCommitted, recompute, releaseSnapshotScope, resyncUnflushedCompanions, runWithOwner, serve, setEffectStatusNotify, setMemo, setSignal, setSnapshotCapture, setVerdict, setWriteHook, signal, slotSignal, snapshotCaptureActive, snapshotSources, spectating, stagedRead, stagedScreen, staleValues, stashFlushed, statusNotifierOf, tracking, unflushedStaged, unflushedValue, untrack, verdict, writeHook };