@solidjs/signals 2.0.0-rc.1 → 2.0.0-rc.10

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 (150) hide show
  1. package/dist/dev-shared.js +7138 -0
  2. package/dist/dev.attribution.js +3307 -0
  3. package/dist/dev.js +5691 -4491
  4. package/dist/observe/affects.js +129 -0
  5. package/dist/observe/attribution.js +7 -0
  6. package/dist/observe/boundaries.js +793 -0
  7. package/dist/observe/core/action.js +177 -0
  8. package/dist/observe/core/async.js +849 -0
  9. package/dist/observe/core/attribution-costs.js +66 -0
  10. package/dist/observe/core/attribution-feedback.js +280 -0
  11. package/dist/observe/core/attribution-hooks.js +83 -0
  12. package/dist/observe/core/attribution-queries.js +43 -0
  13. package/dist/observe/core/attribution.js +2904 -0
  14. package/dist/observe/core/constants.js +283 -0
  15. package/dist/{types-cjs/core/context.d.cts → observe/core/context.js} +37 -12
  16. package/dist/observe/core/core.js +1999 -0
  17. package/dist/observe/core/dev.js +343 -0
  18. package/dist/observe/core/effect.js +200 -0
  19. package/dist/observe/core/error-hooks.js +71 -0
  20. package/dist/observe/core/error.js +80 -0
  21. package/dist/observe/core/external.js +98 -0
  22. package/dist/observe/core/graph.js +155 -0
  23. package/dist/observe/core/heap.js +147 -0
  24. package/dist/observe/core/invariants.js +45 -0
  25. package/dist/observe/core/lanes.js +205 -0
  26. package/dist/observe/core/optimistic.js +627 -0
  27. package/dist/observe/core/owner.js +353 -0
  28. package/dist/observe/core/scheduler.js +1547 -0
  29. package/dist/observe/core/verdict.js +674 -0
  30. package/dist/observe/index.js +59 -0
  31. package/dist/observe/map.js +484 -0
  32. package/dist/observe/signals.js +731 -0
  33. package/dist/observe/store/index.js +44 -0
  34. package/dist/observe/store/next/optimistic.js +717 -0
  35. package/dist/observe/store/next/projection.js +266 -0
  36. package/dist/observe/store/next/reconcile.js +375 -0
  37. package/dist/observe/store/next/store.js +2480 -0
  38. package/dist/observe/store/next/target.js +51 -0
  39. package/dist/observe/store/store.js +331 -0
  40. package/dist/observe/store/storePath.js +103 -0
  41. package/dist/observe/store/utils.js +1070 -0
  42. package/dist/prod/affects.js +10 -9
  43. package/dist/prod/attribution.js +45 -0
  44. package/dist/prod/boundaries.js +388 -178
  45. package/dist/prod/core/action.js +65 -37
  46. package/dist/prod/core/async.js +378 -163
  47. package/dist/prod/core/attribution-hooks.js +3 -0
  48. package/dist/prod/core/constants.js +186 -1
  49. package/dist/prod/core/context.js +10 -16
  50. package/dist/prod/core/core.js +1546 -416
  51. package/dist/prod/core/dev.js +18 -1
  52. package/dist/prod/core/effect.js +119 -79
  53. package/dist/prod/core/error-hooks.js +71 -0
  54. package/dist/prod/core/error.js +13 -1
  55. package/dist/prod/core/external.js +4 -4
  56. package/dist/prod/core/graph.js +105 -54
  57. package/dist/prod/core/heap.js +72 -65
  58. package/dist/prod/core/invariants.js +3 -2
  59. package/dist/prod/core/lanes.js +117 -52
  60. package/dist/prod/core/optimistic.js +458 -104
  61. package/dist/prod/core/owner.js +131 -84
  62. package/dist/prod/core/scheduler.js +998 -241
  63. package/dist/prod/core/verdict.js +417 -112
  64. package/dist/prod/index.js +20 -8
  65. package/dist/prod/map.js +312 -151
  66. package/dist/prod/signals.js +363 -32
  67. package/dist/prod/store/index.js +2 -0
  68. package/dist/prod/store/next/optimistic.js +530 -197
  69. package/dist/prod/store/next/projection.js +142 -57
  70. package/dist/prod/store/next/reconcile.js +149 -103
  71. package/dist/prod/store/next/store.js +1470 -346
  72. package/dist/prod/store/next/target.js +40 -9
  73. package/dist/prod/store/store.js +23 -19
  74. package/dist/prod/store/utils.js +997 -130
  75. package/dist/types/attribution.d.ts +19 -0
  76. package/dist/types/attribution.prod.d.ts +22 -0
  77. package/dist/types/boundaries.d.ts +78 -5
  78. package/dist/types/core/action.d.ts +12 -5
  79. package/dist/types/core/async.d.ts +3 -1
  80. package/dist/types/core/attribution-costs.d.ts +35 -0
  81. package/dist/types/core/attribution-feedback.d.ts +133 -0
  82. package/dist/types/core/attribution-hooks.d.ts +304 -0
  83. package/dist/types/core/attribution-queries.d.ts +24 -0
  84. package/dist/types/core/attribution.d.ts +892 -0
  85. package/dist/types/core/constants.d.ts +179 -0
  86. package/dist/types/core/core.d.ts +299 -25
  87. package/dist/types/core/dev.d.ts +421 -8
  88. package/dist/types/core/effect.d.ts +8 -1
  89. package/dist/types/core/error-hooks.d.ts +71 -0
  90. package/dist/types/core/error.d.ts +9 -0
  91. package/dist/types/core/graph.d.ts +23 -0
  92. package/dist/types/core/heap.d.ts +5 -3
  93. package/dist/types/core/index.d.ts +6 -3
  94. package/dist/types/core/invariants.d.ts +5 -1
  95. package/dist/types/core/lanes.d.ts +51 -19
  96. package/dist/types/core/owner.d.ts +15 -3
  97. package/dist/types/core/scheduler.d.ts +248 -1
  98. package/dist/types/core/types.d.ts +124 -41
  99. package/dist/types/index.d.ts +18 -5
  100. package/dist/types/map.d.ts +2 -0
  101. package/dist/types/signals.d.ts +145 -13
  102. package/dist/types/store/index.d.ts +7 -6
  103. package/dist/types/store/next/optimistic.d.ts +18 -13
  104. package/dist/types/store/next/projection.d.ts +5 -2
  105. package/dist/types/store/next/reconcile.d.ts +7 -0
  106. package/dist/types/store/next/store.d.ts +83 -11
  107. package/dist/types/store/next/target.d.ts +108 -11
  108. package/dist/types/store/store.d.ts +20 -11
  109. package/dist/types/store/utils.d.ts +188 -0
  110. package/package.json +31 -26
  111. package/dist/node.cjs +0 -8137
  112. package/dist/types/store/optimistic.d.ts +0 -45
  113. package/dist/types/store/projection.d.ts +0 -70
  114. package/dist/types/store/reconcile.d.ts +0 -46
  115. package/dist/types-cjs/affects.d.cts +0 -47
  116. package/dist/types-cjs/boundaries.d.cts +0 -175
  117. package/dist/types-cjs/core/action.d.cts +0 -64
  118. package/dist/types-cjs/core/async.d.cts +0 -21
  119. package/dist/types-cjs/core/constants.d.cts +0 -71
  120. package/dist/types-cjs/core/core.d.cts +0 -156
  121. package/dist/types-cjs/core/dev.d.cts +0 -64
  122. package/dist/types-cjs/core/effect.d.cts +0 -30
  123. package/dist/types-cjs/core/error.d.cts +0 -47
  124. package/dist/types-cjs/core/external.d.cts +0 -15
  125. package/dist/types-cjs/core/graph.d.cts +0 -5
  126. package/dist/types-cjs/core/heap.d.cts +0 -22
  127. package/dist/types-cjs/core/index.d.cts +0 -13
  128. package/dist/types-cjs/core/invariants.d.cts +0 -59
  129. package/dist/types-cjs/core/lanes.d.cts +0 -56
  130. package/dist/types-cjs/core/optimistic.d.cts +0 -6
  131. package/dist/types-cjs/core/owner.d.cts +0 -124
  132. package/dist/types-cjs/core/scheduler.d.cts +0 -172
  133. package/dist/types-cjs/core/types.d.cts +0 -148
  134. package/dist/types-cjs/core/verdict.d.cts +0 -2
  135. package/dist/types-cjs/index.d.cts +0 -10
  136. package/dist/types-cjs/map.d.cts +0 -69
  137. package/dist/types-cjs/package.json +0 -3
  138. package/dist/types-cjs/signals.d.cts +0 -624
  139. package/dist/types-cjs/store/index.d.cts +0 -19
  140. package/dist/types-cjs/store/next/optimistic.d.cts +0 -20
  141. package/dist/types-cjs/store/next/projection.d.cts +0 -24
  142. package/dist/types-cjs/store/next/reconcile.d.cts +0 -3
  143. package/dist/types-cjs/store/next/store.d.cts +0 -71
  144. package/dist/types-cjs/store/next/target.d.cts +0 -99
  145. package/dist/types-cjs/store/optimistic.d.cts +0 -45
  146. package/dist/types-cjs/store/projection.d.cts +0 -70
  147. package/dist/types-cjs/store/reconcile.d.cts +0 -46
  148. package/dist/types-cjs/store/store.d.cts +0 -138
  149. package/dist/types-cjs/store/storePath.d.cts +0 -58
  150. package/dist/types-cjs/store/utils.d.cts +0 -74
@@ -0,0 +1,1999 @@
1
+ import { releaseFlightTeardown, handleAsync, clearStatus, parkLoadingWindow, notifyStatus, settlePendingSource, settleErroredDependents } from "./async.js";
2
+
3
+ import { NOT_PENDING, EFFECT_TRACKED, EFFECT_USER, REACTIVE_OPTIMISTIC_DIRTY, CONFIG_DERIVED_OVERRIDE, CONFIG_HELD_CHILDREN, CONFIG_LANE_FRAME, CONFIG_OPTIMISTIC, STATUS_UNINITIALIZED, STATUS_ERROR, STATUS_PENDING, REACTIVE_REASK, REACTIVE_RECOMPUTING_DEPS, REACTIVE_ZOMBIE, CONFIG_SYNC, CONFIG_HAS_LANE, REACTIVE_MISSED_WAKE, REACTIVE_DISPOSED, REACTIVE_SNAPSHOT_STALE, unwrapOverride, CONFIG_DIRECT_COMMIT, CONFIG_HAS_COMPANIONS, CONFIG_PROMOTED, CONFIG_ADOPTED_UNFLUSHED, CONFIG_CHILDREN_FORBIDDEN, CONFIG_FRESH_READ, CONFIG_INPUTS_PUBLISHED, CONFIG_AUTO_DISPOSE, REACTIVE_LAZY, REACTIVE_CHECK, REACTIVE_DIRTY, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, REACTIVE_MANUAL_WRITE, CONFIG_AUTHORITATIVE_READ, CONFIG_OVERRIDE_SUPERSEDED, CONFIG_AUTHORITATIVE_OBSERVED, defaultContext, REACTIVE_NONE, CONFIG_HELD_TRUTH, CONFIG_TRANSPARENT, CONFIG_OWNED_WRITE, CONFIG_NO_SNAPSHOT, CONFIG_PLUMBING, CONFIG_IN_SNAPSHOT_SCOPE, NO_SNAPSHOT, CONFIG_HAS_SNAPSHOT, CONFIG_FW_CHILDREN, CONFIG_SLOT_NODE, STORE_SNAPSHOT_PROPS } from "./constants.js";
4
+
5
+ import { NotReadyError } from "./error.js";
6
+
7
+ import { clearDeps, trimStaleDeps, link, dormantNodes } from "./graph.js";
8
+
9
+ import { deleteFromHeap, queueFor, insertIntoHeapHeight, enqueueSub, markNode, markHeap, insertIntoHeap } from "./heap.js";
10
+
11
+ import { findLane } from "./lanes.js";
12
+
13
+ import { GRAPH_SIZE_WARN_AT, noteFanIn } from "./dev.js";
14
+
15
+ import { attrHooks } from "./attribution-hooks.js";
16
+
17
+ import "./invariants.js";
18
+
19
+ import { disposeChildren, markDisposal, linkChild, inheritId } from "./owner.js";
20
+
21
+ import { GlobalQueue, bumpNotifyEpoch, activeTransition, globalQueue, clock, currentTransition, insertSubs, queuePendingNode, wakeParked, heldTrims, runInTransition, schedule, batchJoins, notifyEpoch, dirtyQueue, projectionWriteActive, reaskArmed, armReaskClear } from "./scheduler.js";
22
+
23
+ // The heap's per-node step. A tracked effect's heap visit is its compute
24
+ // phase — empty, like a user effect whose compute reads nothing — and hands
25
+ // the callback to the user queue. Routing the wake through the heap, rather
26
+ // than straight into the queue at notify time, is what orders the run after
27
+ // the commit regardless of which phase the write came from: a write in a
28
+ // render-effect callback stages its value for the next pass, but a wake pushed
29
+ // directly into the user queue ran in the SAME pass, read the old value, and
30
+ // nothing re-notified it when the value landed (#3291).
31
+ GlobalQueue.nt = e => {
32
+ if (e.He === EFFECT_TRACKED) {
33
+ deleteFromHeap(e, queueFor(e));
34
+ e.it = true;
35
+ e.T.enqueue(EFFECT_USER, e.lt);
36
+ } else recompute(e);
37
+ };
38
+
39
+ GlobalQueue.ut = disposeChildren;
40
+
41
+ let tracking = false;
42
+
43
+ /** @internal verdict-module glue */ function setPendingCheckActive(e) {
44
+ pendingCheckActive = e;
45
+ }
46
+
47
+ /** @internal verdict-module glue */ function setLatestReadActive(e) {
48
+ latestReadActive = e;
49
+ }
50
+
51
+ /** @internal verdict-module glue */ function setContextInternal(e) {
52
+ context = e;
53
+ }
54
+
55
+ let stale = false;
56
+
57
+ let pendingCheckActive = false;
58
+
59
+ let latestReadActive = false;
60
+
61
+ let context = null;
62
+
63
+ let currentOptimisticLane = null;
64
+
65
+ /** Notify `node`'s subscribers on `lane`'s channel: they recompute as the
66
+ * lane's passes — a memo publishes a derived override (lanes stage, #3479),
67
+ * an effect runs from the lane's queue, at the park, ahead of the
68
+ * transaction — the display-ahead view. The write itself is already staged
69
+ * (setSignal) and commits with the frame; this walk shows it now. Used by a
70
+ * boundary re-armed from a lane pass (boundaries.ts `_swap`, #3540). */ function notifyOnLane(e, t) {
71
+ const n = currentOptimisticLane;
72
+ currentOptimisticLane = t;
73
+ try {
74
+ insertSubs(e, true);
75
+ } finally {
76
+ currentOptimisticLane = n;
77
+ }
78
+ }
79
+
80
+ let snapshotCaptureActive = false;
81
+
82
+ let snapshotSources = null;
83
+
84
+ function ownerInSnapshotScope(e) {
85
+ while (e) {
86
+ if (e.ot) return true;
87
+ e = e._parent;
88
+ }
89
+ return false;
90
+ }
91
+
92
+ function setSnapshotCapture(e) {
93
+ snapshotCaptureActive = e;
94
+ if (e && !snapshotSources) snapshotSources = new Set;
95
+ }
96
+
97
+ function markSnapshotScope(e) {
98
+ e.ot = true;
99
+ }
100
+
101
+ function releaseSnapshotScope(e) {
102
+ e.ot = false;
103
+ releaseSubtree(e);
104
+ schedule();
105
+ }
106
+
107
+ function releaseSubtree(e) {
108
+ let t = e._n;
109
+ while (t) {
110
+ if (t.ot) {
111
+ t = t.Fn;
112
+ continue;
113
+ }
114
+ if (t.Se) {
115
+ const e = t;
116
+ e.C &= ~CONFIG_IN_SNAPSHOT_SCOPE;
117
+ if (e.oe & REACTIVE_SNAPSHOT_STALE) {
118
+ e.oe &= ~REACTIVE_SNAPSHOT_STALE;
119
+ e.oe |= REACTIVE_DIRTY;
120
+ if (dirtyQueue.st > e.ft) dirtyQueue.st = e.ft;
121
+ insertIntoHeap(e, dirtyQueue);
122
+ }
123
+ }
124
+ releaseSubtree(t);
125
+ t = t.Fn;
126
+ }
127
+ }
128
+
129
+ function clearSnapshots() {
130
+ if (snapshotSources) {
131
+ for (const e of snapshotSources) {
132
+ // The extension is a fixed-shape object with `_snapshotValue`
133
+ // pre-initialized to undefined (see ext()), and every reader tests
134
+ // `!== undefined` — assign, don't `delete`: deleting a field pushes the
135
+ // object to dictionary mode for every later read of every field.
136
+ const t = e.o;
137
+ if (t != null) t.ct = undefined;
138
+ // StoreNode targets share one pre-initialized hidden class (see
139
+ // createStoreProxy) — same rule, and only when present so signal-node
140
+ // sources don't grow the field.
141
+ if (e[STORE_SNAPSHOT_PROPS] !== undefined) e[STORE_SNAPSHOT_PROPS] = undefined;
142
+ }
143
+ snapshotSources = null;
144
+ }
145
+ snapshotCaptureActive = false;
146
+ }
147
+
148
+ function recompute(e, t = false) {
149
+ // §12d: any recompute can clean a marked subscriber — invalidate skips.
150
+ bumpNotifyEpoch();
151
+ const n = e.He;
152
+ // Attribution hook: fired before this run touches the dep list — `_deps`
153
+ // still holds the previous run's links (the subscriptions that could have
154
+ // triggered this run, and the baseline for the engine's subscription diff).
155
+ let i = false;
156
+ if (attrHooks !== null) attrHooks.recomputeStart(e, t);
157
+ // Lane posture is resolved BEFORE the previous frame is parked below: a
158
+ // lane pass on an effect direct-commits (#3662, see the parking site), so
159
+ // the decision must be known there. `lane` is applied to
160
+ // `currentOptimisticLane` further down, once the previous posture is saved.
161
+ let l = !!(e.oe & REACTIVE_OPTIMISTIC_DIRTY);
162
+ let u = null;
163
+ if (l) {
164
+ u = GlobalQueue._t(e, true);
165
+ // `false` = wake-only lane demotion: recompute plain so a mid-tick
166
+ // latest()/isPending() pull stages instead of direct-committing (#3009).
167
+ // The predicate lives with the engine (recomputeLane).
168
+ if (u === false) l = false;
169
+ } else if (e.C & CONFIG_DERIVED_OVERRIDE) {
170
+ // Lanes stage (#3479): a pass over a live lane member carrying a derived
171
+ // override is the lane's pass whatever channel dirtied it (a boundary
172
+ // reset, an unrelated sync write) — its inputs serve the lane's view, so
173
+ // its result is the lane's and belongs in the override slot. Run plain,
174
+ // A18's sync twin below read that re-derived lane view as a differing
175
+ // truth (a fresh array), superseded the override and demoted the lane;
176
+ // the lane's next pass then dropped the staged "truth" and left the node
177
+ // flagged superseded with nothing to serve (fuzzer latest-1 #2481). A
178
+ // demoted node resolves no lane and stays plain: its pass IS the truth.
179
+ u = GlobalQueue._t(e, true);
180
+ if (u) l = true;
181
+ } else if (activeTransition && !t && activeTransition.bn.length) {
182
+ // Lane adoption: parent-deeper-than-owned-child can run before its OPT-dirty
183
+ // child propagates. Walk deps once and inherit the OPT lane so this node
184
+ // recomputes under the right posture and propagates correctly.
185
+ u = GlobalQueue._t(e, false);
186
+ if (u) l = true;
187
+ }
188
+ if (!t) {
189
+ // A stamped memo re-enters its hold: its value is that transaction's work.
190
+ // An effect's pass belongs to whatever dirtied it (A15 corollary: effects
191
+ // don't entangle parallel transactions); it joins its stamp only when the
192
+ // pass observes the held flight — queue notification (#3407).
193
+ if (e.me && !n && activeTransition !== e.me) globalQueue.initTransition(e.me);
194
+ deleteFromHeap(e, queueFor(e));
195
+ if (e.o !== null) {
196
+ e.o.pe = null;
197
+ // Supersede is where an iterator flight dies (#3122): close it now.
198
+ // Its cleanup(close) registration may sit in a zombie-deferred
199
+ // disposal list that a held transition only drains when the
200
+ // SUPERSEDING flight settles — cancellation must not wait for the
201
+ // work that replaced it. Idempotent with the cleanup-channel close.
202
+ releaseFlightTeardown(e);
203
+ }
204
+ // Tracked effects run after finalizePureQueue, so dispose immediately
205
+ // instead of deferring. Children built by an uncommitted recompute
206
+ // (CONFIG_HELD_CHILDREN) die immediately too: no frame ever showed them.
207
+ // Everything else is the committed frame's and is deferred as zombies
208
+ // until this node's commit — a transaction-owned node included (#3404):
209
+ // a parked node's children predate the hold, and tearing them down when
210
+ // the source lands ran cleanups before the transaction's atomic reveal.
211
+ // A lane pass on an effect parks a LANE frame instead (CONFIG_LANE_FRAME,
212
+ // #3662; A15 lanes corollary): the frame it replaces leaves the screen
213
+ // when the lane's queue applies this run (A30) — not at the action's
214
+ // commit — and a held lane defers that with the frame still displayed.
215
+ // The drain is the lane's first render entry for this pass, pushed before
216
+ // the pass builds the new frame: cleanups before side effects — the
217
+ // retired frame's `onCleanup`s run ahead of every effect callback of the
218
+ // new frame. While the frame waits, the live children were never shown:
219
+ // a superseding pass disposes them here like held children, and the
220
+ // parked frame stays.
221
+ if (n === EFFECT_TRACKED || e.C & (CONFIG_HELD_CHILDREN | CONFIG_LANE_FRAME)) disposeChildren(e); else if (e._n !== null || e.Ve !== null) {
222
+ markDisposal(e);
223
+ const t = ext(e);
224
+ t.Et = e.Ve;
225
+ t.dt = e._n;
226
+ e.Ve = null;
227
+ e._n = null;
228
+ e.Nt = 0;
229
+ if (n && u) {
230
+ e.C |= CONFIG_LANE_FRAME;
231
+ findLane(u).It[0].push(() => {
232
+ e.C &= ~CONFIG_LANE_FRAME;
233
+ disposeChildren(e, false, true);
234
+ });
235
+ }
236
+ } else ;
237
+ }
238
+ // A derived override (lanes stage, #3479) is override-covered like a written
239
+ // one: a plain pass over it — its source superseded (A18) — stages the truth
240
+ // and supersedes the override through the sync twin below.
241
+ const o = (e.C & (CONFIG_OPTIMISTIC | CONFIG_DERIVED_OVERRIDE)) !== 0 && e.o?.we !== NOT_PENDING && e.o?.we !== undefined;
242
+ const r = !!(e.S & STATUS_UNINITIALIZED);
243
+ // Capture both error and pending status before the compute clears them.
244
+ // A conditional can drop its pending source and recover to an unchanged
245
+ // value, leaving blocked dependents outside that source’s settle walk.
246
+ const s = e.S & STATUS_ERROR ? e.o?._ : undefined;
247
+ const a = (e.S & STATUS_PENDING) !== 0;
248
+ const f = a ? e.o?.ue : undefined;
249
+ // Pending SOURCE-hood, captured before the compute clears status: a node
250
+ // whose own flight parked dependents self-registers in _pendingSources
251
+ // (notifyStatus, isSource). If this recompute supersedes that flight and
252
+ // settles synchronously, those dependents settle HERE — asyncWrite's
253
+ // settlePendingSource walk never runs for a landing that was preempted
254
+ // (#3181).
255
+ const c = e.o?.ue?.has(e);
256
+ // Re-ask classification lives in the verdict module; capture the flag before
257
+ // the recompute wipes _flags below.
258
+ const _ = (e.oe & REACTIVE_REASK) !== 0;
259
+ // Captured before the compute clears it on a sync landing: if that landing
260
+ // is transition-held below, the window must stay open until the hold
261
+ // commits (commitPendingNode) — a closed window plus a held value reads as
262
+ // a pending frame to live observers of the verdict (#2990).
263
+ const E = e.ye;
264
+ // Creation-time A29 (see enterStagedRead): a pass outside a flush that is
265
+ // served a live transaction's staged value records the transaction here
266
+ // and is staged INTO it below — "born held" — instead of committing.
267
+ const d = stagedEntry;
268
+ stagedEntry = null;
269
+ const N = context;
270
+ context = e;
271
+ e.Je = null;
272
+ e.We++;
273
+ // REACTIVE_ZOMBIE is position, not scheduling state: it says the node sits
274
+ // on its owner's `_pendingFirstChild` chain, and `disposeChildren` keys its
275
+ // parent-chain splice off it. A zombie reruns for mainline writes until the
276
+ // commit that disposes it (#3463), so the per-pass wipe here — and in the
277
+ // finally below and in updateIfNecessary — must carry it (#3543): a
278
+ // de-flagged zombie spliced itself out of the LIVE chain at disposal,
279
+ // orphaning the owner's current child, which stayed subscribed forever.
280
+ e.oe = REACTIVE_RECOMPUTING_DEPS | e.oe & REACTIVE_ZOMBIE;
281
+ e.Ge = clock;
282
+ let I = e._e === NOT_PENDING ? e.ce : e._e;
283
+ let T = e.ft;
284
+ let S = false;
285
+ let O = tracking;
286
+ let A = currentOptimisticLane;
287
+ tracking = true;
288
+ // A computed's fn establishes its OWN dependencies, so it must never run
289
+ // inside a latest() read window: read() short-circuits through the
290
+ // companion path before dependency linking, so a memo created (eagerly
291
+ // computed) inside latest(fn) came out permanently dependency-less (#2926).
292
+ // latestRead() already suspends the flag for its pull-recomputes; this
293
+ // covers creation-time computes and flushes that run inside the window.
294
+ const C = latestReadActive;
295
+ latestReadActive = false;
296
+ // A memo computes under its OWN lane posture, never the puller's (A31,
297
+ // #3442): its value is one shared slot every reader sees, so a pull from a
298
+ // lane-carrying reader (a probe effect on its companion lane pulling a sync
299
+ // memo) must not run it with that lane's read carve-outs — under a lane, a
300
+ // pending node on no lane serves its committed value instead of throwing,
301
+ // and the memo then published a stale "settled" value, dropped its
302
+ // pending status, and stopped holding its transaction. The branches below
303
+ // re-establish the posture the memo itself owns (OPT-dirty, or adopted
304
+ // through its deps). Effects keep the ambient lane: their runs are the
305
+ // lane's own view.
306
+ if (!n) currentOptimisticLane = null;
307
+ // Lane posture lives with the engine: OPTIMISTIC_DIRTY is only ever set by
308
+ // engine-driven paths, and _optimisticNodes is only pushed by
309
+ // _optimisticWrite, so the hook is installed whenever either gate holds.
310
+ // (Resolved at the top of this pass, ahead of the parking site.)
311
+ if (u) currentOptimisticLane = u;
312
+ const p = n && n !== EFFECT_USER;
313
+ const R = stale;
314
+ if (p) stale = true;
315
+ // An effect recorded for this transaction's commit replay (it once read a
316
+ // node the transaction held and showed the committed value) and now
317
+ // recomputing UNDER the transaction sees its staged view: the value this
318
+ // run produces is applied by the commit itself, and the stale recording
319
+ // would publish the frame a second time. Drop it; the reads below re-record
320
+ // if they are served the committed view again (a lane's committed read).
321
+ if (n && activeTransition !== null && activeTransition.Tt.size) activeTransition.Tt.delete(e);
322
+ try {
323
+ if (!false && e.C & CONFIG_SYNC) {
324
+ I = e.Se(I);
325
+ if (e.o !== null) e.o.pe = null;
326
+ e.ye = false;
327
+ } else {
328
+ // Snapshot `_inFlight` so we can detect whether `_fn` self-registered an async
329
+ // subscription (e.g. `createProjection` calls `handleAsync` from inside its body
330
+ // with a setter callback). In that case, the outer `handleAsync` call below would
331
+ // clobber the fresh subscription, so we skip it and let the internally-registered
332
+ // iteration drive updates.
333
+ const t = e.o?.pe;
334
+ const n = e.Se(I);
335
+ const i = typeof n === "object" && n !== null;
336
+ const l = e.o?.pe !== t;
337
+ I = l || !i ? n : handleAsync(e, n);
338
+ if (!l && !i) {
339
+ if (e.o !== null) e.o.pe = null;
340
+ // A sync (non-object) return is the first real answer; async-shaped
341
+ // results clear inside handleAsync at their own landing points, and a
342
+ // self-registered flight (inFlightChanged — projections) clears when
343
+ // its internal handleAsync lands.
344
+ e.ye = false;
345
+ }
346
+ }
347
+ // On a status-free node clearStatus is a guaranteed no-op: every field
348
+ // its body gates on is either _statusFlags or lives in the cold
349
+ // extension — no extension, no status to clear. (_x from an unrelated
350
+ // installer just makes clearStatus a cheap re-verified no-op.)
351
+ // A node born held keeps STATUS_UNINITIALIZED until its transaction
352
+ // commits: it has no committed value, and read() holds its readers.
353
+ if (e.S !== 0 || e.o !== null) clearStatus(e, t && stagedEntry === null);
354
+ // _optimisticLane is only ever assigned by engine paths (CONFIG_HAS_LANE
355
+ // is their sticky presence mark).
356
+ if (e.C & CONFIG_HAS_LANE && e.o?.Ce) GlobalQueue.St(e);
357
+ } catch (t) {
358
+ const n = t instanceof NotReadyError;
359
+ if (n && e.ye) {
360
+ // Loading window with an unready sync dependency: register for the
361
+ // source's settle (the settlePendingSource walk runs off
362
+ // _pendingSources + _blocked alone) but take NO read-visible pending
363
+ // status, no downstream propagation, no transition, no lane
364
+ // registration — the committed loading value keeps serving. If the
365
+ // node is currently errored the error stays the answer until this
366
+ // retry can actually run.
367
+ parkLoadingWindow(e, t);
368
+ } else {
369
+ // Track pending async in the lane (not the lane's source — it creates the lane
370
+ // but doesn't belong to it). Set lane BEFORE notifyStatus for downstream propagation.
371
+ if (n && currentOptimisticLane) GlobalQueue.Ot(e);
372
+ let i = false;
373
+ if (n) {
374
+ ext(e).Oe = true;
375
+ if (GlobalQueue.At !== null) i = GlobalQueue.At(e, _);
376
+ }
377
+ notifyStatus(e, n ? STATUS_PENDING : STATUS_ERROR, t, undefined, n ? e.o?.Ce : undefined);
378
+ // The replacement source is fully propagated now. If no new flight
379
+ // re-owned self, retire the superseded flight and its dependent copies.
380
+ if (n && c && !e.o?.pe) settlePendingSource(e);
381
+ // A re-park drops what the earlier pass carried (#3456): a source this
382
+ // pass no longer reaches — its branch switched, or a fresh flight
383
+ // replaced the inputs' pending with its own — stays copied onto
384
+ // dependents that reached it only through here, and its landing walk
385
+ // stops at this node (nothing left to retire) before it finds them. A
386
+ // dependent then waits forever on a flight it has no path to. The
387
+ // re-park twin of the unchanged-value recovery sweep below; dependents
388
+ // with another path keep the source (retryReaches).
389
+ if (n && f) for (const t of f) if (t !== e && !e.o?.ue?.has(t)) settlePendingSource(e, t);
390
+ if (i) GlobalQueue.k(e);
391
+ }
392
+ } finally {
393
+ tracking = O;
394
+ latestReadActive = C;
395
+ if (p) stale = R;
396
+ // Consume the missed-wake latch (#3037, set by insertSubs): a dep write
397
+ // landed beneath this pass on a link it had already validated. The wipe
398
+ // below must not key off DIRTY/CHECK — the read-time pull protocol
399
+ // (markNode(c) in read()) marks the running node as part of ordinary
400
+ // bookkeeping, and those marks are correctly discarded here.
401
+ S = (e.oe & REACTIVE_MISSED_WAKE) !== 0;
402
+ // REACTIVE_DISPOSED survives too (#3621): the pass may have disposed its
403
+ // own owner (a memo calling its root's `dispose()`, a cleanup doing so
404
+ // #3601/#3606), and `disposeChildren` set the flag on this node
405
+ // reentrantly. Dropped, the node read as live — `refresh()` re-ran it
406
+ // and `isDisposed()` lied.
407
+ e.oe = e.oe & (REACTIVE_ZOMBIE | REACTIVE_DISPOSED) | (t ? e.oe & REACTIVE_SNAPSHOT_STALE : 0);
408
+ context = N;
409
+ }
410
+ // The cast re-widens: TS narrowed `stagedEntry` to `null` at the reset
411
+ // above and does not invalidate that across the compute call that
412
+ // `enterStagedRead` runs under. No emitted code.
413
+ const G = stagedEntry;
414
+ stagedEntry = d;
415
+ // A node that died during its own pass (#3621) is dead at the end of it,
416
+ // and the pass is void. Its owner's teardown already unlinked its deps,
417
+ // removed it from the heap and ran its cleanups; what remains is what the
418
+ // body did AFTER the `dispose()` call: reads that re-linked the dead node
419
+ // to its sources (unlinked here — the leak that kept it re-running in a
420
+ // torn-down tree), a flight it may have started (retired: the landing
421
+ // checks `_inFlight` identity), and the value it returned. That value is
422
+ // NOT published: a dead node freezes at its last committed value (#3024),
423
+ // so nothing is staged, committed, or propagated to subscribers, and an
424
+ // effect's run is not enqueued (runEffect would refuse it anyway). The
425
+ // attribution frame opened at the top is still closed.
426
+ if (e.oe & REACTIVE_DISPOSED) {
427
+ clearDeps(e);
428
+ if (e.o !== null) e.o.pe = null;
429
+ if (attrHooks !== null) attrHooks.recomputeEnd(e, t, false, false, false, false);
430
+ currentOptimisticLane = A;
431
+ return;
432
+ }
433
+ if (!e.o?._) {
434
+ // Observe-tier fan-in (HUGE_FAN_IN): the validated prefix [_deps.._depsTail]
435
+ // IS this pass's distinct sources — count it here rather than per link. A
436
+ // begin/end bracket around the pass plus a per-link increment measured
437
+ // -5.8% on createRenderEffects:create1to1 (CodSpeed, dev tier) and cost
438
+ // several points of the shape wins elsewhere; this walk is a fraction of
439
+ // the reads that built the list and keeps no module state, so nested
440
+ // pulls need no save/restore. (The stale tail is trimmed at the end of
441
+ // this pass, or at its commit — see recompute's tail.)
442
+ {
443
+ let t = 0;
444
+ for (let n = e.Ee; n !== null; n = n.Ie) {
445
+ t++;
446
+ if (n === e.Je) break;
447
+ }
448
+ if (t >= GRAPH_SIZE_WARN_AT) noteFanIn(e, t);
449
+ }
450
+ // INV-11 (#3330): the equality gate compares against the slot this run
451
+ // publishes to. An override-covered node publishes the override; a lane
452
+ // recompute (OPT-dirty) direct-commits `_value` — the lane's own reveal
453
+ // schedule — so a transaction-held `_pendingValue` that already equals
454
+ // the new result is not "unchanged": the screen still shows `_value`.
455
+ // Only a transaction-staged run compares against `_pendingValue`.
456
+ const u = o ? unwrapOverride(e.o?.we) : l || e._e === NOT_PENDING ? e.ce : e._e;
457
+ let a = false;
458
+ try {
459
+ a = !n && r || !e.Fe || !e.Fe(u, I);
460
+ } catch (t) {
461
+ // A throwing user comparator is an error of this node's computation.
462
+ // Route it through the same status path as a compute-phase throw so
463
+ // error boundaries contain it; otherwise it unwinds the scheduler
464
+ // flush, bypassing every boundary and wedging the queue (#2837).
465
+ notifyStatus(e, STATUS_ERROR, t);
466
+ }
467
+ // A committed derived change becomes a cause for this node's subscribers,
468
+ // chaining their attribution through this node to the root write.
469
+ if (attrHooks !== null) {
470
+ i = a && !e.o?._;
471
+ if (i && !n && !t) attrHooks.derivedChanged(e);
472
+ }
473
+ // Effects use `_equals: false` (no per-effect closure). The side effects that
474
+ // the equals closure used to perform — flagging the effect dirty and enqueueing
475
+ // its runner — happen here instead. `!create` matches the previous `initialized`
476
+ // gate: the explicit recompute(node, true) inside effect() does not enqueue, so
477
+ // effect() can call its runner synchronously for the first run.
478
+ if (n && a) {
479
+ e.it = !e.o?._;
480
+ // Reuse one bound runner per effect — runEffect no-ops on a stale
481
+ // `_modified`, so re-enqueueing the same function is harmless.
482
+ if (!t) {
483
+ e.T.enqueue(n, e.Ct ??= GlobalQueue.Rt.bind(null, e));
484
+ // Contested effect (#3322). Effects don't entangle transactions (a
485
+ // shared effect is not shared state), yet they have one value slot:
486
+ // when this write commits under a different transaction
487
+ // (`activeTransition`, null = mainline) than the one that produced the
488
+ // previous value (`_valueTransition`), that view is gone. Rather than
489
+ // merge the two, each commit re-derives the effect against its own
490
+ // committed world (Transition._contested, re-dirtied by
491
+ // finalizePureQueue ahead of the heap run). The previous owner always
492
+ // needs it if still live: its commit is silent (staging already
493
+ // notified) and the value it computed is gone. The new owner needs it
494
+ // too, for the same reason, unless it is mainline — mainline publishes
495
+ // what it computes. A previous owner that was mainline, or already
496
+ // committed, left nothing to protect.
497
+ let t = e.Gt;
498
+ if (t !== activeTransition) {
499
+ e.Gt = activeTransition;
500
+ if (t !== null && (t = currentTransition(t)) !== activeTransition && !t.Dt) {
501
+ (t.ht ??= []).push(e);
502
+ if (activeTransition !== null) (activeTransition.ht ??= []).push(e);
503
+ }
504
+ }
505
+ }
506
+ }
507
+ if (e.o?._) ; else if (a) {
508
+ const i = o ? e.o?.we : undefined;
509
+ if (t && G === null ||
510
+ // Plain sync flush (no transition on either side) commits effect
511
+ // values directly — the pending round-trip (queuePendingNode +
512
+ // commitPendingNodes) exists to sequence transition reveals, and
513
+ // paying it per effect on the plain path is pure overhead.
514
+ // DIRECT_COMMIT effects (resolve/until) commit directly even under
515
+ // their own held transition: their applies deliver on a microtask,
516
+ // not the stashed queues, so a staged value would hand the immediate
517
+ // apply stale state — see CONFIG_DIRECT_COMMIT.
518
+ n && G === null && (activeTransition !== e.me || activeTransition === null || e.C & CONFIG_DIRECT_COMMIT) || l) {
519
+ // Lanes stage (#3479): a lane pass on a memo publishes its speculative
520
+ // result as an OVERRIDE — `_value` stays the committed truth, so a
521
+ // reader off the lane (A17's committed view, #3460) sees a whole
522
+ // committed frame: the source's shadow and its derivations together,
523
+ // never a committed shadow beside a speculative memo. The lane's own
524
+ // readers and untracked reads see the override (A17); the revert
525
+ // drops it and re-derives (resolveOptimisticNodes). Effects keep the
526
+ // direct commit — their `_value` is a run result the lane's queues
527
+ // already sequence. Either way the lane pass drops any superseded
528
+ // older hold so its queued commit can't clobber the fresh frame: a
529
+ // hold staged on an earlier, lane-free pass of the SAME transaction
530
+ // is superseded — left in place, the commit published the older
531
+ // frame over the fresh one (#3377). A reversion pass (OPT-dirty with
532
+ // no lane — the override dropped) commits directly: it IS the truth.
533
+ if (l && !n && currentOptimisticLane !== null) GlobalQueue.je(e, I, currentOptimisticLane); else e.ce = I;
534
+ if (l) e._e = NOT_PENDING;
535
+ } else {
536
+ e._e = I;
537
+ if (G !== null) {
538
+ // Born held: the pass ran from mainline and derived from this
539
+ // transaction's staged world (enterStagedRead). Its value is the
540
+ // transaction's — staged into it directly, stamped, and committed
541
+ // with it; mainline's batch never sees it. A born-held effect
542
+ // skips its synchronous first run (effect()) and is replayed by
543
+ // the commit like a stale reader that showed the committed frame.
544
+ // (A re-pass under a fresh boundary — the in-flush form — is
545
+ // already the transaction's: restaged, not re-queued.)
546
+ if (e.me !== G) {
547
+ e.me = G;
548
+ G.In.push(e);
549
+ }
550
+ if (n) G.Tt.add(e);
551
+ // Under a boundary that has not revealed, a held first value is
552
+ // something not ready under it (#3540): the boundary collects this
553
+ // node as a source and shows its fallback; the source stays
554
+ // collected while born held (CollectionQueue._checkSources) and
555
+ // is released by the commit that initializes it. A boundary that
556
+ // already shows content is not told — it holds like any reader.
557
+ if (underFreshLoadingBoundary(e)) e.T.notify(e, STATUS_PENDING, STATUS_PENDING, new NotReadyError(e));
558
+ }
559
+ // A window landing that gets held re-opens the window until the hold
560
+ // commits — the verdict's held-value branch is window-gated (#2990).
561
+ if (E) e.ye = true;
562
+ // A staged sync recompute is a write path like setSignal/asyncWrite,
563
+ // so sync derivations of held sources stay visible to isPending()/latest()
564
+ // (#2831). Both companion writes are transition-scoped (optimistic) and
565
+ // auto-revert/re-derive at commit. Not gated on an active transition:
566
+ // a plain flush can still become a hold after this recompute — an
567
+ // async memo downstream pends and the batch is adopted into a
568
+ // transaction (scheduler.enterTransition) — and nothing re-derives
569
+ // the companion at adoption, so a memo held that way read
570
+ // isPending() false while its held source read true (#3413).
571
+ if (e.C & CONFIG_HAS_COMPANIONS && GlobalQueue.Le !== null) GlobalQueue.Le(e, I);
572
+ }
573
+ // insertSubs only walks _subs (no scheduling of its own), so a
574
+ // subscriber-less node has nothing to notify.
575
+ if (e.u !== null && (!o || l || e.o?.we !== i)) insertSubs(e, l || o);
576
+ // A18 supersession, sync twin of asyncWrite's override branch (#3331):
577
+ // this pass published truth that differs from the override (the gate
578
+ // compared against it) into the transaction-held slot. Whether the
579
+ // source is this node's own async or an upstream node it derives from
580
+ // synchronously makes no difference — "the source recomputed". The
581
+ // override stays displayed until the commit; the graph moves to the
582
+ // staged truth now (plain channel, lane demoted). Ordering: "a new
583
+ // value from the source" postdates the override — an override written
584
+ // in this same tick (optimisticWrite stamps `_overrideTime`) is the
585
+ // newer intent over whatever this pass derives from the batch's staged
586
+ // inputs, and is not superseded by it.
587
+ else if (o && !l && e.o.Pt !== clock) GlobalQueue.Qe(e, I);
588
+ } else if (o) {
589
+ // Unchanged value (equals the override) recomputed while the override
590
+ // is active: _value may still be stale, so hold the authoritative value
591
+ // for commit on its own transition's schedule — invisibly (A17/A18).
592
+ if (e._e === NOT_PENDING) queuePendingNode(e);
593
+ e._e = I;
594
+ if (E) e.ye = true;
595
+ // see the held branch above (#2990)
596
+ // A confirmation after a supersession restores the override as the
597
+ // graph's value and notifies; a plain confirmation wakes only an
598
+ // authoritative-view reader (until()'s predicate, refresh()'s waiter)
599
+ // that observed this node past its override — "authoritative arrival
600
+ // equal to the override" is exactly the acknowledgment it waits for;
601
+ // A17 silence holds for every ordinary subscriber. Both live in the
602
+ // engine's supersedeOverride.
603
+ GlobalQueue.Qe(e, I);
604
+ } else if (e.ft != T) {
605
+ for (let t = e.u; t !== null; t = t.Pe) {
606
+ insertIntoHeapHeight(t.Ae, queueFor(t.Ae));
607
+ }
608
+ }
609
+ // Silent recovery: errored → unchanged value fires no notification, but
610
+ // dependents still holding the propagated error consumed their dirty flag
611
+ // in an errored run and may sit on stale commits (#2949). Changed-value
612
+ // recoveries ride insertSubs above; a comparator throw re-errored the node
613
+ // (el._x?._error re-set), so this only runs on a genuinely clean recovery.
614
+ if (!a && !e.o?._) {
615
+ if (s !== undefined) settleErroredDependents(e, s);
616
+ // Self-registration (this node's own superseded flight) is the #3181
617
+ // sweep's business below — retiring it here too would walk twice.
618
+ if (f) for (const t of f) if (t !== e) settlePendingSource(e, t);
619
+ }
620
+ // #3181: a synchronous settle supersedes the old landing callback, so
621
+ // recompute owns its pending-source sweep. An uninitialized node without
622
+ // a replacement source still has no truth to reveal and must stay parked.
623
+ if (c && !(e.S & (STATUS_PENDING | STATUS_UNINITIALIZED))) {
624
+ settlePendingSource(e);
625
+ // The superseded flight will not enter its waiting transactions at
626
+ // landing. Recheck them now so an unchanged value cannot leave their
627
+ // writes parked after the last pending source has settled.
628
+ wakeParked();
629
+ }
630
+ }
631
+ // A REPORTER whose pass stopped reading a source it reported on (a gate
632
+ // closed) stops counting for the transaction waiting on it (A15 / #3426:
633
+ // the hold lasts while a live reporter observes the flight). Nothing else
634
+ // re-judges a parked transaction (see wokenTransitions; the disposal
635
+ // (#3372) and boundary (#3375) twins of this site), so the writes it held
636
+ // stayed staged for as long as the flight stayed up — forever, for one
637
+ // that never lands (fuzzer #3446 P1, spec O3). The event is "this pass
638
+ // dropped a dep" — deps past `_depsTail` (trimmed below, or kept by A30
639
+ // for a staged pass), or a pass that read nothing — not "recovered from
640
+ // pending": a reporter registered by the stale-reader carve-out
641
+ // (heldFromStale, an INITIALIZED source refetching) displays the committed
642
+ // value and is never pending (fuzzer case 79). Every parked transaction,
643
+ // not the reporter's stamp: the transaction waiting on it registered it
644
+ // without stamping it. One idle pass per parked transaction; done ones
645
+ // return at re-entry. Effects only — reporters register from render-effect
646
+ // notification (INV-3).
647
+ // (`_depsTail` was reset at the top of the pass; TS keeps that narrowing.)
648
+ const D = e.Je;
649
+ if (n && (a && !(e.S & STATUS_PENDING) || (D === null ? e.Ee !== null : D.Ie !== null))) wakeParked();
650
+ // Dependencies are the committed frame's until it is replaced (A30, #3410; the
651
+ // deps twin of the held children above): a pass that staged its value
652
+ // leaves the previous pass's tail linked for `commitPendingNode` to trim,
653
+ // so a write to a dependency the committed value still derives from
654
+ // reaches this node — and joins its hold if the stage is transaction-held
655
+ // by then (a plain flush decides nothing here: the transaction that holds
656
+ // the pass may open later in the same flush). A pass that published
657
+ // directly (a first pass, a lane's own reveal) trims now. An errored pass
658
+ // (a throw, NotReady included, or a comparator throw above) keeps its full
659
+ // list as before — `_depsTail` marks where it stopped — and the commit
660
+ // skips it by the same `_error`. An effect's frame is the run its value is
661
+ // applied by, not the value slot (#3438): a direct-committed pass that
662
+ // still owes a run (`_modified`) has not replaced what the last run
663
+ // published — the same flush may stash that run into a transaction it
664
+ // opens later — so its tail waits for `runEffect` to trim once the run
665
+ // applies. A pass that changed nothing replaced nothing either (#3469): the
666
+ // same flush may park with its inputs held, and the committed frame still
667
+ // derives from the tail — its trim waits on the flush's verdict (heldTrims).
668
+ // A tracked effect's pass IS its run (it bypasses the heap and runs after
669
+ // the commit): the frame is replaced, trim now.
670
+ if (!e.o?._ && e._e === NOT_PENDING && !(n && e.it)) {
671
+ if (t || l || n === EFFECT_TRACKED) trimStaleDeps(e); else if (e.Je?.Ie ?? e.Ee) heldTrims.push(e);
672
+ }
673
+ // Attribution hook: fired before the lane restore so `currentOptimisticLane`
674
+ // still reflects THIS run's posture. The facts distinguish an overlay
675
+ // recompute (optimistic lane, transition replay, transition-held commit)
676
+ // from a plain committed one — the engine must not blame overlay runs as
677
+ // waste or double-count them against plain aggregates.
678
+ if (attrHooks !== null) attrHooks.recomputeEnd(e, t, i, l || currentOptimisticLane !== null, activeTransition !== null || e.me !== null, e._e !== NOT_PENDING);
679
+ currentOptimisticLane = A;
680
+ // A parked LANE frame is not a hold (#3662): its drain is the effect's own
681
+ // run, not a commit — the node is neither queued nor stamped for it, and
682
+ // the release below (for transaction zombies) leaves it parked.
683
+ const h = (e.C & CONFIG_LANE_FRAME) !== 0;
684
+ const P = e._e !== NOT_PENDING || !h && e.o !== null && (e.o.dt !== null || e.o.Et !== null) || (e.S & (STATUS_PENDING | STATUS_UNINITIALIZED)) !== 0;
685
+ // Override-covered holds (hasOverride) always queue: their commit belongs
686
+ // to their own transition's schedule (A18 re-rule) and is unobservable
687
+ // under the override (A17). Revert no longer commits anything, so an
688
+ // unqueued covered hold would leak (INV-7) once the revert clears
689
+ // _transition.
690
+
691
+ // While a pass's result waits on a commit, its children (and `_disposal`)
692
+ // wait with it, and a re-run may tear them down immediately
693
+ // (CONFIG_HELD_CHILDREN, #3404). One exception: a transaction-owned effect
694
+ // recomputed mainline (contested, #3322) published its value directly — a
695
+ // `_pendingValue` left from an earlier held pass is that transaction's,
696
+ // not this one's — so this pass's children are the frame's, and any
697
+ // zombies deferred at the top are superseded on that same frame. Its
698
+ // commit rides the transaction, not this flush: release them here rather
699
+ // than let two generations render at once. A LANE pass is the same case
700
+ // (#3662): it direct-commits ahead of its transaction, so its children are
701
+ // the frame's too. Left flagged for an OLDER frame's zombies, the stamped
702
+ // transaction's same-flush re-run below disposed the lane's freshly built
703
+ // children (an insert's inner effect, its run still queued in the lane)
704
+ // and held their replacements for a commit that never came.
705
+ let F = P && (!t || G !== null || (e.S & STATUS_PENDING) !== 0);
706
+ if (F && (!e.me || o)) queuePendingNode(e); else if (F && (activeTransition === null || l) && !(e.S & (STATUS_PENDING | STATUS_UNINITIALIZED))) {
707
+ F = false;
708
+ if (!h) disposeChildren(e, false, true);
709
+ }
710
+ if (F) e.C |= CONFIG_HELD_CHILDREN; else e.C &= ~CONFIG_HELD_CHILDREN;
711
+ // (A born-held pass IS the transaction's staged view — nothing to refresh.)
712
+ if (e.me && n && activeTransition !== e.me && G === null) {
713
+ // The re-run refreshes the transaction's STAGED view (_pendingValue); the
714
+ // value this pass published in _value belongs to the run that just
715
+ // finished. Keep that ownership, or the effect phase parks a
716
+ // mainline-computed value with the transaction (#3412).
717
+ const t = e.Gt;
718
+ runInTransition(e.me, () => recompute(e));
719
+ e.Gt = t;
720
+ }
721
+ // Missed-wake reschedule (see the finally above): values this pass read
722
+ // before the nested commit are stale, so run again now that the heap will
723
+ // accept the node. Equality gates stop same-value landings from cascading,
724
+ // and a re-run only latches again if another nested commit changes a dep
725
+ // beneath it — convergent unless deps genuinely keep changing.
726
+ if (S) {
727
+ enqueueSub(e);
728
+ schedule();
729
+ }
730
+ }
731
+
732
+ function updateIfNecessary(e) {
733
+ // Never re-enter a node that is currently computing: its dep bookkeeping
734
+ // (_depsTail/_depGen) is live, and a nested recompute would corrupt it.
735
+ // A mid-pass mark stays latched for recompute's own tail to reschedule
736
+ // (#3037); readers meanwhile serve the values the pass has so far.
737
+ // Never recompute a DISPOSED node either: recompute rewrites _flags and
738
+ // would resurrect it (#2983) — readers serve its last value.
739
+ if (e.oe & (REACTIVE_RECOMPUTING_DEPS | REACTIVE_DISPOSED)) return;
740
+ if (e.oe & REACTIVE_CHECK) {
741
+ for (let t = e.Ee; t; t = t.Ie) {
742
+ const n = t.De;
743
+ const i = n.Ne || n;
744
+ if (i.Se) {
745
+ updateIfNecessary(i);
746
+ }
747
+ if (e.oe & REACTIVE_DIRTY) {
748
+ break;
749
+ }
750
+ }
751
+ }
752
+ if (e.oe & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY) || e.o?._ && e.Ge < clock && !e.o?.pe) {
753
+ recompute(e);
754
+ }
755
+ // The guard above refused an already-disposed node; the recompute it just
756
+ // ran may have disposed it (#3621) — carry the flag, or it comes back alive.
757
+ // The manual-write mask is state, not scheduling (#3612): it says the
758
+ // node's staging is a PROPOSAL, and only the commit (or a later-tick
759
+ // refresh, #3026) lifts it — a pull that recomputed nothing must not, or
760
+ // an isPending()/latest() probe decided whether a later write to a held
761
+ // node was a second proposal or the derivation's `prev`.
762
+ e.oe = e.oe & (REACTIVE_SNAPSHOT_STALE | REACTIVE_IN_HEAP | REACTIVE_IN_HEAP_HEIGHT | REACTIVE_ZOMBIE | REACTIVE_DISPOSED | REACTIVE_MANUAL_WRITE);
763
+ }
764
+
765
+ function computed(e, t) {
766
+ const n = t?.transparent ?? false;
767
+ // `in` (not `!== undefined`): an explicit `loadingValue: undefined` on a
768
+ // `T | undefined` node is a real commit #0. The typeof guard tolerates
769
+ // non-object option values that older call shapes force through `as any`.
770
+ const i = t !== null && typeof t === "object" && "loadingValue" in t;
771
+ // Two literals, one per tier, selected at build time (the observe flag is
772
+ // a literal after replacement; the untaken branch is dead code). The observe
773
+ // literal is the prod literal plus its `_name` slot — a slot in the
774
+ // boilerplate, because a post-construction `self._name = …` forces a
775
+ // hidden-class transition and an out-of-object property store on EVERY node
776
+ // (measured: the whole of the observe tier's creation overhead). Keep the
777
+ // two in sync — the dist artifact test pins observe's key set to prod's
778
+ // plus `_name`.
779
+ const l = {
780
+ id: inheritId(t, n, context),
781
+ C: (n ? CONFIG_TRANSPARENT : 0) | (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (!context || t?.lazy ? CONFIG_AUTO_DISPOSE : 0) | (t?.sync ? CONFIG_SYNC : 0) | (t?.Z ? CONFIG_NO_SNAPSHOT : 0) | (
782
+ // Plumbing is an observe-tier notion (a name and records to
783
+ // withhold); the prod literal never carries the bit.
784
+ t?.Ft ? CONFIG_PLUMBING : 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
785
+ Fe: t?.equals ?? isEqual,
786
+ Ve: null,
787
+ T: context?.T ?? globalQueue,
788
+ rt: context?.rt ?? defaultContext,
789
+ Nt: 0,
790
+ Se: e,
791
+ ce: i ? t.loadingValue : undefined,
792
+ ft: 0,
793
+ gt: undefined,
794
+ vt: null,
795
+ Ee: null,
796
+ Je: null,
797
+ We: 0,
798
+ u: null,
799
+ Ht: null,
800
+ _parent: context,
801
+ Fn: null,
802
+ kt: null,
803
+ _n: null,
804
+ oe: t?.lazy ? REACTIVE_LAZY : REACTIVE_NONE,
805
+ S: i ? 0 : STATUS_UNINITIALIZED,
806
+ Ge: clock,
807
+ _e: NOT_PENDING,
808
+ me: null,
809
+ Lt: -1,
810
+ ye: i,
811
+ o: null,
812
+ // The slot is always present (hidden class); plumbing leaves it
813
+ // unset, which `ownerPath` skips.
814
+ _name: t?.Ft ? undefined : t?.name ?? "computed"
815
+ };
816
+ if (t?.unobserved) ext(l).bt = t.unobserved;
817
+ setupComputedNode(l, t);
818
+ return l;
819
+ }
820
+
821
+ /** Lazily allocate a node's cold extension (ONE shape for signals and
822
+ * computeds — `_x` access stays monomorphic). Installers write through
823
+ * this; hot paths read `el._x?._field` gated by the _config presence bits.
824
+ * Never call ext() just to store a field's default. */ function ext(e) {
825
+ return e.o ??= {
826
+ we: undefined,
827
+ Vt: undefined,
828
+ Pt: 0,
829
+ Ut: NOT_PENDING,
830
+ yt: 0,
831
+ Ce: undefined,
832
+ xe: undefined,
833
+ Me: undefined,
834
+ $n: undefined,
835
+ t: 0,
836
+ pe: null,
837
+ be: null,
838
+ _: undefined,
839
+ Oe: undefined,
840
+ ue: undefined,
841
+ h: undefined,
842
+ ve: false,
843
+ i: null,
844
+ bt: undefined,
845
+ ct: undefined,
846
+ Et: null,
847
+ dt: null,
848
+ xt: undefined
849
+ };
850
+ }
851
+
852
+ /**
853
+ * Build an Effect node with all effect-specific fields baked into a single object literal,
854
+ * so V8 sees the full hidden class shape at construction time. Effects always run in lazy
855
+ * mode (recompute is called explicitly by `effect()`), so we hardcode the lazy bits and skip
856
+ * the auto-dispose CONFIG bit (effect() previously cleared it post-construction).
857
+ */ function createEffectNode(e, t, n, i, l) {
858
+ const u = l?.transparent ?? false;
859
+ // Prod and observe boilerplates — see computed() for why the observe tier
860
+ // gets its `_name` as a literal slot rather than a write after the fact.
861
+ // The default label is the node kind (tracked effects relabel their computed
862
+ // in trackedEffect); the wrappers in signals.ts no longer spread a name into
863
+ // the options to get it.
864
+ const o = {
865
+ id: inheritId(l, u, context),
866
+ C: (u ? CONFIG_TRANSPARENT : 0) | (l?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (l?.sync ? CONFIG_SYNC : 0) | (l?.Qt ?? 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
867
+ Fe: false,
868
+ Ve: null,
869
+ T: context?.T ?? globalQueue,
870
+ rt: context?.rt ?? defaultContext,
871
+ Nt: 0,
872
+ Se: e,
873
+ ce: undefined,
874
+ ft: 0,
875
+ gt: undefined,
876
+ vt: null,
877
+ Ee: null,
878
+ Je: null,
879
+ We: 0,
880
+ u: null,
881
+ Ht: null,
882
+ _parent: context,
883
+ Fn: null,
884
+ kt: null,
885
+ _n: null,
886
+ oe: REACTIVE_LAZY,
887
+ S: STATUS_UNINITIALIZED,
888
+ Ge: clock,
889
+ _e: NOT_PENDING,
890
+ me: null,
891
+ Lt: -1,
892
+ ye: false,
893
+ it: false,
894
+ Mt: undefined,
895
+ wt: t,
896
+ Wt: n,
897
+ qt: undefined,
898
+ He: i,
899
+ Gt: null,
900
+ o: null,
901
+ _name: l?.name ?? "effect"
902
+ };
903
+ // Effects dispatch status through the SHARED notifier (statusNotifierOf,
904
+ // keyed off _type) — storing it per node forced a full NodeExtension
905
+ // allocation on EVERY effect at creation (an alloc + 19 field stores,
906
+ // +23% effect creation, caught by the creation benches). Only genuinely
907
+ // per-node channels (boundaries) live on _x.
908
+ if (l?.unobserved) ext(o).bt = l.unobserved;
909
+ setupComputedNode(o, lazyOptions);
910
+ return o;
911
+ }
912
+
913
+ /**
914
+ * The shared status notifier for effect nodes, installed once by effect.ts
915
+ * at module evaluation (`this`-dispatched — one function serves every
916
+ * effect, so nodes never store it). Boundary computeds keep their own
917
+ * per-node channel on `_x._notifyStatus`, which takes precedence.
918
+ */ let effectStatusNotify = null;
919
+
920
+ function setEffectStatusNotify(e) {
921
+ effectStatusNotify = e;
922
+ }
923
+
924
+ /** Resolve a node's status notifier: an own `_x` channel (boundaries) wins;
925
+ * effect nodes (`_type` — EFFECT_PURE is 0, and only effect literals carry
926
+ * the field) fall back to the shared notifier. Presence doubles as the
927
+ * "display consumer" membership test in the status walks, exactly as the
928
+ * per-node field did when every effect carried one. */ function statusNotifierOf(e) {
929
+ const t = e.o?.h;
930
+ if (t !== undefined) return t;
931
+ return e.He ? effectStatusNotify ?? undefined : undefined;
932
+ }
933
+
934
+ const lazyOptions = {
935
+ lazy: true
936
+ };
937
+
938
+ function setupComputedNode(e, t) {
939
+ e.vt = e;
940
+ const n = context?.Zt ? context.Yt : context;
941
+ if (context) linkChild(context, e);
942
+ if (n) e.ft = n.ft + 1;
943
+ if (GlobalQueue.Bt !== null) GlobalQueue.Bt(e);
944
+ !t?.lazy && recompute(e, true);
945
+ if (snapshotCaptureActive && !t?.lazy) {
946
+ if (!(e.S & STATUS_PENDING) && !(e.C & CONFIG_NO_SNAPSHOT)) {
947
+ ext(e).ct = e.ce === undefined ? NO_SNAPSHOT : e.ce;
948
+ e.C |= CONFIG_HAS_SNAPSHOT;
949
+ snapshotSources.add(e);
950
+ }
951
+ }
952
+ }
953
+
954
+ function signal(e, t, n = null) {
955
+ // Prod and observe boilerplates — see computed(). The observe literal adds
956
+ // `_name` and `_owner` (the creating owner, stamped by registerGraph for
957
+ // createSignal nodes so ownerPath can locate signal subjects; null here,
958
+ // and staying null on internal signals — one shape either way).
959
+ const i = {
960
+ Fe: t?.equals ?? isEqual,
961
+ C: (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (t?.Z ? CONFIG_NO_SNAPSHOT : 0),
962
+ ce: e,
963
+ u: null,
964
+ Ht: null,
965
+ Ge: clock,
966
+ Ne: n,
967
+ ge: n?.o?.i || null,
968
+ jt: null,
969
+ _e: NOT_PENDING,
970
+ me: null,
971
+ Lt: -1,
972
+ o: null,
973
+ _name: t?.name ?? "signal",
974
+ ie: null
975
+ };
976
+ if (t?.unobserved) ext(i).bt = t.unobserved;
977
+ if (n) linkFirewallChild(n, i);
978
+ if (snapshotCaptureActive && !(i.C & CONFIG_NO_SNAPSHOT) && !((n?.S ?? 0) & STATUS_PENDING)) {
979
+ ext(i).ct = e === undefined ? NO_SNAPSHOT : e;
980
+ i.C |= CONFIG_HAS_SNAPSHOT;
981
+ snapshotSources.add(i);
982
+ }
983
+ return i;
984
+ }
985
+
986
+ // ---------------------------------------------------------------------------
987
+ // SLOT SIGNALS (store leaves) — the create-floor diet. Store mounts
988
+ // materialize one signal per touched leaf (~13 × rows on dbmon), so per-node
989
+ // allocations are mount bytes: the generic path costs an options object, an
990
+ // equals closure, an unobserved closure, a NodeExtension to hold it, and
991
+ // three post-construction expandos (acc/px/pxv → hidden-class transitions).
992
+ // slotSignal bakes everything into ONE literal: `_host`/`_key` backrefs
993
+ // replace the closures (equals is a method call — `this` is the node; the
994
+ // unobserved sweep dispatches CONFIG_SLOT_NODE to one shared hook), and the
995
+ // store's wrap-cache fields are pre-shaped.
996
+ /** The shared slot-node unobserved handler — a live binding read directly by
997
+ * the sweep sites (no wrapper frame, no null check: a CONFIG_SLOT_NODE node
998
+ * existing implies the store module loaded and registered the hook). */ let slotUnobservedHook;
999
+
1000
+ /** Install the shared slot-node unobserved handler (store module, once). */ function setSlotUnobserved(e) {
1001
+ slotUnobservedHook = e;
1002
+ }
1003
+
1004
+ /** Push a new node onto its firewall's child chain (the literal already
1005
+ * points `_nextChild` at the old head). Doubly linked so a released leaf
1006
+ * unlinks in O(1) — the chain is walked per mark of the projection and
1007
+ * would otherwise grow by one node per leaf ever read (#3351). */ function linkFirewallChild(e, t) {
1008
+ const n = t.ge;
1009
+ if (n !== null) n.jt = t;
1010
+ ext(e).i = t;
1011
+ e.C |= CONFIG_FW_CHILDREN;
1012
+ }
1013
+
1014
+ /** Release a firewall child the store no longer addresses (unobserved sweep
1015
+ * dropped it from its target's cache): unlink it from the chain so the
1016
+ * projection stops retaining it and its last value. The node keeps its own
1017
+ * `_nextChild` so a walk that is mid-chain on it still terminates. It also
1018
+ * leaves `_companionChildren` (#3503): the companions themselves are
1019
+ * permanent on the node, but the node is unreachable through the store, so
1020
+ * the set would only retain it and its last value. */ function unlinkFirewallChild(e) {
1021
+ const t = e;
1022
+ const n = t.Ne;
1023
+ if (!n) return;
1024
+ const i = t.jt;
1025
+ const l = t.ge;
1026
+ if (i !== null) i.ge = l; else if (n.o.i === t) n.o.i = l;
1027
+ if (l !== null) l.jt = i;
1028
+ t.jt = null;
1029
+ // #3503: the companion set is the last projection-side reference to a
1030
+ // released leaf; leaving it there kept the leaf and its last value alive
1031
+ // for the projection's lifetime after every latest()/isPending() reader
1032
+ // was disposed.
1033
+ n.o.xt?.delete(t);
1034
+ }
1035
+
1036
+ function slotSignal(e, t, n, i, l, u = null) {
1037
+ // Prod and observe boilerplates — see computed(). The store relabels the
1038
+ // observe slot (`store.<key>`) when the attribution engine is installed.
1039
+ const o = {
1040
+ Fe: t,
1041
+ C: CONFIG_OWNED_WRITE | CONFIG_SLOT_NODE,
1042
+ ce: e,
1043
+ u: null,
1044
+ Ht: null,
1045
+ Ge: clock,
1046
+ Ne: u,
1047
+ ge: u?.o?.i || null,
1048
+ jt: null,
1049
+ _e: NOT_PENDING,
1050
+ me: null,
1051
+ Lt: -1,
1052
+ o: null,
1053
+ Kt: n,
1054
+ zt: i,
1055
+ acc: l,
1056
+ px: undefined,
1057
+ pxv: undefined,
1058
+ _name: "signal"
1059
+ };
1060
+ if (u) linkFirewallChild(u, o);
1061
+ if (snapshotCaptureActive && !((u?.S ?? 0) & STATUS_PENDING)) {
1062
+ ext(o).ct = e === undefined ? NO_SNAPSHOT : e;
1063
+ o.C |= CONFIG_HAS_SNAPSHOT;
1064
+ snapshotSources.add(o);
1065
+ }
1066
+ return o;
1067
+ }
1068
+
1069
+ function optimisticSignal(e, t) {
1070
+ const n = signal(e, t);
1071
+ ext(n).we = NOT_PENDING;
1072
+ n.C |= CONFIG_OPTIMISTIC;
1073
+ return n;
1074
+ }
1075
+
1076
+ function optimisticComputed(e, t) {
1077
+ const n = computed(e, t);
1078
+ ext(n).we = NOT_PENDING;
1079
+ n.C |= CONFIG_OPTIMISTIC;
1080
+ return n;
1081
+ }
1082
+
1083
+ function isEqual(e, t) {
1084
+ return e === t;
1085
+ }
1086
+
1087
+ /**
1088
+ * Runs `fn` outside of any reactive tracking — reads inside `fn` will not
1089
+ * subscribe the current scope. Returns whatever `fn` returns.
1090
+ *
1091
+ * Use `untrack` inside a memo or effect when you need to read a signal once
1092
+ * without making the surrounding computation depend on its future changes.
1093
+ *
1094
+ * Pass a `strictReadLabel` string to enable a dev-mode warning: any reactive
1095
+ * read inside `fn` that isn't inside a nested tracking scope will log a
1096
+ * warning naming the label.
1097
+ *
1098
+ * @example
1099
+ * ```ts
1100
+ * createEffect(
1101
+ * () => trigger(), // tracks `trigger` only
1102
+ * () => {
1103
+ * const snapshot = untrack(() => state); // read once, untracked
1104
+ * log(snapshot);
1105
+ * }
1106
+ * );
1107
+ * ```
1108
+ */ function untrack(e, t) {
1109
+ if (GlobalQueue.Jt === null && !tracking && true) return e();
1110
+ const n = tracking;
1111
+ tracking = false;
1112
+ try {
1113
+ if (GlobalQueue.Jt !== null) return GlobalQueue.Jt(e);
1114
+ return e();
1115
+ } finally {
1116
+ tracking = n;
1117
+ }
1118
+ }
1119
+
1120
+ /**
1121
+ * Set while runtime bookkeeping reads a node inside another node's pass (a
1122
+ * loading boundary priming its tree at creation, from whatever pass is
1123
+ * mounting it). `context` is that node, but the read is nobody's: the value
1124
+ * is probed, never derived from, so nothing the read would normally record
1125
+ * on `context` may be recorded — not the untracked-pending re-run link
1126
+ * (`read`, `!tracking`; #3528: a boundary's `on` key read this way from
1127
+ * `notify` linked the key's source into an unrelated async memo, a cycle that
1128
+ * never converged), and not a transaction entry (`enterStagedRead`; #3540: a
1129
+ * born-held tree would otherwise pull the mounting pass into the hold).
1130
+ */ let spectating = false;
1131
+
1132
+ /**
1133
+ * Evaluates `fn` untracked, recording nothing on the current `context`: no
1134
+ * untracked-pending re-run link, no transaction entry. For bookkeeping reads
1135
+ * made on behalf of no node — see `spectating`.
1136
+ */ function spectate(e) {
1137
+ const t = spectating;
1138
+ spectating = true;
1139
+ try {
1140
+ return untrack(e);
1141
+ } finally {
1142
+ spectating = t;
1143
+ }
1144
+ }
1145
+
1146
+ /**
1147
+ * Bring a computed to a readable state: lazy/disposed nodes are (re)computed;
1148
+ * an isPending() probe (`refresh`) additionally pulls the node fully up to
1149
+ * date so its status flags reflect the current graph.
1150
+ */ function prepareComputed(e, t) {
1151
+ if (e.oe & REACTIVE_LAZY) {
1152
+ e.oe &= ~REACTIVE_LAZY;
1153
+ recompute(e, true);
1154
+ } else if (e.oe & REACTIVE_DISPOSED) {
1155
+ // Two disposal lifecycles share the flag (#3024). Observation-lifecycle
1156
+ // nodes (CONFIG_AUTO_DISPOSE) are dormant — torn down by unobserved()
1157
+ // when the last subscriber left — and reads reawaken them; that is the
1158
+ // pay-for-use contract. Owner-lifecycle nodes are dead: recomputing would
1159
+ // re-run user code in a torn-down tree (and discard manual writes on
1160
+ // derived-writable signals), so reads return the last committed value.
1161
+ if (e.C & CONFIG_AUTO_DISPOSE) {
1162
+ const t = e._parent;
1163
+ if (t !== null) {
1164
+ // A dormant node was off the chain when its owner died, so the strip
1165
+ // in disposeChildren missed it: freeze here instead (#3024).
1166
+ if (t.oe & REACTIVE_DISPOSED) {
1167
+ e.C &= ~CONFIG_AUTO_DISPOSE;
1168
+ return;
1169
+ }
1170
+ // A zombie never left its chain: settleAutodispose releases without
1171
+ // the !ZOMBIE check its siblings have, so this guard is load-bearing.
1172
+ if (!(e.oe & REACTIVE_ZOMBIE)) linkChild(t, e);
1173
+ }
1174
+ recompute(e, true);
1175
+ }
1176
+ } else if (t) {
1177
+ updateIfNecessary(e);
1178
+ }
1179
+ }
1180
+
1181
+ /**
1182
+ * Sentinel returned by readNodeFast when the plain-signal fast path does not
1183
+ * apply and the caller must fall back to the full read().
1184
+ */ const READ_SLOW = Symbol("read-slow");
1185
+
1186
+ /**
1187
+ * read()'s plain-signal fast path as a standalone entry for hot callers
1188
+ * (store traps). Safe to substitute for read() only because the bail
1189
+ * conditions mirror read()'s prelude and fast-path guard exactly: the
1190
+ * latestRead and pendingCheck windows run side-effectful hooks before the
1191
+ * fast path, `_fn` nodes need prepareComputed, and firewall / override /
1192
+ * snapshot / transition / lane / dev-strictRead state all take the full
1193
+ * resolution. Anything slow returns READ_SLOW; the caller then calls read().
1194
+ */
1195
+ /**
1196
+ * Wake only authoritative-view readers (until() predicates) subscribed to `el`.
1197
+ * The A17-silent ack paths — an authoritative arrival equal to the active
1198
+ * override — use this so the predicate re-evaluates without re-firing
1199
+ * ordinary subscribers whose visible (override) value did not change.
1200
+ * Pay-for-use: reached through GlobalQueue._notifyAuthoritativeObservers,
1201
+ * installed at first until() call — apps that never use until() shake it.
1202
+ */ function notifyAuthoritativeObservers(e) {
1203
+ for (let t = e.u; t !== null; t = t.Pe) {
1204
+ const e = t.Ae;
1205
+ if (!(e.C & CONFIG_AUTHORITATIVE_READ)) continue;
1206
+ // Missed-wake latch (#3037), same contract as insertSubs: the reader may
1207
+ // itself have pulled this recompute (updateIfNecessary from its own
1208
+ // read), and the heap refuses RECOMPUTING nodes — latch so recompute's
1209
+ // tail reschedules it with the staged value visible.
1210
+ if (e.oe & REACTIVE_RECOMPUTING_DEPS && t.Ze === e.We && t !== e.Je) e.oe |= REACTIVE_MISSED_WAKE;
1211
+ enqueueSub(e);
1212
+ }
1213
+ schedule();
1214
+ }
1215
+
1216
+ /** Installs the authoritative-reader wakeup hook. Idempotent; called by every
1217
+ * creator of a CONFIG_AUTHORITATIVE_READ computation — until() and refresh() —
1218
+ * before its first read (same late-binding contract as the optimistic engine;
1219
+ * the gating bit is only ever set by such a read, so the `!` call sites are
1220
+ * safe once every setter installs, #3303). */ function installAuthoritativeRead() {
1221
+ if (GlobalQueue.Xt === null) GlobalQueue.Xt = notifyAuthoritativeObservers;
1222
+ }
1223
+
1224
+ /**
1225
+ * Stale-reader term of the value selections below: a render effect reading a
1226
+ * node some OTHER live transaction has staged sees the committed value. The
1227
+ * commit is silent — the staging walk was the notification — so a reader
1228
+ * that linked AFTER that walk (an effect created during the hold, a store
1229
+ * key first read under it) would show the old value past the reveal: record
1230
+ * it for the transaction's commit replay (the `_gatedSubs` contract lanes
1231
+ * already use). An effect the transaction itself computed re-derives at its
1232
+ * commit on its own (parked run, or the contested re-derive, #3322) and is
1233
+ * not recorded — replaying it too would publish the frame twice.
1234
+ *
1235
+ * Flight twin (the pending-branch carve-out): the reader is served the
1236
+ * node's committed, pre-flight value and now observes that flight — A15:
1237
+ * async work observed by a reader settles as one unit with the writes that
1238
+ * asked it — so it joins the transaction's reporters for the node. The
1239
+ * reporter the transaction recorded when the flight started may be gone (a
1240
+ * keyed remount disposed it, #3374); a completion check that found no live
1241
+ * reporter committed the writes ahead of the answer, tearing the new
1242
+ * reader's frame (`Count: 1` beside `Details: 0`). Joins an entry the
1243
+ * transaction already holds; a flight nobody had observed yet has none (the
1244
+ * reader is its first observer — a conditional that just revealed it, #3458)
1245
+ * and is notified up the reader's own queue chain under that transaction,
1246
+ * the one sanctioned registration site (INV-3): a collecting boundary above
1247
+ * the reader consumes it as it would any pending, an unboundaried reader
1248
+ * opens the entry — and the transaction, judged complete on its other
1249
+ * flights, revealed the inputs beside the reader's pre-flight value
1250
+ * otherwise (`Count: 1 | A: 1` beside `B: 0`). A staged signal or a settled
1251
+ * node registers nothing. Every reporter dies with its reader
1252
+ * (reporterBlocksSource: the read linked it as a dep). The node's own entry
1253
+ * is the only one that can matter: a chain's intermediate memo is re-pulled
1254
+ * by the read (updateIfNecessary's retry) and enters the transaction, so the
1255
+ * reader holds through the normal path; a node with its own flight that is
1256
+ * also pending on an upstream re-ask blocks through that flight until it
1257
+ * lands, and its landing re-runs the reader into the normal path.
1258
+ */
1259
+ /** The replay half of the stale-of-foreign clause (A15 / A26): a stale reader
1260
+ * served the committed value because `txn` holds what it read re-runs at
1261
+ * txn's commit, when the value it was denied becomes the frame — unless its
1262
+ * own last value already came from that transaction. One registration for
1263
+ * the node path (heldFromStale) and the store's backing paths, which have
1264
+ * no node to carry the hold (heldFromReader, the adoption hold view). */ function recordStaleReplay(e, t) {
1265
+ const n = t.Gt;
1266
+ if (n == null || currentTransition(n) !== e) e.Tt.add(t);
1267
+ }
1268
+
1269
+ /**
1270
+ * The ownership relation (DESIGN-CONSOLIDATION §6, ruled 2026-09-17): is
1271
+ * `hold` part of the running pass's world? A plain reader's world is the
1272
+ * transaction it runs under, through merges. A lane reader's world is its
1273
+ * lane AND the transition that owns the lane — the one asymmetry between a
1274
+ * lane and a separate transaction (a lane sees what lands from its parent as
1275
+ * its own; a separate transaction would wait for the parent to settle) —
1276
+ * see `ownsLane` in lanes.ts, built on this. One relation for the
1277
+ * stale-of-foreign clause (heldFromStale), the lane arm (readsHeldCommitted)
1278
+ * and the store's backing holds (foreignHold); `serve` has no lane arm of
1279
+ * its own, the lane's extra visibility lives here.
1280
+ */ function ownsHold(e) {
1281
+ return activeTransition !== null && currentTransition(e) === currentTransition(activeTransition);
1282
+ }
1283
+
1284
+ function heldFromStale(e, t) {
1285
+ const n = e.me;
1286
+ if (n === null || ownsHold(n)) return false;
1287
+ const i = currentTransition(n);
1288
+ recordStaleReplay(i, t);
1289
+ const l = i.ae.get(e);
1290
+ if (l) l.add(t); else if (e.S & STATUS_PENDING) runInTransition(i, () => t.T.notify(t, STATUS_PENDING, STATUS_PENDING, e.o._));
1291
+ return true;
1292
+ }
1293
+
1294
+ /**
1295
+ * A tracked computation about to be served a live transaction's staged value
1296
+ * derives from that transaction's world, so it enters the transaction and
1297
+ * this pass's result is held with it (A29, #3408). The write side (`setSignal`)
1298
+ * and a stamped node's recompute already enter; a reader that only now
1299
+ * starts reading the held node — a conditional memo whose branch flipped —
1300
+ * was the gap: it published a value derived from the staged world into the
1301
+ * mainline frame. Not for a probe (`isPending(() => x())` observes, it does
1302
+ * not derive) and a no-op for the ambient batch (`_transition` null) or the
1303
+ * transaction already active.
1304
+ */
1305
+ /** The transaction a pass running OUTSIDE a flush was served a staged value
1306
+ * from (A29, creation-time form). Inside a flush the pass enters through
1307
+ * initTransition; outside one — a memo or effect created from mainline code
1308
+ * while a hold is live — entering would leave `activeTransition` and the
1309
+ * batch pointed at the transaction for the rest of the synchronous block,
1310
+ * so an unrelated write made after the mount was held with someone else's
1311
+ * action. The entry is the pass's alone: recompute stages the node into the
1312
+ * transaction (born held) and mainline is never touched.
1313
+ *
1314
+ * A pass under a FRESH loading boundary takes this path inside a flush too
1315
+ * (#3540). Born held is right for a plain memo or effect: published, its
1316
+ * value would tear the frame. A boundary that has not revealed yet is the
1317
+ * exception by definition — its job is to catch what is not ready under it
1318
+ * rather than let it hold: the pass is staged into the transaction as
1319
+ * above, and the boundary is told (recompute's born-held arm notifies it
1320
+ * with a `NotReadyError` sourced at the node) so it shows its fallback now
1321
+ * and reveals the staged result at the commit.
1322
+ * Entering instead adopted the whole flush into the hold — a `Show` that
1323
+ * flipped mainline to mount a `Loading` over a held value waited for the
1324
+ * hold with it. A boundary that already shows content keeps the entering
1325
+ * path: it has content to keep, and holds like any reader. */ let stagedEntry = null;
1326
+
1327
+ /** Is `el` routed to a loading boundary that still shows its fallback — the
1328
+ * nearest pending-collecting queue up its chain is uninitialized? (Measured
1329
+ * 2026-09-18: relocating the walk behind a `GlobalQueue` slot installed by
1330
+ * boundaries.ts saved ~8 B on the core floor and cost boundary-using apps
1331
+ * 50–70 B — the indirection's tokens outweigh the walk. Kept inline.) */ function underFreshLoadingBoundary(e) {
1332
+ for (let t = e.T; t !== null; t = t._parent) if (t.te & STATUS_PENDING) return !t.H;
1333
+ return false;
1334
+ }
1335
+
1336
+ function enterStagedRead(e, t = e.me) {
1337
+ if (!t || t === activeTransition || pendingCheckActive) return;
1338
+ // A companion (the latest() shadow, the isPending() verdict signal) is the
1339
+ // engine's mirror of the flushed world — reading it, or being it, is an
1340
+ // observation, not a derivation from the hold: latest(x) never enters x's
1341
+ // transaction, and the shadow's own pass never enters either (it would
1342
+ // flip activeTransition under the reader that pulled it). (`el` is null for
1343
+ // a store backing served under a hold — no node, the transaction is the
1344
+ // fold's.)
1345
+ if (e?.o?.$n || context?.o?.$n) return;
1346
+ // A bookkeeping read (`spectate`) is nobody's derivation: it compares or
1347
+ // probes, and enters nothing — the boundary priming read of a born-held
1348
+ // tree must not enter the creator's pass into the hold (#3540).
1349
+ if (spectating) return;
1350
+ // Verdict machinery (GlobalQueue._verdictPull: companion creation and the
1351
+ // latest()/isPending() pulls — the latest() shadow is created before it is
1352
+ // marked optimistic, so the bit alone cannot tell) and optimistic nodes
1353
+ // (own lane posture, A31) read staged truth by design; they keep the
1354
+ // entering path.
1355
+ // (`context` is non-null here: every caller selected a value for a reader.)
1356
+ const n = context;
1357
+ const i = activeTransition === null && !globalQueue.$t;
1358
+ // Verdict pulls are observations, not derivations: a latest() /
1359
+ // isPending() call from mainline must never enter a transaction (it
1360
+ // would capture the rest of the caller's synchronous block).
1361
+ if (i && GlobalQueue.Tn) return;
1362
+ if (n.oe & REACTIVE_RECOMPUTING_DEPS && !(n.C & CONFIG_OPTIMISTIC) && (stagedEntry === null || stagedEntry === t) && (i || !GlobalQueue.Tn && underFreshLoadingBoundary(n))) {
1363
+ stagedEntry = t;
1364
+ return;
1365
+ }
1366
+ globalQueue.initTransition(t);
1367
+ }
1368
+
1369
+ /**
1370
+ * Rule 1 (value selection), the full arm: does this reader see a STAGED
1371
+ * node's COMMITTED value? One implementation of the rule the fast paths
1372
+ * (readNodeFast, read's fast block) carry as their trivial ternary and that
1373
+ * every slow site — read's tail, the store's backing selection, the lane and
1374
+ * verdict arms — used to restate by hand (docs/DESIGN-CONSOLIDATION.md, move 3b). In order:
1375
+ * - no reader at all (an untracked read) — the committed frame;
1376
+ * - a reader under an optimistic lane the engine says reads committed
1377
+ * (laneReadsCommitted: another lane's hold, #3460);
1378
+ * - nothing staged;
1379
+ * - a children-forbidden reader (createTrackedEffect / onSettled: the frame,
1380
+ * never the graph — A32);
1381
+ * - a stale reader (render effect) of a FOREIGN transaction's staged write —
1382
+ * committed, no entanglement (heldFromStale registers the replay; a node
1383
+ * born held has no committed frame to fall back to, `noCommitted`);
1384
+ * - HELD truth (#3164, CONFIG_HELD_TRUTH) read by a LANE pass: staged
1385
+ * confirming truth — fold-staged onto an armed family, or entangle-stolen
1386
+ * by an awaited until() — is masked from lane passes only, owning
1387
+ * transaction or not. A lane applies its frame display-ahead at the park,
1388
+ * so a lane pass served the truth would paint the confirmation beside the
1389
+ * optimism it confirms (`saving=true` beside the saved row — the #3164
1390
+ * tear); it keeps committed and is re-run by the reveal's post-revert
1391
+ * wake. Every other deriving reader falls through to A29 below: the truth
1392
+ * is a staged value like any other, and the pass that derives from it is
1393
+ * held with it — including the retaining transaction's own passes, which
1394
+ * a superseded override already hands the truth (#3568: masking them
1395
+ * composed the landed `length` with rows still masked to committed).
1396
+ * latest() and authoritative readers (until()'s predicate) tunnel
1397
+ * through — the tunnel that keeps the hold deadlock-free.
1398
+ * False means the reader derives from the staged value and enters its
1399
+ * transaction (enterStagedRead, A29).
1400
+ */ function readerSeesCommitted(e, t, n, i) {
1401
+ return !!(!t || currentOptimisticLane !== null && GlobalQueue.On(e, n, t) || e._e === NOT_PENDING || t.C & CONFIG_CHILDREN_FORBIDDEN || stale && !i && heldFromStale(e, t) || e.C & CONFIG_HELD_TRUTH && currentOptimisticLane !== null && !latestReadActive && !(t.C & CONFIG_AUTHORITATIVE_READ));
1402
+ }
1403
+
1404
+ /** A28 — set when a node is staged (queuePendingNode) or a held node rewritten
1405
+ * (stashHeldRewrite) OUTSIDE a flush; cleared when the next flush begins. The
1406
+ * read sites test this one module boolean instead of `globalQueue._running`:
1407
+ * inside a flush it is false and the A28 arm costs nothing; outside, only a
1408
+ * tick with unflushed writes pays the staged-node check. */ let unflushedStaged = false;
1409
+
1410
+ function markUnflushedStaged() {
1411
+ unflushedStaged = true;
1412
+ }
1413
+
1414
+ /** A28 — a write becomes visible at flush. Outside a flush, a node holding an
1415
+ * AMBIENT staged value (no transaction stamp) was written since the last
1416
+ * flush: ambient staging commits at flush end, so nothing else leaves a node
1417
+ * in this state; a stamped value is the flushed held world, which A28 says
1418
+ * latest() serves. Inside a flush the rule does not apply (A28 (4): promoted
1419
+ * within the round). Structural — no marker on the write path. */ function unflushed(e) {
1420
+ return unflushedValue(e) !== NOT_PENDING;
1421
+ }
1422
+
1423
+ /** The value an unflushed node serves — the committed value for an ambient
1424
+ * write, the flushed staged value for a rewrite of a held node — or
1425
+ * NOT_PENDING when nothing is unflushed. Exempt: owned-write nodes (A28 (4):
1426
+ * a write issued inside a recompute is promoted at that recompute's end —
1427
+ * boundary and loading machinery, until()'s internals, signals declared for
1428
+ * in-computation writes) and engine companions (the isPending() verdict
1429
+ * signal, the latest() shadow: the system's own writes, made at the source's
1430
+ * write to mirror it, installing eagerly — A28, A8). */ function unflushedValue(e, t = e.ce) {
1431
+ if (globalQueue.$t || e._e === NOT_PENDING || e.C & CONFIG_PROMOTED || e.o?.$n) return NOT_PENDING;
1432
+ // Ambient, or adopted by a transaction before any flush carried the staging
1433
+ // (CONFIG_ADOPTED_UNFLUSHED): nothing flushed is staged — the committed
1434
+ // value answers (the caller's notion of committed: a store node's backing).
1435
+ // A held node: unflushed only if rewritten since the last flush (stash).
1436
+ if (e.me === null || e.C & CONFIG_ADOPTED_UNFLUSHED) return t;
1437
+ return e.o === null ? NOT_PENDING : e.o.Ut;
1438
+ }
1439
+
1440
+ /** Held nodes rewritten since the last flush (setSignal); the flush clears
1441
+ * their stash — from then on latest() answers with the rewrite. */ const unflushedRewrites = [];
1442
+
1443
+ /** Nodes written inside a creation-time recompute (CONFIG_PROMOTED). */ const promotedWrites = [];
1444
+
1445
+ /** A28 (5): an optimistic write becomes the ACTIVE override at the flush that
1446
+ * carries it. `_overrideTime` is stamped with `clock` at the write and `clock`
1447
+ * advances after every flush, so "this tick, outside a flush" is unflushed. */ function unflushedOverride(e) {
1448
+ // Companions are optimistic signals written by the engine (see unflushed).
1449
+ return !globalQueue.$t && e.o?.Pt === clock && !e.o?.$n;
1450
+ }
1451
+
1452
+ /** Active optimistic override on an armed node (an armed slot idles at
1453
+ * NOT_PENDING; undefined = unarmed plain node). The writer's own channels —
1454
+ * the draft, `in`/keys inside the setter — compose on this regardless of
1455
+ * flush state. */ function hasActiveOverride(e) {
1456
+ const t = e.o;
1457
+ return t !== null && t.we !== undefined && t.we !== NOT_PENDING;
1458
+ }
1459
+
1460
+ /** The override a READER sees: installed, and carried by a flush (A28 (5) —
1461
+ * an optimistic write is a write; until its flush no reader sees it). One
1462
+ * implementation for read()'s override arm, the verdict channels
1463
+ * (latestRead, computePendingState) and the store's selection
1464
+ * (docs/DESIGN-CONSOLIDATION.md, move 3b). */ function visibleOverride(e) {
1465
+ return hasActiveOverride(e) && !unflushedOverride(e);
1466
+ }
1467
+
1468
+ /** A derivation served the committed value because of an unflushed write
1469
+ * (A28) must run again in the flush that carries it — the late-linker case
1470
+ * (#3337's reason to defer the walk): it linked after the write walked. */ function markLateLinker(e) {
1471
+ // The pass's own tail re-enqueues on this latch (recompute's finally) —
1472
+ // a direct enqueue here would be wiped by the pass's flag reset.
1473
+ e.oe |= REACTIVE_MISSED_WAKE;
1474
+ return true;
1475
+ }
1476
+
1477
+ /** Companion-bearing nodes written outside a flush (setSignal); the flush
1478
+ * that carries their writes re-syncs their companions (A28). */ const unflushedCompanions = [];
1479
+
1480
+ function resyncUnflushedCompanions() {
1481
+ unflushedStaged = false;
1482
+ // Length-guarded: the common flush has nothing here, and must allocate nothing.
1483
+ // Length-guarded: the common flush has nothing here and allocates nothing.
1484
+ if (unflushedRewrites.length !== 0) {
1485
+ for (const e of unflushedRewrites) e.o.Ut = NOT_PENDING;
1486
+ unflushedRewrites.length = 0;
1487
+ }
1488
+ if (promotedWrites.length !== 0) {
1489
+ for (const e of promotedWrites) e.C &= ~CONFIG_PROMOTED;
1490
+ promotedWrites.length = 0;
1491
+ }
1492
+ if (unflushedCompanions.length !== 0) {
1493
+ for (const e of unflushedCompanions) GlobalQueue.Le(e, e._e !== NOT_PENDING ? e._e : e.ce);
1494
+ unflushedCompanions.length = 0;
1495
+ }
1496
+ }
1497
+
1498
+ function readNodeFast(e) {
1499
+ if (latestReadActive || pendingCheckActive || e.Se || e.Ne || e.o?.we !== undefined || e.o?.ct !== undefined || activeTransition !== null || currentOptimisticLane !== null || snapshotCaptureActive ||
1500
+ // A28: a staged node read outside a flush may serve its committed value;
1501
+ // that arm lives on the slow path so this body stays inlinable.
1502
+ unflushedStaged && e._e !== NOT_PENDING || false) return READ_SLOW;
1503
+ let t = context;
1504
+ if (t?.Zt) t = t.Yt;
1505
+ if (t && tracking) link(e, t);
1506
+ // Children-forbidden readers (createTrackedEffect / onSettled callbacks) get
1507
+ // committed visibility: like the effect half of createEffect and event
1508
+ // handlers, effect-phase code never observes its own unsettled write — the
1509
+ // write lands in the same flush's continuation (#3006).
1510
+ // A stale reader (render effect) recomputing with no transaction active is
1511
+ // mainline: a write staged by a live transaction (`_transition` stamped —
1512
+ // ambient staging never is) stays masked until that transaction's reveal,
1513
+ // the same rule the slow path applies (#3322). Without it a zombie
1514
+ // recompute, or a commit-time re-derive of a contested effect, published
1515
+ // another transaction's uncommitted value.
1516
+ return !t || e._e === NOT_PENDING || t.C & CONFIG_CHILDREN_FORBIDDEN || stale && heldFromStale(e, t) ? e.ce : (enterStagedRead(e),
1517
+ e._e);
1518
+ }
1519
+
1520
+ function read(e) {
1521
+ // Handle latest() mode: read from _latestValueComputed
1522
+ // Checked before isPending so that isPending(() => latest(x)) checks
1523
+ // the _pendingSignal of _latestValueComputed (async in flight) rather
1524
+ // than the original node (which stays "pending" while held in a transition).
1525
+ if (latestReadActive) return GlobalQueue.An(e);
1526
+ let t = context;
1527
+ if (t?.Zt) t = t.Yt;
1528
+ const n = e;
1529
+ const i = e.Ne;
1530
+ const l = i || e;
1531
+ // Handle isPending() mode: collect pending state while preserving normal read semantics.
1532
+ // Probe mode is suspended while preparing the node so nested reads during a
1533
+ // recompute don't collect into the probe.
1534
+ if (pendingCheckActive) {
1535
+ GlobalQueue.Cn(e, t, l, i);
1536
+ } else if (typeof n.Se === "function") {
1537
+ prepareComputed(e, false);
1538
+ }
1539
+ if (!n.Se && l === e && e.o?.we === undefined && e.o?.ct === undefined && activeTransition === null && currentOptimisticLane === null && !snapshotCaptureActive && (!unflushedStaged || e._e === NOT_PENDING) && // A28, see readNodeFast
1540
+ true) {
1541
+ if (t && tracking) link(e, t);
1542
+ // Committed visibility for children-forbidden readers and for stale
1543
+ // readers of a foreign transaction's staged write — see readNodeFast.
1544
+ return !t || e._e === NOT_PENDING || t.C & CONFIG_CHILDREN_FORBIDDEN || stale && heldFromStale(e, t) ? e.ce : (enterStagedRead(e),
1545
+ e._e);
1546
+ }
1547
+ if (t && tracking) {
1548
+ link(e, t, pendingCheckActive);
1549
+ if (l.Se) {
1550
+ const n = queueFor(e);
1551
+ if (l.ft >= n.st) {
1552
+ markNode(t);
1553
+ markHeap(n);
1554
+ updateIfNecessary(l);
1555
+ }
1556
+ // Fresh-pull readers (awaitable refresh's waiter) recompute a dirty
1557
+ // source inline even when the height gate defers to the flush: the
1558
+ // waiter must park on the re-ask's window (or serve its sync answer),
1559
+ // never read the PRE-re-ask value as settled. Self-guarded: a clean
1560
+ // node no-ops and updateIfNecessary refuses disposed nodes (#2983) —
1561
+ // a dead target serves its last value, which is already quiescent.
1562
+ else if (t.C & CONFIG_FRESH_READ) updateIfNecessary(l);
1563
+ const i = l.ft;
1564
+ // parent check is shallow, might need to be recursive
1565
+ if (i >= t.ft && e._parent !== t) {
1566
+ t.ft = i + 1;
1567
+ }
1568
+ }
1569
+ }
1570
+ if (l.S & STATUS_PENDING) {
1571
+ // A reader landing on a pending node throws — the reveal that discovered
1572
+ // the flight holds on it (A15: observed async settles as one unit) — with
1573
+ // one carve-out: a stale (render) reader of a node pending in some OTHER
1574
+ // transaction keeps showing the node's committed value, no entanglement
1575
+ // (parallel transactions; the reader is recorded for that transaction's
1576
+ // commit replay, `heldFromStale`). The carve-out is sound only while the
1577
+ // committed value is coherent with the visible frame, i.e. while the
1578
+ // flight's inputs are themselves unpublished: the stamp alone does not
1579
+ // say so (it is pending-node bookkeeping), so it is refused when the
1580
+ // inputs are on screen — committed by a batch that left the flight in
1581
+ // the air (CONFIG_INPUTS_PUBLISHED, #3305) or revealed through a lane
1582
+ // (optimistic / latest, #3334) — and the reader holds instead. An
1583
+ // UNINITIALIZED node has no committed value to show and holds too
1584
+ // (firewall-backed store reads always did; plain memos since the #3043
1585
+ // port): falling through served `undefined` as if settled and stranded
1586
+ // the reader outside both transactions, so it never re-ran.
1587
+ if (t && !(stale && !(l.S & STATUS_UNINITIALIZED) && !(l.C & CONFIG_INPUTS_PUBLISHED) && !(l.C & CONFIG_HAS_LANE && GlobalQueue.Rn(l)) && heldFromStale(l, t))) {
1588
+ // Per-lane suspension lives with the engine (a non-null lane implies it
1589
+ // is installed): under a lane, only same-lane pending async without an
1590
+ // active override throws — plus uninitialized sources regardless of
1591
+ // lane (#3276); that check rides laneSuspends so floor bundles don't
1592
+ // pay for it.
1593
+ if (currentOptimisticLane === null || GlobalQueue.Gn(l)) {
1594
+ // An untracked read of a pending node still re-runs its reader when
1595
+ // the node settles — unless nobody is reading (`spectating`, #3528)
1596
+ // or the reader is the node itself.
1597
+ if (!tracking && !spectating && e !== t) link(e, t);
1598
+ throw l.o?._;
1599
+ }
1600
+ } else if (!t && l.S & STATUS_UNINITIALIZED &&
1601
+ // An armed DERIVED override shields the uninitialized state from a
1602
+ // read with no reader identity (#3648 follow-up, A18 (d)): a memo
1603
+ // whose first landing rode its lane (asyncWrite's lane branch →
1604
+ // `laneOverride`) has no `_value` — the flag stays set until the lane's
1605
+ // commit promotes the override — yet the override IS the displayed
1606
+ // value of a pending node (A18 (c), the screen keeps it until the
1607
+ // commit), exactly as an initialized node re-deriving under its override
1608
+ // displays it. "Uninitialized must suspend" (#3276, A19 exception 1) was
1609
+ // written for a node with NOTHING to show; this node has the override.
1610
+ // The arm fires in the body-end window: the source override superseded
1611
+ // (A18 body-end, #3427), the memo re-deriving on the plain channel and
1612
+ // held, its own slot still armed. Fall through to serve()'s override
1613
+ // arm. Readers WITH identity are untouched: a tracked lane reader keeps
1614
+ // the override (A17), an off-lane one suspends (#3651, `overrideRead`).
1615
+ !(hasActiveOverride(e) && e.C & CONFIG_DERIVED_OVERRIDE)) {
1616
+ throw l.o?._;
1617
+ }
1618
+ }
1619
+ // `owner` is the computed itself, or the firewall behind a store node —
1620
+ // firewall-backed reads follow the same rules (memo parity, #2897 ruling):
1621
+ // an errored derive throws for every late reader instead of silently
1622
+ // serving node values (the seed, or last-good data after a failed refetch).
1623
+ if (l.Se && l.S & STATUS_ERROR) {
1624
+ // Only a genuine reactive re-read may retry an errored async source:
1625
+ // - tracking: owned/tracked scope only (never events / `untrack` / effect side-effect phase)
1626
+ // - !pendingCheckActive: an `isPending` probe observes the error, never refetches
1627
+ // - owner._time < clock: only on a later cycle than the one the error was found
1628
+ if (tracking && !pendingCheckActive && l.Ge < clock) {
1629
+ recompute(l);
1630
+ return read(e);
1631
+ } else throw l.o?._;
1632
+ }
1633
+ if (snapshotCaptureActive && t && t.C & CONFIG_IN_SNAPSHOT_SCOPE) {
1634
+ const n = e.o?.ct;
1635
+ if (n !== undefined) {
1636
+ const i = n === NO_SNAPSHOT ? undefined : n;
1637
+ const l = e._e !== NOT_PENDING ? e._e : e.ce;
1638
+ if (l !== i) t.oe |= REACTIVE_SNAPSHOT_STALE;
1639
+ return i;
1640
+ }
1641
+ }
1642
+ const u = serve(e, t, l, e.ce);
1643
+ if (!t && l === e && typeof n.Se === "function" && e.C & CONFIG_AUTO_DISPOSE && !(l.S & STATUS_PENDING) && !e.u &&
1644
+ // An untracked read served a visible override returned before this
1645
+ // sweep registration when the arm was inline; keep that.
1646
+ !visibleOverride(e)) {
1647
+ // Deferred, not inline (#3078): an inline unobserved() here made untracked
1648
+ // reads destructive — dispose on this read, full revival recompute on the
1649
+ // next — so consecutive reads could answer differently with no write in
1650
+ // between (the revival samples the ambient transition/lane context).
1651
+ // The sweep at flush finalization re-validates and reclaims; schedule()
1652
+ // guarantees that flush happens even if nothing else is queued.
1653
+ dormantNodes.add(e);
1654
+ schedule();
1655
+ }
1656
+ return u;
1657
+ }
1658
+
1659
+ /**
1660
+ * Rule 1, the one slow implementation (DESIGN-CONSOLIDATION move 3b, step
1661
+ * 6c): the value a reader `c` (null = untracked, no pass) is served from
1662
+ * `el`, whose committed value is `committed` — the node's own `_value` for
1663
+ * a signal or memo, the BACKING for a store property node (single-home rule,
1664
+ * O6: committed truth lives in the backing and a node's `_value` is never
1665
+ * served for one). Called by read()'s slow tail and by the store's untracked
1666
+ * node path (nodeValue); the fast paths (readNodeFast, read's fast block)
1667
+ * keep their trivial ternary by design (perf, see the doc). Arms, in order:
1668
+ * - the override (A17), routed through the engine for a tracked reader under
1669
+ * a lane or a supersession (A18), an authoritative reader marked instead;
1670
+ * - the lane entanglement gate (committed, recorded for replay);
1671
+ * - a node born held has nothing for an untracked reader (A19 exception 1);
1672
+ * - an unflushed write serves committed and re-runs the reader in the
1673
+ * carrying flush (A28);
1674
+ * - readerSeesCommitted, else the staged value and the transaction (A29).
1675
+ */ function serve(e, t, n, i) {
1676
+ if (hasActiveOverride(e)) {
1677
+ // A17: the override IS the value for every reader — except an authoritative
1678
+ // reader (until()'s predicate carries CONFIG_AUTHORITATIVE_READ): it must
1679
+ // observe independently-arriving truth, and serving it the caller's own
1680
+ // tentative write would trivially satisfy the predicate. The bit is checked
1681
+ // on the reading computation itself, so a shared computed the predicate
1682
+ // pulls recomputes as ITSELF (context = the memo, no bit) under the normal
1683
+ // view. Fall through to normal value selection (staged `_pendingValue` is
1684
+ // authoritative — optimism never lives there); the sticky mark makes the
1685
+ // A17-silent "landing equals override" paths notify this node's subs so
1686
+ // the reader re-runs when truth arrives.
1687
+ // A28 (5): an optimistic write written this tick, outside a flush, is not
1688
+ // yet the active override — fall through to the normal selection (the
1689
+ // authoritative mark below still applies: until() must wake on landing).
1690
+ if (!(t && t.C & CONFIG_AUTHORITATIVE_READ) && !unflushedOverride(e)) {
1691
+ // A tracked read of an override is the engine's selection (a lane or a
1692
+ // supersession implies the engine): a render effect OFF the override's
1693
+ // held lane sees the committed value (#3460, lanes mirror transitions);
1694
+ // a node whose own source answered with a DIFFERENT value hands a
1695
+ // tracked reader the staged truth (A18 supersession, #3331). Untracked
1696
+ // reads display the override.
1697
+ if (t && e.C & (CONFIG_HAS_LANE | CONFIG_OVERRIDE_SUPERSEDED)) return GlobalQueue.Dn(e, t);
1698
+ return unwrapOverride(e.o?.we);
1699
+ }
1700
+ e.C |= CONFIG_AUTHORITATIVE_OBSERVED;
1701
+ }
1702
+ // Entanglement gate: a reader recomputing under an optimistic lane that reads
1703
+ // a pending mid-transition write sees the committed value. Projection-store
1704
+ // manual writes use the firewall's manual-write flag to opt into this path.
1705
+ // Async drivers are not under an optimistic lane and so bypass this, reading
1706
+ // _pendingValue for correct fetching. The sub is recorded for replay at commit
1707
+ // so it re-runs with the new committed view. (Gate details live with the
1708
+ // engine — a non-null lane implies it is installed.)
1709
+ if (currentOptimisticLane !== null && activeTransition !== null && t !== null && GlobalQueue.Pn(e, n, t)) {
1710
+ return i;
1711
+ }
1712
+ // In optimistic lane context, return _value for optimistic/lane-assigned signals
1713
+ // and for regular signals in stale mode (render effects). Non-stale readers (user
1714
+ // effects) see _pendingValue so that latest() and direct reads stay consistent.
1715
+ // (The lane-context clause lives with the engine.) Children-forbidden readers
1716
+ // (createTrackedEffect / onSettled callbacks) get committed visibility — see
1717
+ // readNodeFast (#3006).
1718
+ // A node born held (recompute) has a staged value and no committed one: a
1719
+ // stale reader cannot fall back to the committed frame, so it takes the
1720
+ // staged value and enters like a tracked reader (A29); an untracked reader
1721
+ // has nothing to serve and holds (A19 exception 1) — a bookkeeping read
1722
+ // (`spectate`) likewise: it derives nothing, so it cannot enter, and a
1723
+ // held-only value is simply not ready to it.
1724
+ const l = e._e !== NOT_PENDING && (e.S & STATUS_UNINITIALIZED) !== 0;
1725
+ if (l && (!t || spectating)) throw new NotReadyError(null);
1726
+ const u = t && unflushedStaged ? unflushedValue(e, i) : NOT_PENDING;
1727
+ if (u !== NOT_PENDING) {
1728
+ markLateLinker(t);
1729
+ if (pendingCheckActive) GlobalQueue.Hn(e, u);
1730
+ return u;
1731
+ }
1732
+ const o = readerSeesCommitted(e, t, n, l) ? i : (enterStagedRead(e), e._e);
1733
+ // Record that this isPending() probe observed the fresh pending value, so
1734
+ // the probe doesn't pair "pending" with the new value (#2831).
1735
+ if (pendingCheckActive) GlobalQueue.Hn(e, o);
1736
+ return o;
1737
+ }
1738
+
1739
+ /** A28 — a rewrite of a HELD node outside a flush keeps the staged value the
1740
+ * last flush left, for latest()/verdicts, until the next flush carries it
1741
+ * (stash, cleared at that flush). Cold: only stamped nodes reach here. */ function stashHeldRewrite(e) {
1742
+ if (globalQueue.$t) return;
1743
+ const t = ext(e);
1744
+ if (t.Ut === NOT_PENDING) {
1745
+ t.Ut = e._e;
1746
+ unflushedRewrites.push(e);
1747
+ unflushedStaged = true;
1748
+ }
1749
+ }
1750
+
1751
+ /** A28 (4) — written inside a recompute that runs OUTSIDE a flush (a
1752
+ * creation-time compute): promoted at that pass's end, visible to the rest of
1753
+ * the block. Cold: only contextual writes reach here. */ function notePromotedWrite(e) {
1754
+ if (globalQueue.$t || e.C & CONFIG_PROMOTED) return;
1755
+ e.C |= CONFIG_PROMOTED;
1756
+ promotedWrites.push(e);
1757
+ }
1758
+
1759
+ /** Cold half of setSignal's snapshot arm (see there): first write during
1760
+ * capture to a plain signal without a live snapshot records the pre-write
1761
+ * value. Only plain user signals qualify. Computeds reach setSignal from an
1762
+ * async landing (asyncWrite), and a value arriving from async during the pass
1763
+ * REVEALS — the creation-time arm skips pending computeds for the same
1764
+ * reason. Firewall leaves belong to a projection whose compute is the tree's
1765
+ * own work, captured (or deliberately not) at creation. */ function captureWriteSnapshot(e, t) {
1766
+ if (e.C & CONFIG_NO_SNAPSHOT || e.Se !== undefined || e.Ne || e.o?.ct !== undefined) return;
1767
+ ext(e).ct = t === undefined ? NO_SNAPSHOT : t;
1768
+ e.C |= CONFIG_HAS_SNAPSHOT;
1769
+ snapshotSources.add(e);
1770
+ }
1771
+
1772
+ function setSignal(e, t) {
1773
+ // A write to a held node is a second proposal on a contested node (A34, #3494):
1774
+ // the writer's tick reveals with the hold, the same value or another. Inside
1775
+ // a flush the round enters now; from mainline the entry waits for the next
1776
+ // flush's start (batchJoins) — entering here left activeTransition set for
1777
+ // the rest of the caller's block, so a memo created after the write became
1778
+ // the transaction's instead of mainline's (A28, A29). Before the equality
1779
+ // gate below: repeating the held value proposes it too — and that repeat
1780
+ // leaves through the gate, so the join schedules its own flush here; left
1781
+ // for the next flush to find, it adopted an unrelated tick (#3519 review).
1782
+ // A held DERIVATION is not a proposal (A34 amendment, #3612): the user
1783
+ // setters of derived nodes (setMemo, the derived store's setter) classify
1784
+ // the write before it reaches here — see heldDerivation — and re-derive
1785
+ // under the hold instead of masking; the join stands either way.
1786
+ if (e.me && activeTransition !== e.me) {
1787
+ if (globalQueue.$t) globalQueue.initTransition(e.me); else {
1788
+ batchJoins.push(e.me);
1789
+ // dupes: a bare return in initTransition
1790
+ schedule();
1791
+ }
1792
+ }
1793
+ // The optimistic write path lives with the engine: only optimisticSignal /
1794
+ // optimisticComputed callers and optimistic store nodes carry an
1795
+ // _overrideValue slot (flagged by CONFIG_OPTIMISTIC — a masked read of the
1796
+ // always-present config instead of a missing-property probe), and every
1797
+ // module that installs one installs the engine first.
1798
+ if (e.C & CONFIG_OPTIMISTIC) {
1799
+ if (!projectionWriteActive) return GlobalQueue.Ln(e, t);
1800
+ // An authoritative store landing on an override-covered node: the store
1801
+ // twin of asyncWrite's override branch, decided by the engine (#3331).
1802
+ const n = e.o?.we;
1803
+ if (n !== undefined && n !== NOT_PENDING) return GlobalQueue.Vn(e, t);
1804
+ }
1805
+ const n = e._e === NOT_PENDING ? e.ce : e._e;
1806
+ if (typeof t === "function") t = t(n);
1807
+ // Uninitialized check first: the first commit has no previous value, so the
1808
+ // user comparator must not run against `undefined` (matches recompute).
1809
+ const i = !!(e.S & STATUS_UNINITIALIZED) || !e.Fe || !e.Fe(n, t);
1810
+ if (!i) return t;
1811
+ // Attribution hook: this committed write is where a re-run chain begins.
1812
+ if (attrHooks !== null) attrHooks.write(e, n, t);
1813
+ // A write during hydration's snapshot capture to a source that has no
1814
+ // snapshot — created BEFORE capture began (module-level state: an identity
1815
+ // minted from onSettled in the pass, a preference read from storage) —
1816
+ // captures the pre-write value now, so the write is held like any other:
1817
+ // in-scope readers keep serving what the server rendered with and replay
1818
+ // at release. Left uncaptured, the write cascades live through a claim
1819
+ // pass whose DOM writes are skipped, and a component rendered later in the
1820
+ // pass reads a value the server never had. Store leaves written here are
1821
+ // plain signals and qualify the same way.
1822
+ if (snapshotCaptureActive) captureWriteSnapshot(e, n);
1823
+ const l = e._e !== NOT_PENDING;
1824
+ if (!l) queuePendingNode(e);
1825
+ // A28 arms, gated on the loads the write already pays for (a plain ambient
1826
+ // rewrite outside a flush — the write-loop shape — costs `_transition` and
1827
+ // `context` here and nothing else; the arms themselves are cold helpers so
1828
+ // setSignal stays within every setter's inlining budget, ~300 B bytecode).
1829
+ else if (e.me !== null) stashHeldRewrite(e);
1830
+ e._e = t;
1831
+ if (context !== null) notePromotedWrite(e);
1832
+ // syncCompanions only pokes _pendingSignal/_latestValueComputed — with
1833
+ // neither companion present the call is a guaranteed no-op (companions are
1834
+ // only ever created, never removed, and creating one installs the hook and
1835
+ // sets CONFIG_HAS_COMPANIONS — one masked read replaces two optional-field
1836
+ // probes on every write).
1837
+ if (e.C & CONFIG_HAS_COMPANIONS && GlobalQueue.Le !== null) {
1838
+ GlobalQueue.Le(e, t);
1839
+ // A28 (2): the verdict computed here is the PRE-flush one (an unflushed
1840
+ // write is not pending); the flush that carries the write re-syncs so
1841
+ // the companions mirror the flushed world. Only companion nodes pay.
1842
+ if (!globalQueue.$t) unflushedCompanions.push(e);
1843
+ }
1844
+ // _time is a computed-only slot (§12e): writing it on a signal would fork
1845
+ // the lean shape. Every read site is computed-typed.
1846
+ if (e.Se !== undefined) e.Ge = clock;
1847
+ // Staged-rewrite fast path (§12d): a re-write to a node whose subscribers
1848
+ // were already walked — and where nothing has recomputed or linked since
1849
+ // (epoch) — re-stages the value and stops. The walk is idempotent (subs
1850
+ // marked, heap entries flag-guarded, effects queued once); lane and reask
1851
+ // contexts change what a walk MEANS, so they always walk.
1852
+ if (l && e.Lt === notifyEpoch && currentOptimisticLane === null && !reaskArmed) return t;
1853
+ insertSubs(e);
1854
+ schedule();
1855
+ return t;
1856
+ }
1857
+
1858
+ /**
1859
+ * Suppresses automatic recomputation of `el` until the scheduler drains. Used
1860
+ * when a manual write should win over dependency changes queued in the same
1861
+ * tick. The MANUAL_WRITE flag is cleared by the pending-node drain; projection
1862
+ * computeds don't commit values, but they still need the same end-of-tick
1863
+ * cleanup point.
1864
+ */ function suppressComputedRecompute(e) {
1865
+ deleteFromHeap(e, queueFor(e));
1866
+ if (!(e.oe & REACTIVE_MANUAL_WRITE) && e._e === NOT_PENDING) {
1867
+ queuePendingNode(e);
1868
+ schedule();
1869
+ }
1870
+ e.oe = e.oe & -4 | REACTIVE_MANUAL_WRITE;
1871
+ e.Un = clock;
1872
+ }
1873
+
1874
+ /** A34 amendment (#3612) — is `el`'s staging a DERIVATION another transaction
1875
+ * holds: stamped by a transaction that is not the writer's, and not a manual
1876
+ * proposal (the mask, on the node or — a store leaf — its firewall)? #2692's
1877
+ * "manual write wins" is a rule for one synchronous frame; across a hold the
1878
+ * held pass result is nobody's proposal, and a user setter reaching it
1879
+ * composes on the committed frame it was written against and becomes `prev`
1880
+ * for the transaction's re-derivation (rederiveHeld) instead of replacing it.
1881
+ * Writes made under the holding transaction masked the node and keep
1882
+ * A34(1)'s last-write-wins. Only the user setters ask; an async landing or a
1883
+ * companion writing a stamped node is the transaction's own work. */ function heldDerivation(e) {
1884
+ return e.me !== null && activeTransition !== e.me && !((e.Ne || e).oe & REACTIVE_MANUAL_WRITE);
1885
+ }
1886
+
1887
+ /** The held-derivation write's second half: the node re-derives under its
1888
+ * hold with the written staging as the pass's `prev`. Nothing is masked. The
1889
+ * write took the A34 join (setSignal), which scheduled the flush that drains
1890
+ * this; recompute re-enters the stamp. */ function rederiveHeld(e) {
1891
+ e.oe |= REACTIVE_DIRTY;
1892
+ // over CHECK: the heap visit recomputes, not re-checks
1893
+ enqueueSub(e);
1894
+ }
1895
+
1896
+ /**
1897
+ * User-facing setter for the memo form of `createSignal(fn)`. Behaves like
1898
+ * `setSignal`, but also cancels any pending recompute of the memo so the
1899
+ * manual value wins over a value that would otherwise be produced by an
1900
+ * upstream change in the same tick. Across a hold the write is not a
1901
+ * proposal: a memo another transaction holds as a pass result composes on the
1902
+ * committed value and re-derives under the hold with the write as `prev`
1903
+ * (A34 amendment, #3612; heldDerivation).
1904
+ */ function setMemo(e, t) {
1905
+ const n = heldDerivation(e);
1906
+ if (n && typeof t === "function") t = t(e.ce);
1907
+ const i = setSignal(e, t);
1908
+ n ? rederiveHeld(e) : suppressComputedRecompute(e);
1909
+ return i;
1910
+ }
1911
+
1912
+ /**
1913
+ * Executes `fn` with the given `owner` set as the current owner. Any reactive
1914
+ * primitives (`createSignal`, `createMemo`, `createEffect`, `onCleanup`,
1915
+ * `cleanup`, etc.) created inside `fn` are attached to that owner, so they
1916
+ * are disposed when the owner is disposed.
1917
+ *
1918
+ * The classic pattern: capture the current owner with `getOwner()` inside a
1919
+ * component, then re-enter it from a callback (event handler, async resolve,
1920
+ * setTimeout) so disposables created in the callback get cleaned up with the
1921
+ * component.
1922
+ *
1923
+ * @example
1924
+ * ```ts
1925
+ * function delayed<T>(ms: number, fn: () => T) {
1926
+ * const owner = getOwner();
1927
+ * setTimeout(() => runWithOwner(owner, fn), ms);
1928
+ * }
1929
+ * ```
1930
+ */ function runWithOwner(e, t) {
1931
+ const n = context;
1932
+ const i = tracking;
1933
+ context = e;
1934
+ tracking = false;
1935
+ try {
1936
+ return t();
1937
+ } finally {
1938
+ context = n;
1939
+ tracking = i;
1940
+ }
1941
+ }
1942
+
1943
+ function staleValues(e, t = true) {
1944
+ const n = stale;
1945
+ stale = t;
1946
+ try {
1947
+ return e();
1948
+ } finally {
1949
+ stale = n;
1950
+ }
1951
+ }
1952
+
1953
+ /**
1954
+ * Core marking half of `refresh()` (the public wrapper lives in signals.ts —
1955
+ * it validates the target, marks through here, then builds the quiescence
1956
+ * promise on the resolve()/until() effect machinery). Flags the node's next
1957
+ * recompute as a quiet re-ask and schedules it; no-ops for non-derived or
1958
+ * disposed targets and for same-tick manual writes.
1959
+ */ function markRefresh(e) {
1960
+ if (typeof e.Se === "function" && !(e.oe & REACTIVE_DISPOSED)) {
1961
+ if (e.oe & REACTIVE_MANUAL_WRITE) {
1962
+ // A manual write in the CURRENT tick wins over the refresh (#2692).
1963
+ // A mask stamped in an earlier tick only survives because a
1964
+ // transaction (action) is holding the pending drain open; there the
1965
+ // refresh is a later, explicit re-ask and lifts the mask — otherwise
1966
+ // any setStore early in an action silently swallows every refresh()
1967
+ // for the rest of the transaction (#3026).
1968
+ if (e.Un === clock) return;
1969
+ e.oe &= ~REACTIVE_MANUAL_WRITE;
1970
+ // The lift falls through to the re-ask classification below. The held
1971
+ // write's value change already rides the transaction; the refetch it
1972
+ // asks for is the same question with unchanged inputs. Skipping the
1973
+ // mark here classified that refetch as a NEW question, which pends
1974
+ // every leaf (3.1) — an action doing setStore + yield + refresh(store)
1975
+ // lit up every sibling row, and affects() could not narrow it (a mark
1976
+ // only turns pending on). Same-question motion stays silent (3.4);
1977
+ // the written slot and any declared mark carry the pending instead.
1978
+ }
1979
+ // A refresh with no value-change dirt already queued is a re-ask of the
1980
+ // same question: mark it so the recompute classifies any resulting
1981
+ // pending window as quiet (not pending). If the node is already dirty
1982
+ // from a real input change, the question changed — don't mark.
1983
+ // REACTIVE_IN_HEAP counts as dirt: insertSubs schedules subscribers by
1984
+ // heap insertion alone (no DIRTY/CHECK flag), so a same-batch value
1985
+ // change followed by refresh() must not be laundered into a quiet re-ask.
1986
+ if (!(e.oe & (REACTIVE_DIRTY | REACTIVE_CHECK | REACTIVE_IN_HEAP))) {
1987
+ e.oe |= REACTIVE_REASK;
1988
+ armReaskClear();
1989
+ }
1990
+ e.oe = e.oe & ~REACTIVE_CHECK | REACTIVE_DIRTY;
1991
+ // A refresh() self-invalidation is a root cause too — the target's next
1992
+ // run has no changed dep to point at, so it points here instead.
1993
+ if (attrHooks !== null) attrHooks.refreshed(e);
1994
+ insertIntoHeap(e, queueFor(e));
1995
+ schedule();
1996
+ }
1997
+ }
1998
+
1999
+ export { READ_SLOW, clearSnapshots, computed, context, createEffectNode, currentOptimisticLane, effectStatusNotify, enterStagedRead, ext, hasActiveOverride, heldDerivation, installAuthoritativeRead, isEqual, latestReadActive, markLateLinker, markRefresh, markSnapshotScope, markUnflushedStaged, notifyAuthoritativeObservers, notifyOnLane, optimisticComputed, optimisticSignal, ownsHold, pendingCheckActive, prepareComputed, read, readNodeFast, readerSeesCommitted, recompute, recordStaleReplay, rederiveHeld, releaseSnapshotScope, resyncUnflushedCompanions, runWithOwner, serve, setContextInternal, setEffectStatusNotify, setLatestReadActive, setMemo, setPendingCheckActive, setSignal, setSlotUnobserved, setSnapshotCapture, signal, slotSignal, slotUnobservedHook, snapshotCaptureActive, snapshotSources, spectate, spectating, stale, staleValues, statusNotifierOf, suppressComputedRecompute, tracking, unflushed, unflushedCompanions, unflushedOverride, unflushedStaged, unflushedValue, unlinkFirewallChild, untrack, visibleOverride };