@solidjs/signals 2.0.0-rc.2 → 2.0.0-rc.20

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 (198) hide show
  1. package/dist/dev-shared.js +6505 -0
  2. package/dist/dev.attribution.js +3484 -0
  3. package/dist/dev.js +10227 -9508
  4. package/dist/observe/affects-marks.js +86 -0
  5. package/dist/observe/affects.js +112 -0
  6. package/dist/observe/attribution.js +7 -0
  7. package/dist/observe/boundaries.js +696 -0
  8. package/dist/observe/core/action.js +182 -0
  9. package/dist/observe/core/async.js +824 -0
  10. package/dist/observe/core/attribution-costs.js +66 -0
  11. package/dist/observe/core/attribution-feedback.js +280 -0
  12. package/dist/observe/core/attribution-hooks.js +83 -0
  13. package/dist/observe/core/attribution-queries.js +43 -0
  14. package/dist/observe/core/attribution.js +3079 -0
  15. package/dist/observe/core/constants.js +269 -0
  16. package/dist/observe/core/context.js +63 -0
  17. package/dist/observe/core/copies.js +10 -0
  18. package/dist/observe/core/core.js +1707 -0
  19. package/dist/observe/core/dev.js +394 -0
  20. package/dist/observe/core/effect.js +193 -0
  21. package/dist/observe/core/error-hooks.js +71 -0
  22. package/dist/observe/core/error.js +82 -0
  23. package/dist/observe/core/external.js +97 -0
  24. package/dist/observe/core/graph.js +162 -0
  25. package/dist/observe/core/heap.js +124 -0
  26. package/dist/observe/core/identities.js +62 -0
  27. package/dist/observe/core/invariants.js +12 -0
  28. package/dist/observe/core/lanes.js +972 -0
  29. package/dist/observe/core/owner.js +371 -0
  30. package/dist/observe/core/scheduler.js +1157 -0
  31. package/dist/observe/core/truth.js +47 -0
  32. package/dist/observe/core/verdict.js +470 -0
  33. package/dist/observe/deferred.js +186 -0
  34. package/dist/observe/flatten.js +79 -0
  35. package/dist/observe/from.js +248 -0
  36. package/dist/observe/index.js +71 -0
  37. package/dist/observe/map.js +593 -0
  38. package/dist/observe/reveal.js +212 -0
  39. package/dist/observe/signals.js +717 -0
  40. package/dist/observe/store/affects.js +142 -0
  41. package/dist/observe/store/edges.js +39 -0
  42. package/dist/observe/store/hooks.js +16 -0
  43. package/dist/observe/store/index.js +47 -0
  44. package/dist/observe/store/optimistic.js +713 -0
  45. package/dist/observe/store/projection.js +345 -0
  46. package/dist/observe/store/reconcile.js +281 -0
  47. package/dist/observe/store/store.js +2127 -0
  48. package/dist/observe/store/storePath.js +103 -0
  49. package/dist/observe/store/target.js +46 -0
  50. package/dist/observe/store/types.js +83 -0
  51. package/dist/observe/store/utils.js +1063 -0
  52. package/dist/prod/affects-marks.js +86 -0
  53. package/dist/prod/affects.js +98 -112
  54. package/dist/prod/attribution.js +45 -0
  55. package/dist/prod/boundaries.js +578 -468
  56. package/dist/prod/core/action.js +92 -59
  57. package/dist/prod/core/async.js +522 -336
  58. package/dist/prod/core/attribution-hooks.js +3 -0
  59. package/dist/prod/core/constants.js +168 -44
  60. package/dist/prod/core/context.js +12 -16
  61. package/dist/prod/core/copies.js +10 -0
  62. package/dist/prod/core/core.js +1345 -627
  63. package/dist/prod/core/dev.js +123 -1
  64. package/dist/prod/core/effect.js +113 -80
  65. package/dist/prod/core/error-hooks.js +71 -0
  66. package/dist/prod/core/error.js +18 -4
  67. package/dist/prod/core/external.js +6 -7
  68. package/dist/prod/core/graph.js +119 -72
  69. package/dist/prod/core/heap.js +85 -109
  70. package/dist/prod/core/identities.js +62 -0
  71. package/dist/prod/core/invariants.js +1 -32
  72. package/dist/prod/core/lanes.js +931 -132
  73. package/dist/prod/core/owner.js +192 -129
  74. package/dist/prod/core/scheduler.js +948 -642
  75. package/dist/prod/core/truth.js +47 -0
  76. package/dist/prod/core/verdict.js +409 -420
  77. package/dist/prod/deferred.js +184 -0
  78. package/dist/prod/flatten.js +77 -0
  79. package/dist/prod/from.js +248 -0
  80. package/dist/prod/index.js +40 -16
  81. package/dist/prod/map.js +481 -211
  82. package/dist/prod/reveal.js +212 -0
  83. package/dist/prod/signals.js +401 -84
  84. package/dist/prod/store/affects.js +142 -0
  85. package/dist/prod/store/edges.js +39 -0
  86. package/dist/prod/store/hooks.js +16 -0
  87. package/dist/prod/store/index.js +27 -18
  88. package/dist/prod/store/optimistic.js +707 -0
  89. package/dist/prod/store/projection.js +331 -0
  90. package/dist/prod/store/reconcile.js +281 -0
  91. package/dist/prod/store/store.js +1952 -267
  92. package/dist/prod/store/storePath.js +1 -1
  93. package/dist/prod/store/target.js +43 -0
  94. package/dist/prod/store/types.js +83 -0
  95. package/dist/prod/store/utils.js +997 -138
  96. package/dist/types/affects-marks.d.ts +14 -0
  97. package/dist/types/affects.d.ts +12 -30
  98. package/dist/types/attribution.d.ts +19 -0
  99. package/dist/types/attribution.prod.d.ts +22 -0
  100. package/dist/types/boundaries.d.ts +99 -124
  101. package/dist/types/carved.d.ts +1 -0
  102. package/dist/types/core/action.d.ts +23 -9
  103. package/dist/types/core/async.d.ts +34 -6
  104. package/dist/types/core/attribution-costs.d.ts +35 -0
  105. package/dist/types/core/attribution-feedback.d.ts +133 -0
  106. package/dist/types/core/attribution-hooks.d.ts +307 -8
  107. package/dist/types/core/attribution-queries.d.ts +24 -0
  108. package/dist/types/core/attribution.d.ts +802 -72
  109. package/dist/types/core/constants.d.ts +195 -38
  110. package/dist/types/core/copies.d.ts +54 -0
  111. package/dist/types/core/core.d.ts +295 -65
  112. package/dist/types/core/dev.d.ts +509 -22
  113. package/dist/types/core/effect.d.ts +1 -1
  114. package/dist/types/core/error-hooks.d.ts +71 -0
  115. package/dist/types/core/error.d.ts +18 -1
  116. package/dist/types/core/graph.d.ts +40 -1
  117. package/dist/types/core/heap.d.ts +5 -5
  118. package/dist/types/core/identities.d.ts +17 -0
  119. package/dist/types/core/index.d.ts +11 -7
  120. package/dist/types/core/invariants.d.ts +1 -26
  121. package/dist/types/core/lanes.d.ts +74 -43
  122. package/dist/types/core/owner.d.ts +27 -9
  123. package/dist/types/core/scheduler.d.ts +353 -102
  124. package/dist/types/core/truth.d.ts +18 -0
  125. package/dist/types/core/types.d.ts +97 -71
  126. package/dist/types/core/verdict.d.ts +35 -0
  127. package/dist/types/deferred.d.ts +28 -0
  128. package/dist/types/flatten.d.ts +8 -0
  129. package/dist/types/from.d.ts +47 -0
  130. package/dist/types/index.d.ts +33 -7
  131. package/dist/types/map.d.ts +4 -2
  132. package/dist/types/reveal.d.ts +19 -0
  133. package/dist/types/signals.d.ts +196 -45
  134. package/dist/types/store/affects.d.ts +12 -0
  135. package/dist/types/store/edges.d.ts +9 -0
  136. package/dist/types/store/hooks.d.ts +62 -0
  137. package/dist/types/store/index.d.ts +42 -15
  138. package/dist/types/store/optimistic.d.ts +24 -45
  139. package/dist/types/store/projection.d.ts +17 -68
  140. package/dist/types/store/reconcile.d.ts +3 -46
  141. package/dist/types/store/store.d.ts +133 -136
  142. package/dist/types/store/storePath.d.ts +1 -1
  143. package/dist/types/store/target.d.ts +166 -0
  144. package/dist/types/store/types.d.ts +105 -0
  145. package/dist/types/store/utils.d.ts +188 -0
  146. package/package.json +31 -26
  147. package/dist/node.cjs +0 -8745
  148. package/dist/prod/core/optimistic.js +0 -265
  149. package/dist/prod/store/next/optimistic.js +0 -385
  150. package/dist/prod/store/next/projection.js +0 -172
  151. package/dist/prod/store/next/reconcile.js +0 -331
  152. package/dist/prod/store/next/store.js +0 -1499
  153. package/dist/prod/store/next/target.js +0 -20
  154. package/dist/types/core/optimistic.d.ts +0 -6
  155. package/dist/types/store/next/optimistic.d.ts +0 -20
  156. package/dist/types/store/next/projection.d.ts +0 -8
  157. package/dist/types/store/next/reconcile.d.ts +0 -3
  158. package/dist/types/store/next/store.d.ts +0 -77
  159. package/dist/types/store/next/target.d.ts +0 -117
  160. package/dist/types-cjs/affects.d.cts +0 -47
  161. package/dist/types-cjs/boundaries.d.cts +0 -175
  162. package/dist/types-cjs/core/action.d.cts +0 -64
  163. package/dist/types-cjs/core/async.d.cts +0 -21
  164. package/dist/types-cjs/core/attribution-hooks.d.cts +0 -52
  165. package/dist/types-cjs/core/attribution.d.cts +0 -186
  166. package/dist/types-cjs/core/constants.d.cts +0 -97
  167. package/dist/types-cjs/core/context.d.cts +0 -36
  168. package/dist/types-cjs/core/core.d.cts +0 -163
  169. package/dist/types-cjs/core/dev.d.cts +0 -81
  170. package/dist/types-cjs/core/effect.d.cts +0 -30
  171. package/dist/types-cjs/core/error.d.cts +0 -47
  172. package/dist/types-cjs/core/external.d.cts +0 -15
  173. package/dist/types-cjs/core/graph.d.cts +0 -6
  174. package/dist/types-cjs/core/heap.d.cts +0 -22
  175. package/dist/types-cjs/core/index.d.cts +0 -13
  176. package/dist/types-cjs/core/invariants.d.cts +0 -59
  177. package/dist/types-cjs/core/lanes.d.cts +0 -44
  178. package/dist/types-cjs/core/optimistic.d.cts +0 -6
  179. package/dist/types-cjs/core/owner.d.cts +0 -124
  180. package/dist/types-cjs/core/scheduler.d.cts +0 -180
  181. package/dist/types-cjs/core/types.d.cts +0 -192
  182. package/dist/types-cjs/core/verdict.d.cts +0 -2
  183. package/dist/types-cjs/index.d.cts +0 -10
  184. package/dist/types-cjs/map.d.cts +0 -69
  185. package/dist/types-cjs/package.json +0 -3
  186. package/dist/types-cjs/signals.d.cts +0 -624
  187. package/dist/types-cjs/store/index.d.cts +0 -19
  188. package/dist/types-cjs/store/next/optimistic.d.cts +0 -20
  189. package/dist/types-cjs/store/next/projection.d.cts +0 -8
  190. package/dist/types-cjs/store/next/reconcile.d.cts +0 -3
  191. package/dist/types-cjs/store/next/store.d.cts +0 -77
  192. package/dist/types-cjs/store/next/target.d.cts +0 -117
  193. package/dist/types-cjs/store/optimistic.d.cts +0 -45
  194. package/dist/types-cjs/store/projection.d.cts +0 -70
  195. package/dist/types-cjs/store/reconcile.d.cts +0 -46
  196. package/dist/types-cjs/store/store.d.cts +0 -138
  197. package/dist/types-cjs/store/storePath.d.cts +0 -58
  198. package/dist/types-cjs/store/utils.d.cts +0 -74
@@ -1,104 +1,139 @@
1
- import { handleAsync, clearStatus, parkLoadingWindow, notifyStatus, settleErroredDependents } from "./async.js";
1
+ import { notifyStatus, releaseFlightTeardown, handleAsync, clearStatus, parkLoadingWindow, stageError, settlePendingSource, releaseIfSettledUnobserved, settleErroredDependents } from "./async.js";
2
2
 
3
- import { EFFECT_TRACKED, REACTIVE_OPTIMISTIC_DIRTY, CONFIG_OPTIMISTIC, NOT_PENDING, STATUS_UNINITIALIZED, STATUS_ERROR, REACTIVE_REASK, REACTIVE_RECOMPUTING_DEPS, CONFIG_SYNC, CONFIG_HAS_LANE, STATUS_PENDING, REACTIVE_MISSED_WAKE, REACTIVE_NONE, REACTIVE_SNAPSHOT_STALE, unwrapOverride, OVERRIDE_UNDEFINED, CONFIG_HAS_COMPANIONS, REACTIVE_LAZY, REACTIVE_DISPOSED, CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, REACTIVE_CHECK, REACTIVE_DIRTY, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, defaultContext, CONFIG_IN_SNAPSHOT_SCOPE, CONFIG_TRANSPARENT, CONFIG_OWNED_WRITE, CONFIG_NO_SNAPSHOT, $REFRESH, REACTIVE_MANUAL_WRITE, CONFIG_FW_CHILDREN, NO_SNAPSHOT, CONFIG_HAS_SNAPSHOT, STORE_SNAPSHOT_PROPS, EFFECT_USER } from "./constants.js";
3
+ import { STATUS_UNINITIALIZED, REACTIVE_LAZY, REACTIVE_NONE, defaultContext, CONFIG_TRANSPARENT, CONFIG_OWNED_WRITE, CONFIG_AUTO_DISPOSE, CONFIG_SYNC, CONFIG_NO_SNAPSHOT, CONFIG_IN_SNAPSHOT_SCOPE, NOT_PENDING, CONFIG_HELD, REACTIVE_MANUAL_WRITE, STATUS_ERROR, REACTIVE_DIRTY, CONFIG_OVERRIDE, NO_SNAPSHOT, CONFIG_HAS_SNAPSHOT, REACTIVE_DISPOSED, REACTIVE_CHECK, REACTIVE_IN_HEAP, REACTIVE_REASK, REACTIVE_SNAPSHOT_STALE, CONFIG_CHILDREN_FORBIDDEN, CONFIG_VERDICT, STATUS_PENDING, CONFIG_STAGED_ERROR, REACTIVE_LANE_DIRTY, CONFIG_GUESS, REACTIVE_RECOMPUTING_DEPS, CONFIG_ASYNC, CONFIG_LAGS, EFFECT_RENDER, CONFIG_HELD_FRAME, EFFECT_TRACKED, CONFIG_STAGED, REACTIVE_ZOMBIE, REACTIVE_MISSED_WAKE, REACTIVE_JOINED, REACTIVE_STAGED_READ, REACTIVE_PROBED, REACTIVE_FRAME_READ, REACTIVE_LANE_READ, REACTIVE_SCREEN_READ, REACTIVE_PROBE_UNANSWERED, CAUGHT_REARMED, CONFIG_PROMOTED, CONFIG_INPUTS_PUBLISHED, CONFIG_FRESH_READ, REACTIVE_IN_HEAP_HEIGHT, CONFIG_SLOT_NODE, EFFECT_USER, NewSet, STATUS_NONE } from "./constants.js";
4
4
 
5
5
  import { NotReadyError } from "./error.js";
6
6
 
7
- import { trimStaleDeps, link, unobserved } from "./graph.js";
7
+ import { link, dormantNodes, clearDeps, trimStaleDeps, deferReleases, deferredReleases } from "./graph.js";
8
8
 
9
- import { deleteFromHeap, queueFor, insertIntoHeapHeight, enqueueSub, markNode, markHeap, insertIntoHeap } from "./heap.js";
9
+ import { insertIntoHeap, deleteFromHeap, insertIntoHeapHeight, enqueueSub, markNode, markHeap } from "./heap.js";
10
10
 
11
- import { GlobalQueue, activeTransition, globalQueue, clock, insertSubs, queuePendingNode, runInTransition, schedule, notifyEpoch, dirtyQueue, bumpNotifyEpoch, projectionWriteActive, reaskArmed, armReaskClear } from "./scheduler.js";
11
+ import { inheritId, linkChild, disposeChildren, markDisposal } from "./owner.js";
12
12
 
13
- import "./invariants.js";
13
+ import { schedule, externalUntrackHook, clock, txOf, flushTransaction, laneWriteHook, joinFuture, queuePendingNode, globalQueue, notifyEpoch, insertSubs, dirtyQueue, observeFlightHook, laneStageHook, passLane, laneReadHook, wireExternalSourceHook, bumpNotifyEpoch, setPassLane, joinPassTx, freshHook, guessing, holdWorkHook, GlobalQueue, laneOutcomeHook, passTx, holdNode, laneDirty, heldTrim, staleReader, laneServedHook, latestOpened, stagedReaders, laneSeamsHook, deferZombie } from "./scheduler.js";
14
14
 
15
- import { disposeChildren, markDisposal, inheritId } from "./owner.js";
16
-
17
- GlobalQueue.Ce = recompute;
18
-
19
- GlobalQueue.Fe = disposeChildren;
20
-
21
- let tracking = false;
22
-
23
- /** @internal verdict-module glue */ function setPendingCheckActive(e) {
24
- pendingCheckActive = e;
25
- }
26
-
27
- /** @internal verdict-module glue */ function setLatestReadActive(e) {
28
- latestReadActive = e;
29
- }
15
+ // The heap's per-node step. A tracked effect's heap visit is its compute
16
+ // phase — empty, like a user effect whose compute reads nothing — and hands
17
+ // the callback to the user queue. Routing the wake through the heap, rather
18
+ // than straight into the queue at notify time, is what orders the run after
19
+ // the commit regardless of which phase the write came from: a write in a
20
+ // render-effect callback stages its value for the next pass, but a wake pushed
21
+ // directly into the user queue ran in the SAME pass, read the old value, and
22
+ // nothing re-notified it when the value landed (#3291).
23
+ GlobalQueue._e = e => {
24
+ if (e.i === EFFECT_TRACKED) {
25
+ deleteFromHeap(e, dirtyQueue);
26
+ e.Ae = true;
27
+ globalQueue.enqueue(EFFECT_USER, e.Z);
28
+ }
29
+ // L2: a zombie's fate is the seam's — its owner's commit disposes it, or
30
+ // the park cancels the pass (the write that dirtied it is held). It runs
31
+ // only past a seam that left it alive (a live write to a frame whose
32
+ // owner is held in the future). Its lane's own re-staging is a live write
33
+ // (REACTIVE_LANE_DIRTY — a lane shows ahead of any park): it runs now, as
34
+ // the lane's work, for the reveal this seam (#3463).
35
+ else if (e.R & REACTIVE_ZOMBIE && !(e.R & REACTIVE_LANE_DIRTY)) deferZombie(e);
36
+ // A lane frame's member (parked by a lane pass, #3698) follows mainline
37
+ // writes only: a lane's dirty on it is cancelled — the lane's apply retires
38
+ // it, and the pass that replaced it already decided what that screen shows
39
+ // (#4137: a `<Show>` branch withdrawn by its own lane re-ran on a falsy
40
+ // condition).
41
+ // (Lanes exist only with the lane engine installed: folded away without.)
42
+ else if (laneStageHook && e.R & REACTIVE_ZOMBIE && laneFrameMember(e)) {
43
+ deleteFromHeap(e, dirtyQueue);
44
+ e.R &= -65540;
45
+ } else recompute(e);
46
+ };
30
47
 
31
- /** @internal verdict-module glue */ function setContextInternal(e) {
32
- context = e;
48
+ /** The zombie's frame was parked by lane work: the nearest non-zombie owner
49
+ * (the one whose pass parked it) carries a lane's derived value — or, an
50
+ * effect (no lane value: `laneStage`), is listed in a lane (an `insert()`
51
+ * hole's render effect). */ function laneFrameMember(e) {
52
+ let t = e._parent;
53
+ while (t.R & REACTIVE_ZOMBIE) t = t._parent;
54
+ return (t.l & (CONFIG_OVERRIDE | CONFIG_GUESS)) === CONFIG_OVERRIDE || t.i && t.T?.Re?.P;
33
55
  }
34
56
 
35
- let stale = false;
36
-
37
- let pendingCheckActive = false;
57
+ GlobalQueue.Ce = disposeChildren;
38
58
 
39
- let latestReadActive = false;
59
+ let tracking = false;
40
60
 
41
61
  let context = null;
42
62
 
43
- let currentOptimisticLane = null;
44
-
45
63
  let snapshotCaptureActive = false;
46
64
 
65
+ /** The lane at the root of the running chain of first passes: the lane whose
66
+ * pass created the chain's first node (a mount), inherited by every first
67
+ * pass nested under it, whose seats are mainline (`recompute`, #4166). */ let laneRoot;
68
+
47
69
  let snapshotSources = null;
48
70
 
49
71
  function ownerInSnapshotScope(e) {
50
72
  while (e) {
51
- if (e.He) return true;
52
- e = e.ve;
73
+ if (e.Oe) return true;
74
+ e = e._parent;
53
75
  }
54
76
  return false;
55
77
  }
56
78
 
57
79
  function setSnapshotCapture(e) {
58
80
  snapshotCaptureActive = e;
59
- if (e && !snapshotSources) snapshotSources = new Set;
81
+ if (e && !snapshotSources) snapshotSources = new NewSet;
60
82
  }
61
83
 
62
- function markSnapshotScope(e) {
63
- e.He = true;
84
+ /**
85
+ * Marks `owner` a snapshot scope: while capture is on, the readers created
86
+ * under it serve each source's snapshot (the value it had when hydration
87
+ * began) and replay at `releaseSnapshotScope`.
88
+ *
89
+ * With `served` (hydration), `owner` is instead a hydrated computed and
90
+ * `served` its server answer — what the server rendered every reader of the
91
+ * node with — landing after the node was created pending (so the creation
92
+ * arm skipped it). It becomes the node's snapshot when it has none, whether
93
+ * the node committed the answer or a client write had already taken its
94
+ * compute over. A claim made later reads the value its markup shows, and
95
+ * replays the node's own value at its release, as for a written signal.
96
+ */ function markSnapshotScope(e, t) {
97
+ if (arguments.length < 2) e.Oe = true; else if (snapshotCaptureActive && e.T?.De === undefined) takeSnapshot(e, t);
64
98
  }
65
99
 
66
100
  function releaseSnapshotScope(e) {
67
- e.He = false;
101
+ e.Oe = false;
68
102
  releaseSubtree(e);
69
103
  schedule();
70
104
  }
71
105
 
72
106
  function releaseSubtree(e) {
73
- let t = e.ke;
107
+ let t = e.pe;
74
108
  while (t) {
75
- if (t.He) {
76
- t = t.Ve;
109
+ if (t.Oe) {
110
+ t = t.Ge;
77
111
  continue;
78
112
  }
79
- if (t.Se) {
113
+ if (t.X) {
80
114
  const e = t;
81
- e.T &= ~CONFIG_IN_SNAPSHOT_SCOPE;
82
- if (e.ie & REACTIVE_SNAPSHOT_STALE) {
83
- e.ie &= ~REACTIVE_SNAPSHOT_STALE;
84
- e.ie |= REACTIVE_DIRTY;
85
- if (dirtyQueue.xe > e.Le) dirtyQueue.xe = e.Le;
115
+ e.l &= ~CONFIG_IN_SNAPSHOT_SCOPE;
116
+ if (e.R & REACTIVE_SNAPSHOT_STALE) {
117
+ e.R &= ~REACTIVE_SNAPSHOT_STALE;
118
+ e.R |= REACTIVE_DIRTY;
119
+ if (dirtyQueue.Fe > e.he) dirtyQueue.Fe = e.he;
86
120
  insertIntoHeap(e, dirtyQueue);
87
121
  }
88
122
  }
89
123
  releaseSubtree(t);
90
- t = t.Ve;
124
+ t = t.Ge;
91
125
  }
92
126
  }
93
127
 
94
128
  function clearSnapshots() {
95
129
  if (snapshotSources) {
96
130
  for (const e of snapshotSources) {
97
- delete e.o?.Qe;
98
- // StoreNode targets share one pre-initialized hidden class (see
99
- // createStoreProxy) — assign undefined instead of deleting, and only
100
- // when present so signal-node sources don't grow the field.
101
- if (e[STORE_SNAPSHOT_PROPS] !== undefined) e[STORE_SNAPSHOT_PROPS] = undefined;
131
+ // The extension is a fixed-shape object with `_snapshotValue`
132
+ // pre-initialized to undefined (see ext()), and every reader tests
133
+ // `!== undefined` — assign, don't `delete`: deleting a field pushes the
134
+ // object to dictionary mode for every later read of every field.
135
+ const t = e.T;
136
+ if (t != null) t.De = undefined;
102
137
  }
103
138
  snapshotSources = null;
104
139
  }
@@ -108,240 +143,474 @@ function clearSnapshots() {
108
143
  function recompute(e, t = false) {
109
144
  // §12d: any recompute can clean a marked subscriber — invalidate skips.
110
145
  bumpNotifyEpoch();
111
- const n = e.Re;
146
+ const n = e.i;
147
+ let o;
148
+ // Lanes (plan sec. 28): the seat of a pass is its node's. A node carrying a lane's
149
+ // derived value runs as the lane's whoever dirtied it — a sync write, a
150
+ // boundary reset, a frame rerun — so its children and its result are the
151
+ // lane's; so does a member the lane's own re-staging dirtied
152
+ // (REACTIVE_LANE_DIRTY, lanes.ts — a leaf the lane owns). A written guess's
153
+ // own pass is its truth arriving (A18), the frame's. A first pass is its
154
+ // creator's (ruling A: a lane pass's children are the lane's frame), and
155
+ // under a guess's lane it reads as the lane's too — a binding the lane
156
+ // mounts sees the screen, like the pass that mounted it (#3835). Not
157
+ // under a verdict lane, for its reads or its result: a verdict lane holds
158
+ // verdicts, and a mount it makes is a mainline mount (A29's boundary
159
+ // exemption, #3851). A tracked read of a lane's value moves a
160
+ // derivation's pass into the lane (`read`); a leaf's never moves.
161
+ // Restored at the end, after this pass's staging and runs have been
162
+ // routed.
163
+ // Lanes exist only with the lane engine installed (`laneStageHook`): a
164
+ // bundle without it seats every pass on mainline (`passLane` is never set)
165
+ // and folds the seat, and its restores, away.
166
+ const E = passLane;
167
+ // The mount a first pass belongs to: its creator's lane, or the one its
168
+ // creator's own mount came from — a render effect a verdict lane's mount's
169
+ // content creates is that mount's too (#4166). Lanes only, like the seat.
170
+ const s = laneRoot;
171
+ const i = laneStageHook && (laneRoot = t && (E || s));
172
+ if (laneStageHook) setPassLane((e.R & REACTIVE_LANE_DIRTY || (e.l & (CONFIG_OVERRIDE | CONFIG_GUESS)) === CONFIG_OVERRIDE) && e.T?.Re?.P ? e.T.Re : // Loose `!=`: false with no creator lane (undefined == null).
173
+ t && E?._parent.Pe != E && creatorPass(context)?.R & REACTIVE_RECOMPUTING_DEPS ? E : null);
174
+ // CARVE 2: lane posture resolution (OPTIMISTIC_DIRTY, derived-override
175
+ // re-derivation, lane adoption through deps) went with the optimistic engine.
176
+ // An error a pass staged, which this pass replaces (`valueChanged`).
177
+ const l = e.l & CONFIG_STAGED_ERROR;
112
178
  if (!t) {
113
- if (e._e && (!n || activeTransition) && activeTransition !== e._e) globalQueue.initTransition(e._e);
114
- deleteFromHeap(e, queueFor(e));
115
- if (e.o !== null) e.o.Ee = null;
116
- // Tracked effects run after finalizePureQueue, so dispose immediately instead of deferring
117
- if (e._e || n === EFFECT_TRACKED) disposeChildren(e); else if (e.ke !== null || e.he !== null) {
118
- markDisposal(e);
119
- const t = ext(e);
120
- t.We = e.he;
121
- t.qe = e.ke;
122
- e.he = null;
123
- e.ke = null;
124
- e.Me = 0;
125
- } else ;
126
- }
127
- let i = !!(e.ie & REACTIVE_OPTIMISTIC_DIRTY);
128
- const u = (e.T & CONFIG_OPTIMISTIC) !== 0 && e.o?.De !== NOT_PENDING && e.o?.De !== undefined;
129
- const l = !!(e.S & STATUS_UNINITIALIZED);
130
- // Outgoing error, captured before the compute clears status: if this run
131
- // recovers to an unchanged value, dependents still holding this object must
132
- // be swept (settleErroredDependents, #2949).
133
- const o = e.S & STATUS_ERROR ? e.o?._ : undefined;
134
- // Re-ask classification lives in the verdict module; capture the flag before
135
- // the recompute wipes _flags below.
136
- const s = (e.ie & REACTIVE_REASK) !== 0;
137
- // Captured before the compute clears it on a sync landing: if that landing
138
- // is transition-held below, the window must stay open until the hold
139
- // commits (commitPendingNode) — a closed window plus a held value reads as
140
- // a pending frame to live observers of the verdict (#2990).
141
- const a = e.Ie;
142
- const r = context;
143
- context = e;
144
- e.Ye = null;
145
- e.Ze++;
146
- e.ie = REACTIVE_RECOMPUTING_DEPS;
147
- e.Te = clock;
148
- let c = e.Pe === NOT_PENDING ? e.be : e.Pe;
149
- let _ = e.Le;
150
- let f = false;
151
- let E = tracking;
152
- let I = currentOptimisticLane;
153
- tracking = true;
154
- // A computed's fn establishes its OWN dependencies, so it must never run
155
- // inside a latest() read window: read() short-circuits through the
156
- // companion path before dependency linking, so a memo created (eagerly
157
- // computed) inside latest(fn) came out permanently dependency-less (#2926).
158
- // latestRead() already suspends the flag for its pull-recomputes; this
159
- // covers creation-time computes and flushes that run inside the window.
160
- const N = latestReadActive;
161
- latestReadActive = false;
162
- // Lane posture lives with the engine: OPTIMISTIC_DIRTY is only ever set by
163
- // engine-driven paths, and _optimisticNodes is only pushed by
164
- // _optimisticWrite, so the hook is installed whenever either gate holds.
165
- if (i) {
166
- const t = GlobalQueue.je(e, true);
167
- if (t) currentOptimisticLane = t;
168
- // `false` = wake-only lane demotion: recompute plain so a mid-tick
169
- // latest()/isPending() pull stages instead of direct-committing (#3009).
170
- // The predicate lives with the engine (recomputeLane).
171
- else if (t === false) i = false;
172
- } else if (activeTransition && !t && activeTransition.Ke.length) {
173
- // Lane adoption: parent-deeper-than-owned-child can run before its OPT-dirty
174
- // child propagates. Walk deps once and inherit the OPT lane so this node
175
- // recomputes under the right posture and propagates correctly.
176
- const t = GlobalQueue.je(e, false);
177
- if (t) {
178
- i = true;
179
- currentOptimisticLane = t;
179
+ deleteFromHeap(e, dirtyQueue);
180
+ // A pending flight is the transition's own work (#3884), and so is a
181
+ // held async memo's re-run with no verdict window of its own (D-25,
182
+ // `holdWork`): a pass that takes no verdict role this time. A probe on
183
+ // an earlier pass left CONFIG_VERDICT, and a frame read here would serve
184
+ // the committed screen, settle the flight and drop the hold — a
185
+ // superseding write's verdict would depend on that probe. Cleared for
186
+ // this pass, before the flight it judges by is superseded; a probe in
187
+ // the body re-stamps it after the plain reads. Not an effect: it has no
188
+ // flight of its own (its pending is a source's), and a verdict memo's
189
+ // change may have just made it a verdict reader (`laneDirty`). Only a
190
+ // build with the lane engine (`laneStageHook`; verdict.ts imports it)
191
+ // ever sets CONFIG_VERDICT: without it the line folds away.
192
+ if (laneStageHook && !n && (e.m & STATUS_PENDING || (e.l & (CONFIG_ASYNC | CONFIG_HELD)) === (CONFIG_ASYNC | CONFIG_HELD))) e.l &= ~CONFIG_VERDICT;
193
+ if (e.T !== null) {
194
+ // The seam's re-derive runs with the parked flight still current; any
195
+ // later pass asks its own question (`_parked`; only the lane engine
196
+ // parks one).
197
+ const t = laneStageHook && e.T.He;
198
+ if (t && t !== e.T.Ee) e.T.He = undefined;
199
+ e.T.Ee = null;
200
+ // Supersede is where an iterator flight dies (#3122): close it now.
201
+ // Idempotent with the cleanup-channel close.
202
+ releaseFlightTeardown(e);
203
+ }
204
+ // L2: a pass over a held derivation joins its transaction — the
205
+ // propagation that reached this node reached the transaction, and this
206
+ // pass's answer replaces the held one there. A node held with nothing
207
+ // staged — pending when its flush parked, or a member of a held frame —
208
+ // joins all the same. Not a render effect (rule 3): it is the frame.
209
+ // Re-run outside its transaction's flush it publishes mainline — the
210
+ // value it staged there is superseded (`frameRead` remembers it for the
211
+ // landing). A frame born into the transaction (`bornInto`: never run,
212
+ // A29, or mounted by a pass of it still awaiting its commit, #4010) keeps
213
+ // staging: live writes keep it current, it effects at the swap (ruling
214
+ // A).
215
+ // A boundary's structure (CONFIG_FRAME) is the frame like a render
216
+ // effect: it publishes mainline outside its transaction's flush (#4283).
217
+ if (e.l & CONFIG_HELD) {
218
+ const t = txOf(e);
219
+ // Held by a blocked lane: the frame joins nothing — a lane never holds
220
+ // a sync write (#3460). The pass is the lane's if it reads the lane's
221
+ // world (`read`), and has left it otherwise.
222
+ // An answered deferred node's re-pass is a lagging question (D-40):
223
+ // it joins nothing, so the write that reached it commits mainline.
224
+ // `EFFECT_RENDER << 23` is CONFIG_FRAME (a render effect, a boundary's
225
+ // tree and output), spelled from the effect type as at creation.
226
+ if (t.P || e.l & CONFIG_LAGS) ; else if (!(e.l & (CONFIG_VERDICT | EFFECT_RENDER << 23))) joinPassTx(t, freshHook?.(e, e.m & STATUS_UNINITIALIZED)); else if (t !== flushTransaction && !(e.m & STATUS_UNINITIALIZED || e.l & CONFIG_HELD_FRAME)) {
227
+ // Published mainline, it is not held: the frame this pass builds is
228
+ // replaced on the spot (#3404) — this flush's commit retires the
229
+ // committed frame the held pass had parked (`commitPendingNode`),
230
+ // after the new frame's first runs, and the held pass's own
231
+ // children die below (`disposeChildren`). The landing re-derives a
232
+ // fresh held frame for the reveal (`_reruns`).
233
+ // Not held any more, whichever frame reader it is: its readers read
234
+ // the value this pass publishes — those that read the staging it
235
+ // supersedes re-derive, even when it publishes the committed value
236
+ // back (no change to notify them by): their held runs carry the
237
+ // superseded frame, and the landing would apply them (#4378).
238
+ if (e.ue !== NOT_PENDING) insertSubs(e);
239
+ e.ue = NOT_PENDING;
240
+ e.l &= ~CONFIG_HELD;
241
+ }
242
+ }
243
+ // The previous pass's frame. If that pass is still awaiting its commit
244
+ // (CONFIG_STAGED) its children were never shown and die on the spot — the
245
+ // frame parked before it stays parked. Otherwise they ARE the frame:
246
+ // park them until this pass commits. Tracked effects run after the seam
247
+ // and own nothing to park.
248
+ if (n === EFFECT_TRACKED || e.l & CONFIG_STAGED) {
249
+ // A lazy node only those children read is released when this pass
250
+ // ends, if the pass doesn't read it again (#4142). A cleanup or
251
+ // `unobserved` hook that throws here neither stops the pass nor skips
252
+ // that drain: its error is rethrown when the flush ends (owner.ts).
253
+ const t = deferredReleases;
254
+ deferReleases(o = []);
255
+ disposeChildren(e);
256
+ deferReleases(t);
257
+ } else if (e.pe !== null || e.ke !== null) parkChildren(e);
258
+ // An error a pass staged (D-41) is replaced by this pass's answer: a
259
+ // value, a flight, or an error of its own (#4109, #4145).
260
+ if (e.l & CONFIG_STAGED_ERROR) {
261
+ e.ue = NOT_PENDING;
262
+ e.l ^= CONFIG_STAGED_ERROR;
180
263
  }
181
264
  }
182
- const T = n && n !== EFFECT_USER;
183
- const d = stale;
184
- if (T) stale = true;
265
+ const r = !!(e.m & STATUS_UNINITIALIZED);
266
+ // Capture both error and pending status before the compute clears them.
267
+ // A conditional can drop its pending source and recover to an unchanged
268
+ // value, leaving blocked dependents outside that source’s settle walk.
269
+ const a = e.m & STATUS_ERROR && e.T?.U;
270
+ const _ = (e.m & STATUS_PENDING) !== 0;
271
+ const u = _ && e.T?.se;
272
+ // Pending SOURCE-hood, captured before the compute clears status: a node
273
+ // whose own flight parked dependents self-registers in _pendingSources
274
+ // (notifyStatus, isSource). If this recompute supersedes that flight and
275
+ // settles synchronously, those dependents settle HERE — asyncWrite's
276
+ // settlePendingSource walk never runs for a landing that was preempted
277
+ // (#3181).
278
+ const I = e.T?.se?.has(e);
279
+ // D-41: this pass's error is staged, not published (`stageError`).
280
+ let N = false;
281
+ // A19 exc. 2: a `refresh()` re-asks the question already answered — quiet
282
+ // for the verdict — unless the question in flight is a new one, which the
283
+ // re-ask does not launder (a poll during a quiet confirm stays quiet).
284
+ // Nor a guess's new question whose answer is still held (#4336: an
285
+ // action's refresh after an outside input change has not committed it).
286
+ const f = (e.R & REACTIVE_REASK) !== 0 && !((e.m & STATUS_PENDING || guessing && e.ue !== NOT_PENDING) && e.T !== null && !e.T.Ve);
287
+ const T = context;
288
+ context = e;
289
+ e._ = null;
290
+ e.de++;
291
+ // REACTIVE_ZOMBIE is position: it says the node sits on its owner's parked
292
+ // frame, and the splice/reawaken paths key off it. A zombie reruns for
293
+ // live writes until the commit that disposes it, so every per-pass wipe
294
+ // — here, the finally below, updateIfNecessary — carries it (#3543).
295
+ e.R = REACTIVE_RECOMPUTING_DEPS | e.R & (REACTIVE_ZOMBIE | REACTIVE_REASK);
296
+ e.ie = clock;
297
+ // The pass's previous value: its staging, else a lane node's lane value
298
+ // when it has one (lanes.ts), else the committed one.
299
+ let S = e.ue !== NOT_PENDING ? e.ue : e.l & CONFIG_OVERRIDE && e.T.P !== NOT_PENDING ? e.T.P : e.le;
300
+ let c = e.he;
301
+ let A = false;
302
+ // L2: did this pass read the future (REACTIVE_JOINED, set by read) — or,
303
+ // a lane's mount, a staging of this flush (#3851, #4166)?
304
+ let R = false;
305
+ let C = tracking;
306
+ tracking = true;
185
307
  try {
186
- if (!false && e.T & CONFIG_SYNC) {
187
- c = e.Se(c);
188
- if (e.o !== null) e.o.Ee = null;
189
- e.Ie = false;
308
+ if (!false && e.l & CONFIG_SYNC) {
309
+ S = e.X(S);
310
+ if (e.T !== null) e.T.Ee = null;
311
+ e.ce = false;
190
312
  } else {
191
- // Snapshot `_inFlight` so we can detect whether `_fn` self-registered an async
192
- // subscription (e.g. `createProjection` calls `handleAsync` from inside its body
193
- // with a setter callback). In that case, the outer `handleAsync` call below would
194
- // clobber the fresh subscription, so we skip it and let the internally-registered
195
- // iteration drive updates.
196
- const t = e.o?.Ee;
197
- const n = e.Se(c);
198
- const i = typeof n === "object" && n !== null;
199
- const u = e.o?.Ee !== t;
200
- c = u || !i ? n : handleAsync(e, n);
201
- if (!u && !i) {
202
- if (e.o !== null) e.o.Ee = null;
313
+ // A projection's body self-registers its flight through handleAsync
314
+ // (with the commit as setter): its undefined return is not a sync
315
+ // answer, and the outer handleAsync must not clobber the registration.
316
+ const t = e.T?.Ee;
317
+ const n = e.X(S);
318
+ const o = typeof n === "object" && n !== null;
319
+ const E = e.T?.Ee !== t;
320
+ S = E || !o ? n : handleAsync(e, n);
321
+ if (!E && !o) {
322
+ if (e.T !== null) {
323
+ e.T.Ee = null;
324
+ if (holdWorkHook) e.l &= ~CONFIG_ASYNC;
325
+ }
203
326
  // A sync (non-object) return is the first real answer; async-shaped
204
327
  // results clear inside handleAsync at their own landing points, and a
205
- // self-registered flight (inFlightChanged — projections) clears when
206
- // its internal handleAsync lands.
207
- e.Ie = false;
328
+ // self-registered flight clears when its own handleAsync lands.
329
+ e.ce = false;
208
330
  }
209
331
  }
210
332
  // On a status-free node clearStatus is a guaranteed no-op: every field
211
333
  // its body gates on is either _statusFlags or lives in the cold
212
334
  // extension — no extension, no status to clear. (_x from an unrelated
213
335
  // installer just makes clearStatus a cheap re-verified no-op.)
214
- if (e.S !== 0 || e.o !== null) clearStatus(e, t);
215
- // _optimisticLane is only ever assigned by engine paths (CONFIG_HAS_LANE
216
- // is their sticky presence mark).
217
- if (e.T & CONFIG_HAS_LANE && e.o?.Be) GlobalQueue.ze(e);
336
+ if (e.m !== 0 || e.T !== null) clearStatus(e, t);
218
337
  } catch (t) {
219
338
  const n = t instanceof NotReadyError;
220
- if (n && e.Ie) {
339
+ // An error answers, and so does a loading window's unready read (D-25,
340
+ // verdict.ts `holdWork`): no flight is its own. A pass left pending
341
+ // keeps the last answer's. (Kept by the verdict engine alone.)
342
+ if (holdWorkHook && (!n || e.ce)) e.l &= ~CONFIG_ASYNC;
343
+ if (n && e.ce) {
221
344
  // Loading window with an unready sync dependency: register for the
222
345
  // source's settle (the settlePendingSource walk runs off
223
346
  // _pendingSources + _blocked alone) but take NO read-visible pending
224
- // status, no downstream propagation, no transition, no lane
225
- // registration — the committed loading value keeps serving. If the
347
+ // status and no downstream propagation — the committed loading value
348
+ // keeps serving. If the
226
349
  // node is currently errored the error stays the answer until this
227
350
  // retry can actually run.
228
351
  parkLoadingWindow(e, t);
229
352
  } else {
230
- // Track pending async in the lane (not the lane's source — it creates the lane
231
- // but doesn't belong to it). Set lane BEFORE notifyStatus for downstream propagation.
232
- if (n && currentOptimisticLane) GlobalQueue.$e(e);
233
- let i = false;
234
- if (n) {
235
- ext(e).ue = true;
236
- if (GlobalQueue.Je !== null) i = GlobalQueue.Je(e, s);
353
+ // D-41: a pass of a held node, or one that read a staging of this
354
+ // flush, stages its error (`stageError`).
355
+ if (n || !(N = stageError(e, t, true))) {
356
+ if (n) ext(e).fe = true;
357
+ notifyStatus(e, n ? STATUS_PENDING : STATUS_ERROR, t);
358
+ // The replacement source is fully propagated now. If no new flight
359
+ // re-owned self, retire the superseded flight and its dependent copies.
360
+ if (n && I && !e.T?.Ee) settlePendingSource(e);
237
361
  }
238
- notifyStatus(e, n ? STATUS_PENDING : STATUS_ERROR, t, undefined, n ? e.o?.Be : undefined);
239
- if (i) GlobalQueue.p(e);
362
+ // A re-park drops what the earlier pass carried (#3456): a source this
363
+ // pass no longer reaches — its branch switched, or a fresh flight
364
+ // replaced the inputs' pending with its own — stays copied onto
365
+ // dependents that reached it only through here, and its landing walk
366
+ // stops at this node (nothing left to retire) before it finds them. A
367
+ // dependent then waits forever on a flight it has no path to. The
368
+ // re-park twin of the unchanged-value recovery sweep below; dependents
369
+ // with another path keep the source (retryReaches). A staged error
370
+ // answered what the pass carried: the same.
371
+ if ((n || N) && u) for (const t of u) if (t !== e && !e.T?.se?.has(t)) settlePendingSource(e, t);
240
372
  }
241
373
  } finally {
242
- tracking = E;
243
- latestReadActive = N;
244
- if (T) stale = d;
374
+ tracking = C;
375
+ if (laneStageHook) laneRoot = s;
245
376
  // Consume the missed-wake latch (#3037, set by insertSubs): a dep write
246
377
  // landed beneath this pass on a link it had already validated. The wipe
247
378
  // below must not key off DIRTY/CHECK — the read-time pull protocol
248
379
  // (markNode(c) in read()) marks the running node as part of ordinary
249
380
  // bookkeeping, and those marks are correctly discarded here.
250
- f = (e.ie & REACTIVE_MISSED_WAKE) !== 0;
251
- e.ie = REACTIVE_NONE | (t ? e.ie & REACTIVE_SNAPSHOT_STALE : 0);
252
- context = r;
253
- }
254
- if (!e.o?._) {
255
- trimStaleDeps(e);
256
- const s = u ? unwrapOverride(e.o?.De) : e.Pe === NOT_PENDING ? e.be : e.Pe;
257
- let r = false;
258
- try {
259
- r = !n && l || !e.Ue || !e.Ue(s, c);
260
- } catch (t) {
261
- // A throwing user comparator is an error of this node's computation.
262
- // Route it through the same status path as a compute-phase throw so
263
- // error boundaries contain it; otherwise it unwinds the scheduler
264
- // flush, bypassing every boundary and wedging the queue (#2837).
265
- notifyStatus(e, STATUS_ERROR, t);
266
- }
381
+ A = (e.R & REACTIVE_MISSED_WAKE) !== 0;
382
+ // A mount's staged read joins it — except a nested derivation left
383
+ // pending: no render effect reaches it yet, so it holds nothing and
384
+ // reaches no boundary (Reader, #4203), whatever it read — a memo the
385
+ // mount bore held over the staging included, at any depth (#4340). A
386
+ // reader that does is caught.
387
+ R = !!(e.R & (i ? E || n || !(e.m & STATUS_PENDING) ? REACTIVE_JOINED | REACTIVE_STAGED_READ : 0 : REACTIVE_JOINED));
388
+ // A verdict reader is one for as long as it probes, or reads a memo
389
+ // that does (`read`): a pass that did neither (REACTIVE_PROBED, the same
390
+ // bit) is an ordinary derivation again. Its async posture is the
391
+ // verdict engine's to keep (CONFIG_ASYNC, verdict.ts `holdWork`, D-25).
392
+ // (Only the verdict engine probes: without it the line folds away.)
393
+ if (observeFlightHook) e.l = e.l & ~CONFIG_VERDICT | e.R & REACTIVE_PROBED;
394
+ // REACTIVE_DISPOSED survives too (#3621): the pass may have disposed its
395
+ // own owner (a memo calling its root's `dispose()`, a cleanup doing so
396
+ // #3601/#3606), and `disposeChildren` set the flag on this node
397
+ // reentrantly. Dropped, the node read as live — `refresh()` re-ran it
398
+ // and `isDisposed()` lied. REACTIVE_FRAME_READ (A15 stale reader) is
399
+ // this pass's verdict for the landing; the next pass's wipe at the top
400
+ // clears it.
401
+ e.R = e.R & (REACTIVE_ZOMBIE | REACTIVE_DISPOSED | REACTIVE_FRAME_READ | REACTIVE_STAGED_READ | REACTIVE_LANE_READ | REACTIVE_SCREEN_READ | REACTIVE_PROBE_UNANSWERED) | (t ? e.R & REACTIVE_SNAPSHOT_STALE : 0);
402
+ context = T;
403
+ if (o) for (const e of o) releaseIfSettledUnobserved(e);
404
+ // A19 exc. 2: a pass that went pending on a `refresh()` re-asks the
405
+ // question already answered — quiet for the verdict; any other pending
406
+ // pass is a new question. Settled, there is nothing to classify.
407
+ if (e.T !== null) e.T.Ve = f && (e.m & STATUS_PENDING) !== 0;
408
+ }
409
+ // A node that died during its own pass (#3621) is dead at the end of it,
410
+ // and the pass is void. Its owner's teardown already unlinked its deps,
411
+ // removed it from the heap and ran its cleanups; what remains is what the
412
+ // body did AFTER the `dispose()` call: reads that re-linked the dead node
413
+ // to its sources (unlinked here — the leak that kept it re-running in a
414
+ // torn-down tree), a flight it may have started (retired: the landing
415
+ // checks `_inFlight` identity), and the value it returned. That value is
416
+ // NOT published: a dead node freezes at its last committed value (#3024),
417
+ // so nothing is staged, committed, or propagated to subscribers, and an
418
+ // effect's run is not enqueued (runEffect would refuse it anyway). The
419
+ // attribution frame opened at the top is still closed.
420
+ if (e.R & REACTIVE_DISPOSED) {
421
+ clearDeps(e);
422
+ if (e.T !== null) e.T.Ee = null;
423
+ if (laneStageHook) setPassLane(E);
424
+ return;
425
+ }
426
+ // The lane this pass is work of, if any: its seat (the node's, its
427
+ // creator's, or the one a lane read moved it into). A pass in a lane's
428
+ // seat that read none of the lane's world has left it: its result is the
429
+ // frame's (a derivation whose branch no longer reaches the guess). A
430
+ // guess is written, not derived — it never leaves this way.
431
+ let O = !laneStageHook ? null : passLane;
432
+ // Listed before its staging, a pending pass included (the lane's own
433
+ // flight is the lane's); false: the pass left the lane (lanes.ts).
434
+ const d = !!e.T?.U || N;
435
+ if (laneStageHook && O !== null) {
436
+ if (laneStageHook(e, O, t, d)) setPassLane(O); else O = null;
437
+ }
438
+ if (!d) {
439
+ // INV-11 (#3330): the equality gate compares against the slot this run
440
+ // publishes to — the staged `_pendingValue` when staged; a lane node's
441
+ // lane value (a node entering the lane this pass has none yet — its
442
+ // committed value, `laneStage` having voided a frame staging).
443
+ const o = e.ue !== NOT_PENDING ? e.ue : e.l & CONFIG_OVERRIDE && e.T.P !== NOT_PENDING ? e.T.P : e.le;
444
+ // A throwing comparator is contained inside `isSame` (A3, #2837; #4111).
445
+ // A pass replacing an error this node staged is a new answer, even the
446
+ // committed value (D-41: a recovery equal to the last good value is a
447
+ // change): the slot it publishes to held the error, and the readers that
448
+ // derived from it — `latest()` throws it — re-derive (#4403).
449
+ const E = !n && r || !!e.T?.Te || !!l || !isSame(e, o, S);
267
450
  // Effects use `_equals: false` (no per-effect closure). The side effects that
268
451
  // the equals closure used to perform — flagging the effect dirty and enqueueing
269
452
  // its runner — happen here instead. `!create` matches the previous `initialized`
270
453
  // gate: the explicit recompute(node, true) inside effect() does not enqueue, so
271
454
  // effect() can call its runner synchronously for the first run.
272
- if (n && r) {
273
- e.Xe = !e.o?._;
455
+ if (n && E &&
456
+ // A stale reader of a lane (REACTIVE_SCREEN_READ) that computed what it
457
+ // last applied owes no run (lanes.ts).
458
+ !(e.R & REACTIVE_SCREEN_READ && !e.Ae && Object.is(e.ge, S)) &&
459
+ // A re-staging pass owes no run: the commit replays the effect (#3802).
460
+ e.ue === NOT_PENDING) {
461
+ e.Ae = !e.T?.U;
274
462
  // Reuse one bound runner per effect — runEffect no-ops on a stale
275
463
  // `_modified`, so re-enqueueing the same function is harmless.
276
- if (!t) e.C.enqueue(n, e.et ??= GlobalQueue.tt.bind(null, e));
464
+ // Lane work's run is the lane's (released at its reveal) — a first
465
+ // pass under a lane too: `effect()` skips the synchronous first run of
466
+ // a lane's node.
467
+ if (!t || O !== null) globalQueue.enqueue(n, e.W ??= GlobalQueue.q.bind(null, e), O);
277
468
  }
278
- if (e.o?._) ; else if (r) {
279
- const l = u ? e.o?.De : undefined;
280
- if (t ||
281
- // Plain sync flush (no transition on either side) commits effect
282
- // values directly — the pending round-trip (queuePendingNode +
283
- // commitPendingNodes) exists to sequence transition reveals, and
284
- // paying it per effect on the plain path is pure overhead.
285
- n && (activeTransition !== e._e || activeTransition === null) || i) {
286
- e.be = c;
287
- // Lane-propagated correction: upstream data is fresh, correct the
288
- // override unconditionally. The direct _value commit is the lane's
289
- // own reveal schedule; drop any superseded older hold so its queued
290
- // commit can't clobber the fresh value.
291
- if (u && i) {
292
- ext(e).De = c === undefined ? OVERRIDE_UNDEFINED : c;
293
- e.Pe = NOT_PENDING;
294
- }
469
+ // Lanes. A written guess's own pass from the frame: its source
470
+ // recomputed it — the truth (A18: "the source is whatever recomputes the
471
+ // node"). It stages under the lane's parent whether or not it changed (a
472
+ // confirm is a landing too — the lane's end commits it) and notifies
473
+ // only a correction: an equal truth re-runs nothing (lanes.ts). (As the
474
+ // lane's own work the lane judged it — `laneStage`.)
475
+ // (The hook first: a build without lanes folds the arm away.)
476
+ if (laneOutcomeHook && !t && O === null && e.l & CONFIG_GUESS && laneOutcomeHook(e, S, d)) ; else if (d) ; else if (E) {
477
+ // L2: a first pass is born held — staged, committed with the
478
+ // transaction, its effect's first run the landing's (A29; `effect()`
479
+ // skips the synchronous first run on a staged value) — when the flush
480
+ // has joined a transaction and either the pass read a held node (it
481
+ // derives from that world — the join merged the node's transaction
482
+ // into the flush's), or — a verdict lane's mount, mainline — read a
483
+ // staging of this flush (the same world before the seam parks it; the
484
+ // mount is not shown ahead of it: #3851), or a pass created it (ruling
485
+ // A: a held pass's children are the transaction's). Any other mount
486
+ // that read a staging does not join by being new (2026-10-07): it
487
+ // publishes and re-derives if the flush parks. A node created outside any pass
488
+ // that read only the committed world (root setup, a mount, an effect
489
+ // callback) is nobody's frame and publishes directly, as does a first
490
+ // pass that runs before anything joins: the pass's input, not a verdict
491
+ // after it. An effect still carrying an uncommitted staged value
492
+ // re-stages: the commit applies the latest pass, not the born-held one.
493
+ if (O !== null) {
494
+ // Lane work: the lane's value — into the slot while the lane has not
495
+ // revealed (its reveal shows it), as its staging once it has (the
496
+ // next reveal promotes it; the screen keeps the revealed value). An
497
+ // effect's value slot is private: the run the lane holds is what
498
+ // shows it — unless the effect was born held, whose first run is the
499
+ // commit's (A29): it re-stages, as below.
500
+ if (n && e.ue === NOT_PENDING) e.le = S; else if (n || O.Ue) e.ue = S; else e.T.P = S;
501
+ } else if (t ? !((flushTransaction !== null || passTx !== null) && (R || creatorPass(T)?.R & REACTIVE_JOINED)) : n && e.ue === NOT_PENDING) {
502
+ // A first pass publishes directly. So does an effect: its value slot
503
+ // is private (only its own run reads it), and the staging round-trip
504
+ // (queuePendingNode + commitPendingNodes) paid per effect on the
505
+ // plain path is pure overhead.
506
+ // NOTE (stage-3, 2026-08-21): a quiet-world MEMO direct-commit was
507
+ // attempted here and REVERTED — memo staging is load-bearing beyond
508
+ // transitions: mid-batch pulls (read-triggered recomputes before
509
+ // sources commit) must see the fresh value while PLAIN reads stay
510
+ // committed until flush (#3009 purity). The pending round-trip is
511
+ // that separation; it cannot be skipped on any path a pull can reach.
512
+ e.le = S;
295
513
  } else {
296
- e.Pe = c;
297
- // A window landing that gets held re-opens the window until the hold
298
- // commits — the verdict's held-value branch is window-gated (#2990).
299
- if (a) e.Ie = true;
300
- // Transition-held sync recompute is a write path like setSignal/asyncWrite,
301
- // so sync derivations of held sources stay visible to isPending()/latest()
302
- // (#2831). Both companion writes are transition-scoped (optimistic) and
303
- // auto-revert/re-derive at commit. Skipped for plain flushes where the
304
- // pending value commits before effects run.
305
- if ((activeTransition || e._e) && GlobalQueue.Oe !== null) GlobalQueue.Oe(e, c);
514
+ e.ue = S;
515
+ // D-39: a staged recovery keeps the error it replaces the display
516
+ // until it commits (`commitPendingNode`), as a write over one does
517
+ // (`setMemo`).
518
+ if (a) e.T.Te = a;
519
+ // Born held: in the transaction from birth, so a read of it this
520
+ // tick is a read of its world (the mount-during-a-hold case), not
521
+ // only after the seam parks it.
522
+ if (t) {
523
+ holdNode(e, flushTransaction ?? passTx);
524
+ // Born into the future: no committed value until the landing
525
+ // (`commitPendingNode` initializes it) — every reader of it
526
+ // derives from the future (`read`), an untracked one throws
527
+ // (A19 exc. 1).
528
+ e.m |= STATUS_UNINITIALIZED;
529
+ // A boundary that has not shown content catches it: its fallback
530
+ // waits for the commit (#3540). (The pass's own status clear above
531
+ // dropped what the read recorded.) Not when the flush has joined
532
+ // the hold and the pass is its own (not a verdict lane's mount,
533
+ // #3851): the mount is part of the hold, and the boundary appears
534
+ // at its commit, its fallback never seen (D-13, U-3) — a `<Show>`
535
+ // mounting it over a landing included.
536
+ freshHook?.(e, !i && flushTransaction);
537
+ }
306
538
  }
307
539
  // insertSubs only walks _subs (no scheduling of its own), so a
308
540
  // subscriber-less node has nothing to notify.
309
- if (e.u !== null && (!u || i || e.o?.De !== l)) insertSubs(e, i || u);
310
- } else if (u) {
311
- // Unchanged value (equals the override) recomputed while the override
312
- // is active: _value may still be stale, so hold the authoritative value
313
- // for commit on its own transition's schedule — invisibly (A17/A18).
314
- if (e.Pe === NOT_PENDING) queuePendingNode(e);
315
- e.Pe = c;
316
- if (a) e.Ie = true;
317
- // see the held branch above (#2990)
318
- } else if (e.Le != _) {
319
- for (let t = e.u; t !== null; t = t.fe) {
320
- insertIntoHeapHeight(t.ae, queueFor(t.ae));
541
+ if (e.o !== null) {
542
+ insertSubs(e);
543
+ // Lane work re-staged: the lane's members re-derive as its work.
544
+ if (O !== null) laneDirty(e, O);
321
545
  }
546
+ } else if (e.he != c) {
547
+ for (let t = e.o; t !== null; t = t.t) insertIntoHeapHeight(t.u, dirtyQueue);
322
548
  }
323
549
  // Silent recovery: errored → unchanged value fires no notification, but
324
550
  // dependents still holding the propagated error consumed their dirty flag
325
551
  // in an errored run and may sit on stale commits (#2949). Changed-value
326
552
  // recoveries ride insertSubs above; a comparator throw re-errored the node
327
553
  // (el._x?._error re-set), so this only runs on a genuinely clean recovery.
328
- if (o !== undefined && !r && !e.o?._) settleErroredDependents(e, o);
329
- }
330
- currentOptimisticLane = I;
331
- const S = e.Pe !== NOT_PENDING || e.o !== null && (e.o.qe !== null || e.o.We !== null) || (e.S & (STATUS_PENDING | STATUS_UNINITIALIZED)) !== 0;
332
- // Override-covered holds (hasOverride) always queue: their commit belongs
333
- // to their own transition's schedule (A18 re-rule) and is unobservable
334
- // under the override (A17). Revert no longer commits anything, so an
335
- // unqueued covered hold would leak (INV-7) once the revert clears
336
- // _transition.
337
- S && (!t || e.S & STATUS_PENDING) && (!e._e || u) && queuePendingNode(e);
338
- e._e && n && activeTransition !== e._e && runInTransition(e._e, () => recompute(e));
554
+ if (!E && !e.T?.U) {
555
+ if (a) settleErroredDependents(e, a);
556
+ // Self-registration (this node's own superseded flight) is the #3181
557
+ // sweep's business below — retiring it here too would walk twice.
558
+ if (u) for (const t of u) if (t !== e) settlePendingSource(e, t);
559
+ }
560
+ // #3181: a synchronous settle supersedes the old landing callback, so
561
+ // recompute owns its pending-source sweep. An uninitialized node without
562
+ // a replacement source still has no truth to reveal and must stay parked.
563
+ if (I && !(e.m & (STATUS_PENDING | STATUS_UNINITIALIZED))) settlePendingSource(e);
564
+ }
565
+ // A first pass that went pending where the born-held arm would have held
566
+ // its value (it read a held node, or a verdict lane's mount read a staging,
567
+ // #3851) is held like it: its answer is the hold's, and the hold waits
568
+ // for it (A29, #3800). A boundary that has not shown content catches it
569
+ // and it is held all the same — its answer reveals no earlier than the
570
+ // hold's commit (D-12) — but the hold does not wait for it: its only
571
+ // readers are behind the fallback (`_hidden`; F-4, #3540). A boundary
572
+ // re-armed by `on` owns what it collects outright (D-14): not held.
573
+ if (t && O === null && R && e.m & STATUS_PENDING) {
574
+ const t = flushTransaction ?? passTx;
575
+ if (t && !t.P && freshHook?.(e) !== CAUGHT_REARMED) holdNode(e, t);
576
+ }
577
+ // Dependencies are the committed frame's until it is replaced (A30, #3410):
578
+ // a pass that staged its value leaves the previous pass's tail linked for
579
+ // `commitPendingNode` to trim, so a write to a dependency the committed
580
+ // value still derives from reaches this node until the commit. A pass that
581
+ // published directly, or changed nothing, trims now. An errored pass (a
582
+ // throw, NotReady included, or a comparator throw above) keeps its full
583
+ // list as before — `_depsTail` marks where it stopped — and the commit
584
+ // skips it by the same `_error`. An effect's frame is the run its value is
585
+ // applied by, not the value slot (#3438): a pass that still owes a run
586
+ // (`_modified`) has not replaced what the last run published, so its tail
587
+ // waits for `runEffect` to trim once the run applies. A pass that changed
588
+ // nothing replaced nothing either (#3469): it cannot know at its tail
589
+ // whether the flush that ran it will park with its inputs held, so the
590
+ // trim waits on the flush's verdict (`heldTrim`: trimmed at the commit,
591
+ // kept at a park — one spurious recompute at most). A creation pass and a
592
+ // tracked effect trim now: their frames are replaceable like a direct
593
+ // commit, and a tracked effect's spurious run would be user-visible.
594
+ if (!e.T?.U && e.ue === NOT_PENDING && !(n && e.Ae)) {
595
+ if (t || n === EFFECT_TRACKED) trimStaleDeps(e); else heldTrim(e);
596
+ }
597
+ // A staged value, a parked frame (L2: the commit retires it), or status the
598
+ // commit sweep must settle (a pending or uninitialized pass), queues the
599
+ // node for this flush's commit. A first pass queues only when pending or
600
+ // born held: otherwise its value published directly. A queued pass's
601
+ // children are uncommitted (CONFIG_STAGED) until that commit.
602
+ if (e.ue !== NOT_PENDING || e.T !== null && (e.T.me !== null || e.T.Le !== null) || (e.m & (STATUS_PENDING | STATUS_UNINITIALIZED)) !== 0 && (!t || (e.m & STATUS_PENDING) !== 0)) {
603
+ e.l |= CONFIG_STAGED;
604
+ // Lane work is the lane's to reveal (its seam), not this flush's commit.
605
+ if (O === null) queuePendingNode(e);
606
+ }
607
+ if (laneStageHook) setPassLane(E);
339
608
  // Missed-wake reschedule (see the finally above): values this pass read
340
609
  // before the nested commit are stale, so run again now that the heap will
341
610
  // accept the node. Equality gates stop same-value landings from cascading,
342
611
  // and a re-run only latches again if another nested commit changes a dep
343
612
  // beneath it — convergent unless deps genuinely keep changing.
344
- if (f) {
613
+ if (A) {
345
614
  enqueueSub(e);
346
615
  schedule();
347
616
  }
@@ -352,23 +621,32 @@ function updateIfNecessary(e) {
352
621
  // (_depsTail/_depGen) is live, and a nested recompute would corrupt it.
353
622
  // A mid-pass mark stays latched for recompute's own tail to reschedule
354
623
  // (#3037); readers meanwhile serve the values the pass has so far.
355
- if (e.ie & REACTIVE_RECOMPUTING_DEPS) return;
356
- if (e.ie & REACTIVE_CHECK) {
357
- for (let t = e.nt; t; t = t.it) {
358
- const n = t.ut;
359
- const i = n.lt || n;
360
- if (i.Se) {
361
- updateIfNecessary(i);
624
+ // Never recompute a DISPOSED node either: recompute rewrites _flags and
625
+ // would resurrect it (#2983) — readers serve its last value.
626
+ if (e.R & (REACTIVE_RECOMPUTING_DEPS | REACTIVE_DISPOSED)) return;
627
+ if (e.R & REACTIVE_CHECK) {
628
+ for (let t = e.C; t; t = t.G) {
629
+ const n = t.S;
630
+ if (n.X) {
631
+ updateIfNecessary(n);
362
632
  }
363
- if (e.ie & REACTIVE_DIRTY) {
633
+ if (e.R & REACTIVE_DIRTY) {
364
634
  break;
365
635
  }
366
636
  }
367
637
  }
368
- if (e.ie & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY) || e.o?._ && e.Te < clock && !e.o?.Ee) {
638
+ if (e.R & REACTIVE_DIRTY || e.T?.U && e.ie < clock && !e.T?.Ee) {
369
639
  recompute(e);
370
640
  }
371
- e.ie = e.ie & (REACTIVE_SNAPSHOT_STALE | REACTIVE_IN_HEAP | REACTIVE_IN_HEAP_HEIGHT);
641
+ // The guard above refused an already-disposed node; the recompute it just
642
+ // ran may have disposed it (#3621) — carry the flag, or it comes back alive.
643
+ // The manual-write mark is state, not scheduling (#3612, A34 rule B): it
644
+ // says the node's staging is a PROPOSAL, lifted by a pass that re-derives
645
+ // or by the commit — a pull that recomputed nothing must not. Nor may it
646
+ // erase the last pass's verdicts for the landing (a stale reader's
647
+ // REACTIVE_FRAME_READ — a verdict reader pulled by a sibling before the
648
+ // landing lost its re-derivation; the lane reads likewise).
649
+ e.R = e.R & (REACTIVE_SNAPSHOT_STALE | REACTIVE_IN_HEAP | REACTIVE_IN_HEAP_HEIGHT | REACTIVE_ZOMBIE | REACTIVE_DISPOSED | REACTIVE_MANUAL_WRITE | REACTIVE_FRAME_READ | REACTIVE_STAGED_READ | REACTIVE_LANE_READ | REACTIVE_SCREEN_READ | REACTIVE_PROBE_UNANSWERED);
372
650
  }
373
651
 
374
652
  function computed(e, t) {
@@ -376,124 +654,270 @@ function computed(e, t) {
376
654
  // `in` (not `!== undefined`): an explicit `loadingValue: undefined` on a
377
655
  // `T | undefined` node is a real commit #0. The typeof guard tolerates
378
656
  // non-object option values that older call shapes force through `as any`.
379
- const i = t !== null && typeof t === "object" && "loadingValue" in t;
380
- const u = {
657
+ const o = t !== null && typeof t === "object" && "loadingValue" in t;
658
+ // Two literals, one per tier, selected at build time (the observe flag is
659
+ // a literal after replacement; the untaken branch is dead code). The observe
660
+ // literal is the prod literal plus its `_name` and `_loc` slots — slots in
661
+ // the boilerplate, because a post-construction `self._name = …` forces a
662
+ // hidden-class transition and an out-of-object property store on EVERY node
663
+ // (measured: the whole of the observe tier's creation overhead). Keep the
664
+ // two in sync — the dist artifact test pins observe's key set to prod's
665
+ // plus the slots.
666
+ const E = {
381
667
  id: inheritId(t, n, context),
382
- T: (n ? CONFIG_TRANSPARENT : 0) | (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (!context || t?.lazy ? CONFIG_AUTO_DISPOSE : 0) | (t?.sync ? CONFIG_SYNC : 0) | (t?.H ? CONFIG_NO_SNAPSHOT : 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
383
- Ue: t?.equals != null ? t.equals : isEqual,
384
- he: null,
385
- C: context?.C ?? globalQueue,
386
- we: context?.we ?? defaultContext,
387
- Me: 0,
388
- Se: e,
389
- be: i ? t.loadingValue : undefined,
390
- Le: 0,
391
- ot: undefined,
392
- st: null,
393
- nt: null,
394
- Ye: null,
395
- Ze: 0,
396
- u: null,
397
- rt: null,
398
- ve: context,
399
- Ve: null,
400
- ct: null,
668
+ l: (n ? CONFIG_TRANSPARENT : 0) | (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (!context || t?.lazy ? CONFIG_AUTO_DISPOSE : 0) | (t?.sync ? CONFIG_SYNC : 0) | (t?.ae ? CONFIG_NO_SNAPSHOT : 0) | (t?.xe ?? 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
669
+ ye: t?.equals ?? isEqual,
401
670
  ke: null,
402
- ie: t?.lazy ? REACTIVE_LAZY : REACTIVE_NONE,
671
+ A: context?.A ?? defaultContext,
672
+ be: 0,
673
+ X: e,
674
+ le: o ? t.loadingValue : undefined,
675
+ he: 0,
676
+ ve: undefined,
677
+ We: null,
678
+ C: null,
679
+ _: null,
680
+ de: 0,
681
+ o: null,
682
+ Ze: null,
683
+ _parent: context,
684
+ Ge: null,
685
+ we: null,
686
+ pe: null,
687
+ R: t?.lazy ? REACTIVE_LAZY : REACTIVE_NONE,
403
688
  // A loadingValue node is born committed: commit #0 is already in _value.
404
- S: i ? 0 : STATUS_UNINITIALIZED,
405
- Te: clock,
406
- Pe: NOT_PENDING,
407
- _e: null,
408
- _t: -1,
409
- Ie: i,
410
- // Cold machinery (async/transition/optimistic/verdict slots) lives one
411
- // hop away in the lazily-allocated extension — the core literal MUST
689
+ m: o ? 0 : STATUS_UNINITIALIZED,
690
+ ie: clock,
691
+ ue: NOT_PENDING,
692
+ Me: -1,
693
+ ce: o,
694
+ // Cold machinery (async slots) lives one hop away in the lazily-allocated extension — the core literal MUST
412
695
  // stay under V8's in-object boundary (§12: past ~39 fields every
413
696
  // allocation spills to a backing store and creation cost ~4x's).
414
- o: null
697
+ T: null
415
698
  };
416
- if (t?.unobserved) ext(u).ft = t.unobserved;
417
- setupComputedNode(u, t);
418
- return u;
699
+ if (t?.unobserved) ext(E).Ye = t.unobserved;
700
+ setupComputedNode(E, t);
701
+ return E;
419
702
  }
420
703
 
421
704
  /** Lazily allocate a node's cold extension (ONE shape for signals and
422
705
  * computeds — `_x` access stays monomorphic). Installers write through
423
706
  * this; hot paths read `el._x?._field` gated by the _config presence bits.
424
707
  * Never call ext() just to store a field's default. */ function ext(e) {
425
- return e.o ??= {
426
- De: undefined,
427
- Et: undefined,
428
- Be: undefined,
429
- Ge: undefined,
430
- ge: undefined,
431
- It: undefined,
432
- t: 0,
708
+ return e.T ??= {
433
709
  Ee: null,
434
- _: undefined,
435
- ue: undefined,
436
- le: undefined,
437
- A: undefined,
438
- pe: false,
439
- l: null,
440
- ft: undefined,
441
- Qe: undefined,
442
- We: null,
443
- qe: null,
444
- Nt: undefined
710
+ Se: null,
711
+ U: undefined,
712
+ fe: undefined,
713
+ se: undefined,
714
+ Ye: undefined,
715
+ De: undefined,
716
+ me: null,
717
+ Le: null,
718
+ Re: null,
719
+ Ve: false,
720
+ je: NOT_PENDING,
721
+ Be: -1,
722
+ Ie: 0,
723
+ P: NOT_PENDING,
724
+ I: 0,
725
+ Te: null
445
726
  };
446
727
  }
447
728
 
729
+ /** A tracked read of a held node (the reader derives from its transaction's
730
+ * world): the reading pass is marked as having read it (born-held decision
731
+ * in `recompute`), and the flush joins the node's transaction — unless the
732
+ * reader is a render effect. A render effect is the frame, not a derivation
733
+ * (rule 3): in that transaction's own flush, or born into it (uninitialized,
734
+ * A29), it reads the staged value and holds nothing of its own; otherwise it
735
+ * reads the committed value instead (`frameRead`). A node born into the
736
+ * future has no committed value: every reader joins it. A first pass is
737
+ * something not ready under a loading boundary that has not shown content:
738
+ * the boundary collects it and shows its fallback, and the pass is the
739
+ * boundary's, not the tick's (A29's boundary exemption, #3540). */ function joinPass(e, t) {
740
+ e.R |= REACTIVE_JOINED;
741
+ // A new memo's first pass is the hold's work (born held) but not the
742
+ // tick's: a render effect that reads it decides — under a boundary that
743
+ // catches it, the boundary's; otherwise the tick joins the hold (#3540:
744
+ // the reader's position, not where the memo was created). Its catch, if
745
+ // any, is at its staging.
746
+ if (t.m & STATUS_UNINITIALIZED || e.i !== EFFECT_RENDER) joinPassTx(txOf(t), !e.i && e.m & STATUS_UNINITIALIZED || freshHook?.(e));
747
+ }
748
+
749
+ /** `joinPass` into the hold `t` (a folded guess's: store/optimistic.ts
750
+ * `foldedRead`, D-34) — a reader is never a render effect there. */ function joinPassAt(e, t) {
751
+ e.R |= REACTIVE_JOINED;
752
+ joinPassTx(t, !e.i && e.m & STATUS_UNINITIALIZED || freshHook?.(e));
753
+ }
754
+
755
+ /** A15's stale reader (shared-hole and reveal corollaries): a render effect
756
+ * reading a node held by a transaction that is not the flush's. It is served
757
+ * the committed value — a pending one's too — publishes mainline, and is
758
+ * re-derived after that transaction's landing (`_reruns`,
759
+ * REACTIVE_FRAME_READ) so the frame catches up with it once it is the
760
+ * committed world. Not a frame born into that transaction (held and never
761
+ * committed — created by one of its held passes, A29; `bornInto`): that
762
+ * one reads its transaction's values and keeps staging. Not of a flight
763
+ * whose inputs are already visible (CONFIG_INPUTS_PUBLISHED, #3305): its
764
+ * committed value would tear against them, so the reveal observes the
765
+ * flight instead. Once per pass.
766
+ *
767
+ * Lane work is a stale reader too (maintainer, 2026-10-01: a lane sees the
768
+ * screen plus its own guesses). A held write is held because showing it
769
+ * would tear against the rest of its frame; a lane revealing a derivation
770
+ * of it would show it anyway, beside inputs still committed elsewhere on
771
+ * the page. The guess is the one value the user opted into predicting. So
772
+ * a lane pass reading a transaction's held node — its parent's or any
773
+ * other's — derives from the committed value and is re-derived at that
774
+ * landing. The case read-through was built for — a corrected guess whose
775
+ * downstream async must refetch with the truth at once, while another of
776
+ * the parent's flights is still up — needs none of this: the correction
777
+ * dissolves the lane, and the refetching pass is the parent's own, reading
778
+ * the truth as a member (`joinPass`). */ function frameRead(e, t) {
779
+ // Frame readers: a render effect, a verdict reader (CONFIG_VERDICT — it
780
+ // reads the screen like one: `[isPending(x), x()]` never pairs the fresh
781
+ // value with pending, A10, because the reader never sees the fresh value),
782
+ // and lane work (a lane sees the screen plus its own guesses). Not a
783
+ // mount's memo (2026-10-02, considered and reverted): a memo is not a
784
+ // leaf — the transaction's own later passes read it — so a mainline mount's
785
+ // derivations carry the future (A29, born held); only its direct bindings
786
+ // read the screen. Lane work with no committed value yet reads a flight
787
+ // as a mount's memo does: it enters, and the boundary it mounts catches
788
+ // the pending (#3540).
789
+ // A boundary's tree and output (CONFIG_FRAME) read as a render effect
790
+ // does: structure, not a derivation of what it shows (#4283). A mount
791
+ // under a hold still enters it: the content memo it reads is born held,
792
+ // with no committed value to serve.
793
+ // (Verdict readers and lanes exist only with their engines installed.)
794
+ const n = observeFlightHook && e.l & CONFIG_VERDICT;
795
+ const o = laneStageHook && passLane !== null;
796
+ if ((!o || e.m & STATUS_UNINITIALIZED && t.m & STATUS_PENDING) && !n && (
797
+ // CONFIG_FRAME, spelled as at the effect's creation.
798
+ !(e.l & EFFECT_RENDER << 23) || t.l & CONFIG_INPUTS_PUBLISHED)) return false;
799
+ const E = txOf(t);
800
+ // Lane work and verdict readers are never the transaction's pass, whichever
801
+ // flush runs it: a node T holds re-enters T when it re-runs (`recompute`'s
802
+ // head), and the pass may then read a guess and become the lane's.
803
+ if (E === flushTransaction && !o && !n || bornInto(e, E)) return false;
804
+ staleReader(e, E);
805
+ // Lane work or a verdict reader no longer waits on `el`'s flight (lanes.ts
806
+ // `laneServed`, A15).
807
+ if (o || n) laneServedHook(e, t);
808
+ return true;
809
+ }
810
+
811
+ /** A node born into transaction `t`: held by it and never committed (A29,
812
+ * ruling A) — uninitialized (born held), or a member of the frame one of its
813
+ * held passes built (CONFIG_HELD_FRAME). Its branch exists only in `t`: it
814
+ * has no screen to read, so it reads `t`'s values, whichever flush runs it,
815
+ * and keeps staging (#4010). */ function bornInto(e, t) {
816
+ return (e.m & STATUS_UNINITIALIZED || e.l & CONFIG_HELD_FRAME) && e.l & CONFIG_HELD && txOf(e) === t;
817
+ }
818
+
819
+ /** The computed whose pass a creation under `owner` belongs to (a root's is
820
+ * its nearest computed ancestor's), or null outside every pass. */ function creatorPass(e) {
821
+ return e !== null && e.Qe ? e.Ke : e;
822
+ }
823
+
824
+ /** What `born` and the passes still running around `p` read — its
825
+ * creator's, theirs — of the future or a staging (REACTIVE_JOINED |
826
+ * REACTIVE_STAGED_READ): a pass created by one that did has no screen; its
827
+ * branch exists only in that world (#4341). */ function creatorReads(e, t) {
828
+ return (e = creatorPass(e._parent))?.R & REACTIVE_RECOMPUTING_DEPS ? creatorReads(e, t | e.R) : t & (REACTIVE_JOINED | REACTIVE_STAGED_READ);
829
+ }
830
+
831
+ /** L2 — park the previous pass's frame for this node's commit: the children
832
+ * (already allocated — nothing new) and the `_disposal` list move to the
833
+ * extension, keep reacting from the heap, and die when the commit publishes
834
+ * the frame that replaces them (`commitPendingNode`) or when the owner does. */ function parkChildren(e) {
835
+ markDisposal(e);
836
+ const t = ext(e);
837
+ t.Le = e.ke;
838
+ t.me = e.pe;
839
+ e.ke = null;
840
+ e.pe = null;
841
+ e.be = 0;
842
+ }
843
+
448
844
  /**
449
845
  * Build an Effect node with all effect-specific fields baked into a single object literal,
450
846
  * so V8 sees the full hidden class shape at construction time. Effects always run in lazy
451
847
  * mode (recompute is called explicitly by `effect()`), so we hardcode the lazy bits and skip
452
848
  * the auto-dispose CONFIG bit (effect() previously cleared it post-construction).
453
- */ function createEffectNode(e, t, n, i, u, l) {
454
- const o = l?.transparent ?? false;
455
- const s = {
456
- id: inheritId(l, o, context),
457
- T: (o ? CONFIG_TRANSPARENT : 0) | (l?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (l?.sync ? CONFIG_SYNC : 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
458
- Ue: false,
459
- he: null,
460
- C: context?.C ?? globalQueue,
461
- we: context?.we ?? defaultContext,
462
- Me: 0,
463
- Se: e,
464
- be: undefined,
465
- Le: 0,
466
- ot: undefined,
467
- st: null,
468
- nt: null,
469
- Ye: null,
470
- Ze: 0,
471
- u: null,
472
- rt: null,
473
- ve: context,
474
- Ve: null,
475
- ct: null,
849
+ */ function createEffectNode(e, t, n, o, E) {
850
+ const s = E?.transparent ?? false;
851
+ // Prod and observe boilerplates — see computed() for why the observe tier
852
+ // gets its `_name` as a literal slot rather than a write after the fact.
853
+ // The default label is the node kind (tracked effects relabel their computed
854
+ // in trackedEffect); the wrappers in signals.ts no longer spread a name into
855
+ // the options to get it.
856
+ const i = {
857
+ id: inheritId(E, s, context),
858
+ l:
859
+ // CONFIG_FRAME for a render effect (EFFECT_RENDER = 1; a user
860
+ // effect's 1 << 24 is unused).
861
+ o << 23 | (s ? CONFIG_TRANSPARENT : 0) | (E?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (E?.sync ? CONFIG_SYNC : 0) | (E?.xe ?? 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
862
+ ye: false,
476
863
  ke: null,
477
- ie: REACTIVE_LAZY,
478
- S: STATUS_UNINITIALIZED,
479
- Te: clock,
480
- Pe: NOT_PENDING,
481
- _e: null,
482
- _t: -1,
483
- Ie: false,
484
- Xe: false,
485
- Tt: undefined,
486
- dt: t,
487
- St: n,
488
- At: undefined,
489
- Re: i,
490
- o: null
864
+ A: context?.A ?? defaultContext,
865
+ be: 0,
866
+ X: e,
867
+ le: undefined,
868
+ he: 0,
869
+ ve: undefined,
870
+ We: null,
871
+ C: null,
872
+ _: null,
873
+ de: 0,
874
+ o: null,
875
+ Ze: null,
876
+ _parent: context,
877
+ Ge: null,
878
+ we: null,
879
+ pe: null,
880
+ R: REACTIVE_LAZY,
881
+ m: STATUS_UNINITIALIZED,
882
+ ie: clock,
883
+ ue: NOT_PENDING,
884
+ Me: -1,
885
+ ce: false,
886
+ Ae: false,
887
+ ge: undefined,
888
+ qe: t,
889
+ Je: n,
890
+ ze: undefined,
891
+ i: o,
892
+ T: null
491
893
  };
492
- // Boundary effects carry a status channel; most effects never touch _x.
493
- if (u !== undefined) ext(s).A = u;
494
- if (l?.unobserved) ext(s).ft = l.unobserved;
495
- setupComputedNode(s, lazyOptions);
496
- return s;
894
+ // Effects dispatch status through the SHARED notifier (statusNotifierOf,
895
+ // keyed off _type) — storing it per node forced a full NodeExtension
896
+ // allocation on EVERY effect at creation (an alloc + 19 field stores,
897
+ // +23% effect creation, caught by the creation benches). Only genuinely
898
+ // per-node channels (boundaries) live on _x.
899
+ if (E?.unobserved) ext(i).Ye = E.unobserved;
900
+ setupComputedNode(i, lazyOptions);
901
+ return i;
902
+ }
903
+
904
+ /**
905
+ * The shared status notifier for effect nodes, installed once by effect.ts
906
+ * at module evaluation (`this`-dispatched — one function serves every
907
+ * effect, so nodes never store it). CARVE 4: the boundaries' per-node
908
+ * channel (`_x._notifyStatus`) went with them; effects are the only
909
+ * display consumers.
910
+ */ let effectStatusNotify = null;
911
+
912
+ function setEffectStatusNotify(e) {
913
+ effectStatusNotify = e;
914
+ }
915
+
916
+ /** Resolve a node's status notifier: effect nodes (`_type` — EFFECT_PURE is
917
+ * 0, and only effect literals carry the field) get the shared notifier.
918
+ * Presence doubles as the "display consumer" membership test in the status
919
+ * walks, exactly as the per-node field did when every effect carried one. */ function statusNotifierOf(e) {
920
+ return e.i ? effectStatusNotify : null;
497
921
  }
498
922
 
499
923
  const lazyOptions = {
@@ -501,79 +925,87 @@ const lazyOptions = {
501
925
  };
502
926
 
503
927
  function setupComputedNode(e, t) {
504
- e.st = e;
505
- const n = context?.Ct ? context.Ot : context;
506
- if (context) {
507
- const t = context.ke;
508
- if (t === null) {
509
- context.ke = e;
510
- } else {
511
- e.Ve = t;
512
- t.ct = e;
513
- context.ke = e;
514
- }
515
- }
516
- if (n) e.Le = n.Le + 1;
517
- if (GlobalQueue.Rt !== null) GlobalQueue.Rt(e);
928
+ e.We = e;
929
+ const n = context?.Qe ? context.Ke : context;
930
+ if (context) linkChild(context, e);
931
+ if (n) e.he = n.he + 1;
932
+ wireExternalSourceHook?.(e);
518
933
  !t?.lazy && recompute(e, true);
519
934
  if (snapshotCaptureActive && !t?.lazy) {
520
- if (!(e.S & STATUS_PENDING) && !(e.T & CONFIG_NO_SNAPSHOT)) {
521
- ext(e).Qe = e.be === undefined ? NO_SNAPSHOT : e.be;
522
- e.T |= CONFIG_HAS_SNAPSHOT;
523
- snapshotSources.add(e);
524
- }
935
+ if (!(e.m & STATUS_PENDING) && !(e.l & CONFIG_NO_SNAPSHOT)) takeSnapshot(e, e.le);
525
936
  }
526
937
  }
527
938
 
528
- function signal(e, t, n = null) {
529
- const i = {
530
- Ue: t?.equals != null ? t.equals : isEqual,
531
- T: (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (t?.H ? CONFIG_NO_SNAPSHOT : 0),
532
- be: e,
533
- u: null,
534
- rt: null,
535
- Te: clock,
536
- lt: n,
537
- ce: n?.o?.l || null,
538
- Pe: NOT_PENDING,
939
+ function signal(e, t) {
940
+ // Prod and observe boilerplates — see computed(). The observe literal adds
941
+ // `_name`, `_loc` and `_owner` (the creating owner, stamped by registerGraph for
942
+ // createSignal nodes so ownerPath can locate signal subjects; null here,
943
+ // and staying null on internal signals — one shape either way).
944
+ const n = {
945
+ ye: t?.equals ?? isEqual,
946
+ l: (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (t?.ae ? CONFIG_NO_SNAPSHOT : 0),
947
+ le: e,
948
+ o: null,
949
+ Ze: null,
950
+ ie: clock,
951
+ ue: NOT_PENDING,
539
952
  // Signal-literal diet (§12e): NO _time/_fn/_statusFlags slots. Stores
540
953
  // materialize one signal per touched leaf, so signal bytes are store
541
954
  // bytes. _time is write-only on signals (every read site is computed-
542
955
  // typed error-retry gating); _fn/_statusFlags read falsy-identically as
543
956
  // missing properties on the shared paths (undefined masks to 0).
544
- _e: null,
545
- _t: -1,
546
- o: null
957
+ Me: -1,
958
+ T: null
547
959
  };
548
- if (t?.unobserved) ext(i).ft = t.unobserved;
549
- if (n) {
550
- ext(n).l = i;
551
- n.T |= CONFIG_FW_CHILDREN;
552
- }
553
- if (snapshotCaptureActive && !(i.T & CONFIG_NO_SNAPSHOT) && !((n?.S ?? 0) & STATUS_PENDING)) {
554
- ext(i).Qe = e === undefined ? NO_SNAPSHOT : e;
555
- i.T |= CONFIG_HAS_SNAPSHOT;
556
- snapshotSources.add(i);
557
- }
558
- return i;
960
+ if (t?.unobserved) ext(n).Ye = t.unobserved;
961
+ if (snapshotCaptureActive && !(n.l & CONFIG_NO_SNAPSHOT)) takeSnapshot(n, e);
962
+ return n;
559
963
  }
560
964
 
561
- function optimisticSignal(e, t) {
562
- const n = signal(e, t);
563
- ext(n).De = NOT_PENDING;
564
- n.T |= CONFIG_OPTIMISTIC;
565
- return n;
965
+ function isEqual(e, t) {
966
+ return e === t;
566
967
  }
567
968
 
568
- function optimisticComputed(e, t) {
569
- const n = computed(e, t);
570
- ext(n).De = NOT_PENDING;
571
- n.T |= CONFIG_OPTIMISTIC;
572
- return n;
969
+ /** A store slot node (CONFIG_SLOT_NODE): the whole node in ONE literal — the
970
+ * target and key as back-refs in place of closures (`equals` is a method
971
+ * call, `this` the node; the unobserved sweep dispatches to one shared hook,
972
+ * graph.ts), the store's wrap cache (`px`/`pxv`: the proxy last served for
973
+ * this key and the raw it wrapped — one pointer compare replaces a WeakMap
974
+ * lookup per read) and the accessor verdict (`acc`) pre-shaped. No options
975
+ * object, no NodeExtension, no post-construction expandos. `ownedWrite` is
976
+ * baked in: the setter carries the owned-scope guard; node-level writes are
977
+ * the store's notification machinery. The observe literal adds the label
978
+ * and location slots (the store relabels `store.<key>` and stamps the
979
+ * store's `loc` when the engine is installed). */ function slotSignal(e, t, n, o, E) {
980
+ const s = {
981
+ ye: t,
982
+ l: CONFIG_OWNED_WRITE | CONFIG_SLOT_NODE,
983
+ le: e,
984
+ o: null,
985
+ Ze: null,
986
+ ie: clock,
987
+ ue: NOT_PENDING,
988
+ Me: -1,
989
+ T: null,
990
+ O: n,
991
+ Xe: o,
992
+ acc: E,
993
+ px: undefined,
994
+ pxv: undefined
995
+ };
996
+ return s;
573
997
  }
574
998
 
575
- function isEqual(e, t) {
576
- return e === t;
999
+ /**
1000
+ * When set to a component name string, any reactive read that is not inside a nested tracking
1001
+ * scope will log a dev-mode warning. Managed automatically by `untrack(fn, strictReadLabel)`.
1002
+ */
1003
+ /** The verdict windows' read (verdict.ts), installed while a `latest` or
1004
+ * `isPending` window is open and null otherwise: `read` dispatches to it
1005
+ * then, and a program that never imports them never has one. */ let verdict = null;
1006
+
1007
+ function setVerdict(e) {
1008
+ verdict = e;
577
1009
  }
578
1010
 
579
1011
  /**
@@ -583,6 +1015,16 @@ function isEqual(e, t) {
583
1015
  * Use `untrack` inside a memo or effect when you need to read a signal once
584
1016
  * without making the surrounding computation depend on its future changes.
585
1017
  *
1018
+ * `untrack` stops dependency tracking, not async settlement. Reading an
1019
+ * uninitialized async source still suspends the owning computation; it runs
1020
+ * again when that source first resolves. Afterward, the untracked read is a
1021
+ * snapshot and does not subscribe the computation to later changes.
1022
+ *
1023
+ * If the first read must return immediately, give the async source a
1024
+ * `loadingValue` before reading it with `untrack`. `isPending` and `latest`
1025
+ * inspect updates to an existing value; neither supplies a missing initial
1026
+ * value.
1027
+ *
586
1028
  * Pass a `strictReadLabel` string to enable a dev-mode warning: any reactive
587
1029
  * read inside `fn` that isn't inside a nested tracking scope will log a
588
1030
  * warning naming the label.
@@ -596,246 +1038,560 @@ function isEqual(e, t) {
596
1038
  * log(snapshot);
597
1039
  * }
598
1040
  * );
1041
+ *
1042
+ * const color = createMemo<string>(() => fetchColor(), { loadingValue: "gray" });
1043
+ * const initialColor = untrack(color); // "gray" while the fetch is unresolved
599
1044
  * ```
600
1045
  */ function untrack(e, t) {
601
- if (GlobalQueue.Gt === null && !tracking && true) return e();
602
1046
  const n = tracking;
603
1047
  tracking = false;
604
1048
  try {
605
- if (GlobalQueue.Gt !== null) return GlobalQueue.Gt(e);
1049
+ if (externalUntrackHook) return externalUntrackHook(e);
606
1050
  return e();
607
1051
  } finally {
608
1052
  tracking = n;
609
1053
  }
610
1054
  }
611
1055
 
1056
+ /**
1057
+ * Set while runtime bookkeeping reads a node inside another node's pass (a
1058
+ * loading boundary priming its tree at creation, from whatever pass is
1059
+ * mounting it). `context` is that node, but the read is nobody's: the value
1060
+ * is probed, never derived from, so nothing the read would normally record
1061
+ * on `context` may be recorded — not the untracked-pending re-run link
1062
+ * (`read`, `!tracking`; #3528: a boundary's `on` key read this way from
1063
+ * `notify` linked the key's source into an unrelated async memo, a cycle that
1064
+ * never converged), and not a transaction entry (`enterStagedRead`; #3540: a
1065
+ * born-held tree would otherwise pull the mounting pass into the hold).
1066
+ */ let spectating = false;
1067
+
612
1068
  /**
613
1069
  * Bring a computed to a readable state: lazy/disposed nodes are (re)computed;
614
- * an isPending() probe (`refresh`) additionally pulls the node fully up to
615
- * date so its status flags reflect the current graph.
1070
+ * `refresh` additionally pulls the node fully up to date so its status flags
1071
+ * reflect the current graph.
616
1072
  */ function prepareComputed(e, t) {
617
- if (e.ie & REACTIVE_LAZY) {
618
- e.ie &= ~REACTIVE_LAZY;
1073
+ if (e.R & REACTIVE_LAZY) {
1074
+ e.R &= ~REACTIVE_LAZY;
619
1075
  recompute(e, true);
620
- } else if (e.ie & REACTIVE_DISPOSED) {
1076
+ } else if (e.R & REACTIVE_DISPOSED) {
621
1077
  // Two disposal lifecycles share the flag (#3024). Observation-lifecycle
622
1078
  // nodes (CONFIG_AUTO_DISPOSE) are dormant — torn down by unobserved()
623
1079
  // when the last subscriber left — and reads reawaken them; that is the
624
1080
  // pay-for-use contract. Owner-lifecycle nodes are dead: recomputing would
625
1081
  // re-run user code in a torn-down tree (and discard manual writes on
626
1082
  // derived-writable signals), so reads return the last committed value.
627
- if (e.T & CONFIG_AUTO_DISPOSE) recompute(e, true);
628
- } else if (t) {
629
- updateIfNecessary(e);
630
- }
1083
+ if (e.l & CONFIG_AUTO_DISPOSE) {
1084
+ const t = e._parent;
1085
+ if (t !== null) {
1086
+ // A dormant node was off the chain when its owner died, so the strip
1087
+ // in disposeChildren missed it: freeze here instead (#3024).
1088
+ if (t.R & REACTIVE_DISPOSED) {
1089
+ e.l &= ~CONFIG_AUTO_DISPOSE;
1090
+ return;
1091
+ }
1092
+ // A zombie never left its chain (the parked frame is drained whole,
1093
+ // and its members skip the splice): relinking would put it on the
1094
+ // live chain too.
1095
+ if (!(e.R & REACTIVE_ZOMBIE)) linkChild(t, e);
1096
+ }
1097
+ recompute(e, true);
1098
+ }
1099
+ } else ;
631
1100
  }
632
1101
 
1102
+ // CARVE 3: until()'s authoritative-view wakeup (notifyAuthoritativeObservers /
1103
+ // installAuthoritativeRead), the stale-of-foreign clause (heldFromStale,
1104
+ // recordStaleReplay, ownsHold) and the transaction entry on a staged read
1105
+ // (enterStagedRead, stagedEntry / born held, underFreshLoadingBoundary) went
1106
+ // with the transactions.
633
1107
  /**
634
- * Sentinel returned by readNodeFast when the plain-signal fast path does not
635
- * apply and the caller must fall back to the full read().
636
- */ const READ_SLOW = Symbol("read-slow");
1108
+ * Rule 1 (value selection): does this reader see a STAGED node's COMMITTED
1109
+ * value? One implementation of the rule the fast paths (read's fast block)
1110
+ * carry as their trivial ternary. In order:
1111
+ * - no reader at all (an untracked read) — the committed frame;
1112
+ * - nothing staged;
1113
+ * - a children-forbidden reader (createTrackedEffect / onSettled: the frame,
1114
+ * never the graph — A32).
1115
+ * False means the reader derives from the staged value.
1116
+ */ function readerSeesCommitted(e, t) {
1117
+ return !t || e.ue === NOT_PENDING || t.l & CONFIG_CHILDREN_FORBIDDEN;
1118
+ }
637
1119
 
638
- /**
639
- * read()'s plain-signal fast path as a standalone entry for hot callers
640
- * (store traps). Safe to substitute for read() only because the bail
641
- * conditions mirror read()'s prelude and fast-path guard exactly: the
642
- * latestRead and pendingCheck windows run side-effectful hooks before the
643
- * fast path, `_fn` nodes need prepareComputed, and firewall / override /
644
- * snapshot / transition / lane / dev-strictRead state all take the full
645
- * resolution. Anything slow returns READ_SLOW; the caller then calls read().
646
- */ function readNodeFast(e) {
647
- if (latestReadActive || pendingCheckActive || e.Se || e.lt || e.o?.De !== undefined || e.o?.Qe !== undefined || activeTransition !== null || currentOptimisticLane !== null || snapshotCaptureActive || false) return READ_SLOW;
648
- let t = context;
649
- if (t?.Ct) t = t.Ot;
650
- if (t && tracking) link(e, t);
651
- // Children-forbidden readers (createTrackedEffect / onSettled callbacks) get
652
- // committed visibility: like the effect half of createEffect and event
653
- // handlers, effect-phase code never observes its own unsettled write — the
654
- // write lands in the same flush's continuation (#3006).
655
- return !t || e.Pe === NOT_PENDING || t.T & CONFIG_CHILDREN_FORBIDDEN ? e.be : e.Pe;
1120
+ /** A28 — set when a node is staged (queuePendingNode) OUTSIDE a flush;
1121
+ * cleared when the next flush begins. The read sites test this one module
1122
+ * boolean instead of `globalQueue._running`: inside a flush it is false and
1123
+ * the A28 arm costs nothing; outside, only a tick with unflushed writes pays
1124
+ * the staged-node check. */ let unflushedStaged = false;
1125
+
1126
+ function markUnflushedStaged() {
1127
+ unflushedStaged = true;
1128
+ }
1129
+
1130
+ /** A28 — a write becomes visible at flush. Outside a flush, a node holding a
1131
+ * staged value was written since the last flush: staging commits at flush
1132
+ * end, so nothing else leaves a node in this state. Inside a flush the rule
1133
+ * does not apply (A28 (4): promoted within the round). The value an
1134
+ * unflushed node serves is its committed value, or NOT_PENDING when nothing
1135
+ * is unflushed. Exempt: promoted writes (A28 (4): a write issued inside a
1136
+ * recompute is promoted at that recompute's end — boundary and loading
1137
+ * machinery, signals declared for in-computation writes). */ function unflushedValue(e) {
1138
+ if (globalQueue.Y || e.ue === NOT_PENDING || e.l & CONFIG_PROMOTED) return NOT_PENDING;
1139
+ // A held node (L2) carries a staged value between flushes too, but it was
1140
+ // flushed — and parked. Not an unflushed write — unless rewritten since:
1141
+ // then the flushed staging is what the world sees until the next flush.
1142
+ if (e.l & CONFIG_HELD) return e.T.Be === clock ? e.T.je : NOT_PENDING;
1143
+ return e.le;
1144
+ }
1145
+
1146
+ /** A28: a held node rewritten outside a flush keeps the staging the last
1147
+ * flush left (`_flushed`) until the flush that carries the rewrite — the
1148
+ * stash is stamped with the clock, which that flush advances. */ function stashFlushed(e) {
1149
+ const t = e.T;
1150
+ if (t.Be !== clock) {
1151
+ t.je = e.ue;
1152
+ t.Be = clock;
1153
+ unflushedStaged = true;
1154
+ }
1155
+ }
1156
+
1157
+ /** Nodes written inside a creation-time recompute (CONFIG_PROMOTED). */ const promotedWrites = [];
1158
+
1159
+ /** A derivation served the committed value because of an unflushed write
1160
+ * (A28) must run again in the flush that carries it — the late-linker case
1161
+ * (#3337's reason to defer the walk): it linked after the write walked. */ function markLateLinker(e) {
1162
+ // The pass's own tail re-enqueues on this latch (recompute's finally) —
1163
+ // a direct enqueue here would be wiped by the pass's flag reset.
1164
+ e.R |= REACTIVE_MISSED_WAKE;
1165
+ return true;
1166
+ }
1167
+
1168
+ function resyncUnflushedCompanions() {
1169
+ unflushedStaged = false;
1170
+ // Length-guarded: the common flush has nothing here and allocates nothing.
1171
+ if (promotedWrites.length !== 0) {
1172
+ for (const e of promotedWrites) e.l &= ~CONFIG_PROMOTED;
1173
+ promotedWrites.length = 0;
1174
+ }
1175
+ }
1176
+
1177
+ /** A tracked read's pull of a computed: bring it current when the flush has
1178
+ * reached its height (or the reader is a fresh-pull waiter), and sit the
1179
+ * reader above it. */ function pullComputed(e, t) {
1180
+ if (e.he >= dirtyQueue.Fe) {
1181
+ markNode(t);
1182
+ markHeap(dirtyQueue);
1183
+ updateIfNecessary(e);
1184
+ }
1185
+ // Fresh-pull readers (awaitable refresh's waiter) recompute a dirty
1186
+ // source inline even when the height gate defers to the flush: the
1187
+ // waiter must park on the re-ask's window (or serve its sync answer),
1188
+ // never read the PRE-re-ask value as settled. Self-guarded: a clean
1189
+ // node no-ops and updateIfNecessary refuses disposed nodes (#2983) —
1190
+ // a dead target serves its last value, which is already quiescent.
1191
+ else if (t.l & CONFIG_FRESH_READ) updateIfNecessary(e);
1192
+ const n = e.he;
1193
+ // parent check is shallow, might need to be recursive
1194
+ if (n >= t.he && e._parent !== t) t.he = n + 1;
656
1195
  }
657
1196
 
658
1197
  function read(e) {
659
- // Handle latest() mode: read from _latestValueComputed
660
- // Checked before isPending so that isPending(() => latest(x)) checks
661
- // the _pendingSignal of _latestValueComputed (async in flight) rather
662
- // than the original node (which stays "pending" while held in a transition).
663
- if (latestReadActive) return GlobalQueue.Pt(e);
664
1198
  let t = context;
665
- if (t?.Ct) t = t.Ot;
1199
+ if (t?.Qe) t = t.Ke;
666
1200
  const n = e;
667
- const i = e.lt;
668
- const u = i || e;
669
- // Handle isPending() mode: collect pending state while preserving normal read semantics.
670
- // Probe mode is suspended while preparing the node so nested reads during a
671
- // recompute don't collect into the probe.
672
- if (pendingCheckActive) {
673
- GlobalQueue.Dt(e, t, u, i);
674
- } else if (typeof n.Se === "function") {
675
- prepareComputed(e, false);
676
- }
677
- if (!n.Se && u === e && e.o?.De === undefined && e.o?.Qe === undefined && activeTransition === null && currentOptimisticLane === null && !snapshotCaptureActive && true) {
678
- if (t && tracking) link(e, t);
679
- // Committed visibility for children-forbidden readers — see readNodeFast.
680
- return !t || e.Pe === NOT_PENDING || t.T & CONFIG_CHILDREN_FORBIDDEN ? e.be : e.Pe;
681
- }
682
- if (t && tracking) {
683
- link(e, t, pendingCheckActive);
684
- if (u.Se) {
685
- const n = queueFor(e);
686
- if (u.Le >= n.xe) {
687
- markNode(t);
688
- markHeap(n);
689
- updateIfNecessary(u);
1201
+ // CARVE 1: `owner` was `el._firewall || el` — a store leaf's status,
1202
+ // height and error were read off its projection computed. (Typed loosely
1203
+ // as the `||` expression was: a signal's `_statusFlags`/`_height` read as
1204
+ // undefined and mask to 0 on every site below.)
1205
+ const o = e;
1206
+ // The verdict windows (`latest`, `isPending`; verdict.ts): one cold path,
1207
+ // off this one, present only while a window is open. It wakes a lazy or
1208
+ // dormant memo itself, outside the window (#4366).
1209
+ if (verdict !== null) return verdict(e, t);
1210
+ if (typeof n.X === "function") prepareComputed(e);
1211
+ if (!n.X && e.T?.De === undefined && !snapshotCaptureActive && (!unflushedStaged || e.ue === NOT_PENDING) && // A28, see readNodeFast
1212
+ !(e.l & CONFIG_OVERRIDE) && // lanes: the slow path's one arm
1213
+ true) {
1214
+ if (t) {
1215
+ if (tracking) link(e, t);
1216
+ // L2: a pass that reads something already in the future joins it —
1217
+ // the one mechanism behind "a write that reaches the future joins
1218
+ // it". Tracked or not (`untrack` is about dependencies, not about
1219
+ // which world a pass derives from — posture-store-parity S4/S5): a
1220
+ // derivation of a held write published mainline would tear. Frame
1221
+ // readers (children-forbidden, A32) see committed below and stay
1222
+ // out; a render effect outside a parking flush sees committed and
1223
+ // stays out too (rule 3, `frameRead`). The pass remembers it read the
1224
+ // future (REACTIVE_JOINED): a first pass that did is born held (A29),
1225
+ // wherever it was created. Lane work sees the screen (`frameRead`).
1226
+ if (e.l & CONFIG_HELD && !(t.l & CONFIG_CHILDREN_FORBIDDEN)) {
1227
+ if (frameRead(t, e)) return e.le;
1228
+ joinPass(t, e);
690
1229
  }
691
- const i = u.Le;
692
- // parent check is shallow, might need to be recursive
693
- if (i >= t.Le && e.ve !== t) {
694
- t.Le = i + 1;
1230
+ }
1231
+ // Committed visibility for untracked and children-forbidden readers.
1232
+ if (!t || e.ue === NOT_PENDING || t.l & CONFIG_CHILDREN_FORBIDDEN) return e.le;
1233
+ // (A plain signal is never uninitialized — the carve-out is `serve`'s.)
1234
+ // Lane work over a folded lane (D-34), once the frame has a
1235
+ // transaction, reads the screen too.
1236
+ if ((observeFlightHook && t.l & CONFIG_VERDICT || laneStageHook && passLane?.folded) && stagedScreen(t)) return e.le;
1237
+ stagedRead(t);
1238
+ return e.ue;
1239
+ }
1240
+ // Rule 3: this read serves the committed value to a render effect outside a
1241
+ // parking flush (decided after the pull below — a pull can join).
1242
+ let E = false;
1243
+ if (t) {
1244
+ if (tracking) {
1245
+ link(e, t);
1246
+ if (o.X) {
1247
+ pullComputed(o, t);
1248
+ // F-6 (#4050): a memo over a verdict reader's value derives from a
1249
+ // display-ahead read — a verdict reader from this pass's end, as if
1250
+ // it probed inline: it reads held values as the screen and shows the
1251
+ // verdict now. Not an effect: a render effect is a frame reader
1252
+ // already, and a user effect's posture is its own reads'.
1253
+ // (A verdict memo exists only with the verdict engine installed.)
1254
+ if (observeFlightHook && !t.i) t.R |= o.l & CONFIG_VERDICT;
695
1255
  }
696
1256
  }
1257
+ // L2, as in the fast block above (tracked or not). A node born into the
1258
+ // future (uninitialized) has no committed value: every reader derives
1259
+ // from the future there, a render effect included (A29: a stale reader
1260
+ // of it cannot fall back to the committed frame and enters instead).
1261
+ if (e.l & CONFIG_HELD && !(e.l & CONFIG_OVERRIDE) && !(t.l & CONFIG_CHILDREN_FORBIDDEN)) {
1262
+ if (o.m & STATUS_UNINITIALIZED || !frameRead(t, e)) joinPass(t, e); else E = true;
1263
+ }
1264
+ }
1265
+ // Lanes: a lane's node (after the pull — the node is current). NOT_PENDING
1266
+ // falls through: lane work's read of a pending member throws like any.
1267
+ // (CONFIG_OVERRIDE is the lane engine's: no engine, no lane's node.)
1268
+ if (laneReadHook && e.l & CONFIG_OVERRIDE) {
1269
+ const n = laneReadHook(t, e);
1270
+ if (n !== NOT_PENDING) return n;
1271
+ }
1272
+ // A reader in a snapshot scope (a hydration claim) is served the value the
1273
+ // server rendered with: a client write's recompute still in flight does
1274
+ // not pull the claim off the markup it adopts — the reader replays at the
1275
+ // scope's release. An errored node throws below as for any reader.
1276
+ if (snapshotCaptureActive && t && t.l & CONFIG_IN_SNAPSHOT_SCOPE) {
1277
+ const n = e.T?.De;
1278
+ if (n !== undefined && !(o.m & STATUS_ERROR)) {
1279
+ const o = n === NO_SNAPSHOT ? undefined : n;
1280
+ const E = e.ue !== NOT_PENDING ? e.ue : e.le;
1281
+ if (E !== o) t.R |= REACTIVE_SNAPSHOT_STALE;
1282
+ return o;
1283
+ }
697
1284
  }
698
- if (u.S & STATUS_PENDING) {
699
- if (t && !(stale && u._e && activeTransition !== u._e)) {
700
- // Per-lane suspension lives with the engine (a non-null lane implies it
701
- // is installed): under a lane, only same-lane pending async without an
702
- // active override throws.
703
- if (currentOptimisticLane === null || GlobalQueue.ht(u)) {
704
- if (!tracking && e !== t) link(e, t);
705
- throw u.o?._;
1285
+ // A stale reader of a held flight (rule 3, A15's reveal corollary) is served
1286
+ // the committed value — coherent with the frame, whose inputs are the
1287
+ // committed ones too — and does not go pending on it. So is lane work
1288
+ // reading a flight that is not the lane's own (the parent's, or
1289
+ // mainline's): a lane breaks out of its parent's hold and waits only on
1290
+ // what derives from the guess (A31: "under a lane, a pending node on no
1291
+ // lane serves its committed value").
1292
+ // A verdict reader likewise (CONFIG_VERDICT): a frame reader sees the
1293
+ // screen, and for a flight the screen is the committed value — a render
1294
+ // effect keeps its DOM by throwing, a memo has no DOM and is handed the
1295
+ // value. `[isPending(x), x()]` reads `[true, stale]` in either order (A10)
1296
+ // — unless the flight committed beneath inputs already on screen
1297
+ // (`observeFlight`), or the reader was born into the flight's transaction
1298
+ // (#4010): it has no screen, and waits on the flight with its frame.
1299
+ // (A lane, and a verdict reader, exist only with their engines installed —
1300
+ // the verdict engine imports the lane engine: without it the test folds.)
1301
+ if (laneStageHook && o.m & STATUS_PENDING && !E && !(e.l & CONFIG_OVERRIDE) && !(o.m & STATUS_UNINITIALIZED)) {
1302
+ // A reader with no committed value yet sees the flight pending: a
1303
+ // boundary a lane mounts shows its fallback (A29's boundary exemption).
1304
+ if (passLane !== null && !(t?.m & STATUS_UNINITIALIZED)) E = true; else if (t !== null && t.l & CONFIG_VERDICT) E = observeFlightHook(t, o);
1305
+ }
1306
+ if (o.m & STATUS_PENDING && !E) {
1307
+ // A reader landing on a pending node throws; an untracked read of an
1308
+ // UNINITIALIZED node has no committed value to serve and throws too. A
1309
+ // children-forbidden reader (createTrackedEffect / onSettled) sees the
1310
+ // frame, not the graph (A32): the committed value, and the NotReady only
1311
+ // where there is none (uninitialized).
1312
+ if (t) {
1313
+ if (!(t.l & CONFIG_CHILDREN_FORBIDDEN) || o.m & STATUS_UNINITIALIZED) {
1314
+ // An untracked read of a pending node still re-runs its reader when
1315
+ // the node settles — unless nobody is reading (`spectating`, #3528)
1316
+ // or the reader is the node itself.
1317
+ if (!tracking && !spectating && e !== t) link(e, t);
1318
+ throw o.T?.U;
706
1319
  }
707
- } else if (t && u.S & STATUS_UNINITIALIZED) {
708
- // A stale (render) reader of a node held pending in ANOTHER transition
709
- // normally keeps showing the node's committed value instead of
710
- // entangling the two transactions — but an uninitialized node has no
711
- // committed value to show. Suspend on it (firewall-backed store reads
712
- // always took this branch; plain memos now do too): the reader
713
- // registers as a reporter of that source, and its pending-node stamp
714
- // ties it to the active transaction, so the two transactions merge
715
- // when the source settles. Falling through served `undefined` as if
716
- // settled and stranded the reader outside both transactions, so it
717
- // never re-ran when either landed (#3043 port).
718
- if (!tracking && e !== t) link(e, t);
719
- throw u.o?._;
720
- } else if (!t && u.S & STATUS_UNINITIALIZED) {
721
- throw u.o?._;
1320
+ } else if (o.m & STATUS_UNINITIALIZED) {
1321
+ throw o.T?.U;
722
1322
  }
723
1323
  }
724
- // `owner` is the computed itself, or the firewall behind a store node —
725
- // firewall-backed reads follow the same rules (memo parity, #2897 ruling):
726
- // an errored derive throws for every late reader instead of silently
727
- // serving node values (the seed, or last-good data after a failed refetch).
728
- if (u.Se && u.S & STATUS_ERROR) {
1324
+ // An errored derive throws for every late reader instead of silently
1325
+ // serving node values (memo parity, #2897 ruling).
1326
+ if (o.X && o.m & STATUS_ERROR) {
729
1327
  // Only a genuine reactive re-read may retry an errored async source:
730
1328
  // - tracking: owned/tracked scope only (never events / `untrack` / effect side-effect phase)
731
- // - !pendingCheckActive: an `isPending` probe observes the error, never refetches
732
1329
  // - owner._time < clock: only on a later cycle than the one the error was found
733
- if (tracking && !pendingCheckActive && u.Te < clock) {
734
- recompute(u);
1330
+ if (tracking && o.ie < clock) {
1331
+ recompute(o);
735
1332
  return read(e);
736
- } else throw u.o?._;
737
- }
738
- if (snapshotCaptureActive && t && t.T & CONFIG_IN_SNAPSHOT_SCOPE) {
739
- const n = e.o?.Qe;
740
- if (n !== undefined) {
741
- const i = n === NO_SNAPSHOT ? undefined : n;
742
- const u = e.Pe !== NOT_PENDING ? e.Pe : e.be;
743
- if (u !== i) t.ie |= REACTIVE_SNAPSHOT_STALE;
744
- return i;
745
1333
  }
1334
+ // D-41: a staged error replacing the shown one is this flush's answer
1335
+ // (`stageError`): a reader served the staging (`serve`) throws it, as it
1336
+ // would read a staged value, and stages its own error with it; one
1337
+ // served the screen throws the shown error. Thrown the shown error, a
1338
+ // reader born this flush published it, and its readers' stale error
1339
+ // committed after the new one (#4404).
1340
+ if (!E && e.l & CONFIG_STAGED_ERROR) serve(e, t);
1341
+ throw o.T?.U;
1342
+ }
1343
+ // D-41: an untracked read reads the screen, and so does a frame reader
1344
+ // served the committed value — `serve` hands it that value as it does an
1345
+ // untracked read — a shown error included (`throwShown`).
1346
+ if (E || !t) throwShown(e);
1347
+ const s = serve(e, E ? null : t);
1348
+ if (!t && typeof n.X === "function" && e.l & CONFIG_AUTO_DISPOSE && !(o.m & STATUS_PENDING) && !e.o) {
1349
+ // Deferred, not inline (#3078): an inline unobserved() here made untracked
1350
+ // reads destructive — dispose on this read, full revival recompute on the
1351
+ // next — so consecutive reads could answer differently with no write in
1352
+ // between.
1353
+ // The sweep at flush finalization re-validates and reclaims; schedule()
1354
+ // guarantees that flush happens even if nothing else is queued.
1355
+ dormantNodes.add(e);
1356
+ schedule();
1357
+ }
1358
+ return s;
1359
+ }
1360
+
1361
+ /** D-41: a reader served the screen — an untracked read, a frame reader
1362
+ * served the committed value, a verdict reader (`serve`) — reads a shown
1363
+ * error there until the answer that replaces it commits. */ function throwShown(e) {
1364
+ if (e.T?.Te) throw e.T.Te;
1365
+ }
1366
+
1367
+ /**
1368
+ * Rule 1, the one slow implementation (DESIGN-CONSOLIDATION move 3b, step
1369
+ * 6c): the value a reader `c` (null = untracked, no pass) is served from
1370
+ * `el`. Called by read()'s slow tail; the fast path (read's fast block) keeps
1371
+ * its trivial ternary by design (perf, see the doc). Arms, in order:
1372
+ * - a node born staged has nothing for an untracked reader (A19 exception 1);
1373
+ * - an unflushed write serves committed and re-runs the reader in the
1374
+ * carrying flush (A28);
1375
+ * - readerSeesCommitted, else the staged value.
1376
+ */ function serve(e, t) {
1377
+ // A node born staged (recompute) has a staged value and no committed one:
1378
+ // an untracked reader has nothing to serve and holds (A19 exception 1) — a
1379
+ // bookkeeping read (`spectate`) likewise, and a children-forbidden reader
1380
+ // (A32: the frame, which has nothing here).
1381
+ if (e.ue !== NOT_PENDING && e.m & STATUS_UNINITIALIZED && (!t || spectating || t.l & CONFIG_CHILDREN_FORBIDDEN)) throw new NotReadyError(null);
1382
+ const n = t && unflushedStaged ? unflushedValue(e) : NOT_PENDING;
1383
+ if (n !== NOT_PENDING) {
1384
+ markLateLinker(t);
1385
+ return n;
1386
+ }
1387
+ if (readerSeesCommitted(e, t) || observeFlightHook && t.l & CONFIG_VERDICT && !(e.m & STATUS_UNINITIALIZED) && stagedScreen(t) && !throwShown(e)) return e.le;
1388
+ stagedRead(t, e);
1389
+ // D-41: the staged answer is an error (`stageError`).
1390
+ if (e.l & CONFIG_STAGED_ERROR) throw e.ue;
1391
+ return e.ue;
1392
+ }
1393
+
1394
+ /** A pass read a staging of this flush: it derives from what the flush may
1395
+ * yet hold (REACTIVE_STAGED_READ — the seam decides). Lane work too (plan sec. 28, a
1396
+ * lane sees the screen plus its own guesses) — a folded lane's only before
1397
+ * the frame has a transaction (after, it reads the screen like a verdict
1398
+ * reader, `stagedScreen` — a list pass never pairs a held length with the
1399
+ * guess's rows, D-34): the staging is the screen if
1400
+ * the frame commits — one pass, the common case — and a held write if it
1401
+ * parks, which the seam repairs (`stagedReaders`): the pass re-derives on
1402
+ * the committed world and its lane's runs wait that round, so the held
1403
+ * write never shows through the lane. A verdict lane's work likewise: the
1404
+ * lane holds verdicts, not the frame's other stagings (#3851) — except a
1405
+ * verdict reader, which answered for itself (the lane seam, lanes.ts). Not
1406
+ * a read of a node born staged at all: it has no committed value to
1407
+ * re-derive on, and a re-run would read the same staging and re-queue every
1408
+ * round (#3969: nor does it make a later lane read a re-derive, `enterLane`). */ function stagedRead(e, t) {
1409
+ if (!(t?.m & STATUS_UNINITIALIZED)) {
1410
+ e.R |= REACTIVE_STAGED_READ;
1411
+ // A verdict reader's too when it is an effect, before the frame has a
1412
+ // transaction (after, `stagedScreen` serves it the screen): the staging
1413
+ // is the frame's read, and the pass re-derives once as a verdict reader
1414
+ // if the flush parks (D-22; D-25: the probe decides the posture in either
1415
+ // read order). A memo's is the frame's derivation either way: a memo is
1416
+ // not a leaf (`frameRead`).
1417
+ if (laneSeamsHook && (passLane !== null || e.i && e.l & CONFIG_VERDICT)) stagedReaders.push(e);
1418
+ }
1419
+ }
1420
+
1421
+ /** A10 for a staged node: a verdict reader (the pass entered a window) that
1422
+ * also reads a frame's proposal plainly sees the screen — `[latest(q), q()]`
1423
+ * is `[b, a]` — once the frame has a transaction, and re-derives when it
1424
+ * lands: at this very seam if nothing holds it (the stale run is void,
1425
+ * #3322 — one run, on the committed world), or with the hold. Before any
1426
+ * transaction the answer is the seam's and the reader watches it
1427
+ * (verdict.ts). A node born into the future has no screen: its staging is
1428
+ * its only value (A29). Nor has a reader born into the flush's transaction
1429
+ * (`bornInto`, #4010): its branch exists only there. */ function stagedScreen(e) {
1430
+ if (flushTransaction === null || bornInto(e, flushTransaction)) return false;
1431
+ // A transaction only a `latest()` read opened holds nothing: the staging
1432
+ // is the frame's read for a pass in the frame's seat, as it is before the
1433
+ // frame has one, and the seam re-derives the pass once only if the flush
1434
+ // parks (D-22, F-10). Lane work sees the screen (D-37). Not a pass with no
1435
+ // value yet: its first answer is nobody's hold (D-6) and would commit the
1436
+ // staging whether or not the frame parks — it reads the screen, and runs
1437
+ // again at the landing.
1438
+ if (observeFlightHook && flushTransaction === latestOpened && passLane === null && e.l & CONFIG_VERDICT && !(e.m & STATUS_UNINITIALIZED)) return !stagedReaders.push(e);
1439
+ staleReader(e, flushTransaction);
1440
+ return true;
1441
+ }
1442
+
1443
+ /** A28 (4) — written inside a recompute that runs OUTSIDE a flush (a
1444
+ * creation-time compute): promoted at that pass's end, visible to the rest of
1445
+ * the block. Cold: only contextual writes reach here. */ function notePromotedWrite(e) {
1446
+ if (globalQueue.Y || e.l & CONFIG_PROMOTED) return;
1447
+ e.l |= CONFIG_PROMOTED;
1448
+ promotedWrites.push(e);
1449
+ }
1450
+
1451
+ /** Cold half of setSignal's snapshot arm (see there), shared with a store
1452
+ * container's structural notify: first write during capture to a plain
1453
+ * signal without a live snapshot records the pre-write value. Only plain
1454
+ * user signals qualify. Computeds reach setSignal from an
1455
+ * async landing (asyncWrite), and a value arriving from async during the pass
1456
+ * REVEALS — the creation-time arm skips pending computeds for the same
1457
+ * reason. Firewall leaves belong to a projection whose compute is the tree's
1458
+ * own work, captured (or deliberately not) at creation. */ function captureWriteSnapshot(e, t) {
1459
+ if (e.l & CONFIG_NO_SNAPSHOT || e.X !== undefined || e.T?.De !== undefined) return;
1460
+ takeSnapshot(e, t);
1461
+ }
1462
+
1463
+ function takeSnapshot(e, t) {
1464
+ ext(e).De = t === undefined ? NO_SNAPSHOT : t;
1465
+ e.l |= CONFIG_HAS_SNAPSHOT;
1466
+ snapshotSources.add(e);
1467
+ }
1468
+
1469
+ /**
1470
+ * A callback run before every signal or store write lands, while set, with
1471
+ * the written signal or the store proxy whose setter runs. The
1472
+ * server-components client sets it while slot occurrences are left
1473
+ * unmounted after hydration (deferred hydration): a write may be one an
1474
+ * unmounted component would have observed, so the client mounts what is
1475
+ * left before the write applies, and clears the hook. `null` otherwise.
1476
+ */ let writeHook = null;
1477
+
1478
+ /** @internal */ function setWriteHook(e) {
1479
+ writeHook = e;
1480
+ }
1481
+
1482
+ /**
1483
+ * `el`'s comparator verdict (`_equals`), the one guarded call for recompute,
1484
+ * setSignal (user setters and async landings) and the seam's
1485
+ * staged-equals-committed check.
1486
+ * - A comparator that read an unresolved async source has not shown the
1487
+ * values equal (#4111): falsy, a change, whose readers suspend in their own
1488
+ * tracked context.
1489
+ * - Any other throw is an error of `el`'s computation (A3, #2837): routed
1490
+ * through the status path so error boundaries contain it, instead of
1491
+ * unwinding a flush or a setter; answered "same" so nothing commits.
1492
+ * @internal
1493
+ */ function isSame(e, t, n) {
1494
+ if (e.ye === isEqual) return t === n;
1495
+ try {
1496
+ return e.ye && e.ye(t, n);
1497
+ } catch (t) {
1498
+ if (!(t instanceof NotReadyError)) return notifyStatus(e, STATUS_ERROR, t), true;
746
1499
  }
747
- if (e.o?.De !== undefined && e.o?.De !== NOT_PENDING) {
748
- // A17: the override IS the value for every reader.
749
- return unwrapOverride(e.o?.De);
750
- }
751
- // Entanglement gate: a reader recomputing under an optimistic lane that reads
752
- // a pending mid-transition write sees the committed value. Projection-store
753
- // manual writes use the firewall's manual-write flag to opt into this path.
754
- // Async drivers are not under an optimistic lane and so bypass this, reading
755
- // _pendingValue for correct fetching. The sub is recorded for replay at commit
756
- // so it re-runs with the new committed view. (Gate details live with the
757
- // engine — a non-null lane implies it is installed.)
758
- if (currentOptimisticLane !== null && activeTransition !== null && t !== null && GlobalQueue.Ft(e, u, t)) {
759
- return e.be;
760
- }
761
- // In optimistic lane context, return _value for optimistic/lane-assigned signals
762
- // and for regular signals in stale mode (render effects). Non-stale readers (user
763
- // effects) see _pendingValue so that latest() and direct reads stay consistent.
764
- // (The lane-context clause lives with the engine.) Children-forbidden readers
765
- // (createTrackedEffect / onSettled callbacks) get committed visibility — see
766
- // readNodeFast (#3006).
767
- const l = !t || currentOptimisticLane !== null && GlobalQueue.gt(e, u, t) || e.Pe === NOT_PENDING || t.T & CONFIG_CHILDREN_FORBIDDEN || stale && e._e && activeTransition !== e._e ? e.be : e.Pe;
768
- // Record that this isPending() probe observed the fresh pending value, so
769
- // the probe doesn't pair "pending" with the new value (#2831).
770
- if (pendingCheckActive) GlobalQueue.Ht(e, l);
771
- if (!t && u === e && typeof n.Se === "function" && e.T & CONFIG_AUTO_DISPOSE && !(u.S & STATUS_PENDING) && !e.u) {
772
- unobserved(e);
773
- }
774
- return l;
775
1500
  }
776
1501
 
777
1502
  function setSignal(e, t) {
778
- if (e._e && activeTransition !== e._e) globalQueue.initTransition(e._e);
779
- // The optimistic write path lives with the engine: only optimisticSignal /
780
- // optimisticComputed callers and optimistic store nodes carry an
781
- // _overrideValue slot (flagged by CONFIG_OPTIMISTIC — a masked read of the
782
- // always-present config instead of a missing-property probe), and every
783
- // module that installs one installs the engine first.
784
- if (e.T & CONFIG_OPTIMISTIC && !projectionWriteActive) return GlobalQueue.vt(e, t);
785
- const n = e.Pe === NOT_PENDING ? e.be : e.Pe;
1503
+ if (writeHook !== null) writeHook(e);
1504
+ const n = e.ue === NOT_PENDING ? e.le : e.ue;
786
1505
  if (typeof t === "function") t = t(n);
1506
+ // Lanes: a write landing on a guess is its truth (A18 — an async guess's
1507
+ // own landing), judged against the guess; one landing on a lane's
1508
+ // derivation (its flight) is the lane's staging (lanes.ts).
1509
+ if (laneWriteHook && e.l & CONFIG_OVERRIDE) return laneWriteHook(e, t);
1510
+ // L2 (T4) / A34 (1): a write to a node whose staged state is held is a
1511
+ // second proposal for the slot — the same value again or another — and
1512
+ // the two cannot finish at different times: the writer's tick joins the
1513
+ // hold, before the equality gate.
1514
+ if (e.l & CONFIG_HELD) joinFuture(txOf(e));
787
1515
  // Uninitialized check first: the first commit has no previous value, so the
788
1516
  // user comparator must not run against `undefined` (matches recompute).
789
- const i = !!(e.S & STATUS_UNINITIALIZED) || !e.Ue || !e.Ue(n, t);
790
- if (!i) return t;
791
- const u = e.Pe !== NOT_PENDING;
792
- if (!u) queuePendingNode(e);
793
- e.Pe = t;
794
- // syncCompanions only pokes _pendingSignal/_latestValueComputed — with
795
- // neither companion present the call is a guaranteed no-op (companions are
796
- // only ever created, never removed, and creating one installs the hook and
797
- // sets CONFIG_HAS_COMPANIONS — one masked read replaces two optional-field
798
- // probes on every write).
799
- e.T & CONFIG_HAS_COMPANIONS && GlobalQueue.Oe !== null && GlobalQueue.Oe(e, t);
1517
+ // D-41: a write over a shown error is a change whatever the last good
1518
+ // value was — the error leaves at its commit (`commitPendingNode`).
1519
+ const o = !!(e.m & STATUS_UNINITIALIZED) || !!e.T?.Te || !isSame(e, n, t);
1520
+ if (!o) return t;
1521
+ // A write during hydration's snapshot capture to a source that has no
1522
+ // snapshot — created BEFORE capture began (module-level state: an identity
1523
+ // minted from onSettled in the pass, a preference read from storage) —
1524
+ // captures the pre-write value now, so the write is held like any other:
1525
+ // in-scope readers keep serving what the server rendered with and replay
1526
+ // at release. Left uncaptured, the write cascades live through a claim
1527
+ // pass whose DOM writes are skipped, and a component rendered later in the
1528
+ // pass reads a value the server never had. Store leaves written here are
1529
+ // plain signals and qualify the same way.
1530
+ if (snapshotCaptureActive) captureWriteSnapshot(e, n);
1531
+ const E = e.ue !== NOT_PENDING;
1532
+ if (!E) queuePendingNode(e);
1533
+ // A28: a held node rewritten outside a flush keeps its flushed staging
1534
+ // for every reader until the flush that carries the rewrite.
1535
+ else if (e.l & CONFIG_HELD && !globalQueue.Y) stashFlushed(e);
1536
+ e.ue = t;
1537
+ if (context !== null) notePromotedWrite(e);
800
1538
  // _time is a computed-only slot (§12e): writing it on a signal would fork
801
1539
  // the lean shape. Every read site is computed-typed.
802
- if (e.Se !== undefined) e.Te = clock;
1540
+ if (e.X !== undefined) e.ie = clock;
803
1541
  // Staged-rewrite fast path (§12d): a re-write to a node whose subscribers
804
1542
  // were already walked — and where nothing has recomputed or linked since
805
1543
  // (epoch) — re-stages the value and stops. The walk is idempotent (subs
806
- // marked, heap entries flag-guarded, effects queued once); lane and reask
807
- // contexts change what a walk MEANS, so they always walk.
808
- if (u && e._t === notifyEpoch && currentOptimisticLane === null && !reaskArmed) return t;
1544
+ // marked, heap entries flag-guarded, effects queued once).
1545
+ if (E && e.Me === notifyEpoch) return t;
809
1546
  insertSubs(e);
810
1547
  schedule();
811
1548
  return t;
812
1549
  }
813
1550
 
814
1551
  /**
815
- * Suppresses automatic recomputation of `el` until the scheduler drains. Used
816
- * when a manual write should win over dependency changes queued in the same
817
- * tick. The MANUAL_WRITE flag is cleared by the pending-node drain; projection
818
- * computeds don't commit values, but they still need the same end-of-tick
819
- * cleanup point.
820
- */ function suppressComputedRecompute(e) {
821
- deleteFromHeap(e, queueFor(e));
822
- if (!(e.ie & REACTIVE_MANUAL_WRITE) && e.Pe === NOT_PENDING) {
823
- queuePendingNode(e);
824
- schedule();
825
- }
826
- e.ie = e.ie & -4 | REACTIVE_MANUAL_WRITE;
827
- e.kt = clock;
828
- }
829
-
830
- /**
831
- * User-facing setter for the memo form of `createSignal(fn)`. Behaves like
832
- * `setSignal`, but also cancels any pending recompute of the memo so the
833
- * manual value wins over a value that would otherwise be produced by an
834
- * upstream change in the same tick.
1552
+ * User-facing setter for the memo form of `createSignal(fn)`. A write applies
1553
+ * first, then derivations re-run (A34 as amended 2026-10-01, rule B; core
1554
+ * R31): the write stages like any other, and a source change — in this flush
1555
+ * or a later one, under a hold or on mainline — re-derives with the staged
1556
+ * write as `prev`. The write never refuses the re-run (the frame-scoped mask
1557
+ * of #3733/#3740 and #2692's "write wins the tick" before it are gone; call
1558
+ * order inside a flush was never observable) and on its own never causes one.
1559
+ * REACTIVE_MANUAL_WRITE only marks the staging as a user proposal — the A34
1560
+ * (1)/(3) discriminator — until a pass re-derives it or the commit lands it.
835
1561
  */ function setMemo(e, t) {
836
- const n = setSignal(e, t);
837
- suppressComputedRecompute(e);
838
- return n;
1562
+ // A34 (3), #3612: is `el`'s staging a derivation another transaction holds
1563
+ // — held, not a manual proposal, and the writer not that transaction
1564
+ // (mainline, or a flush joined elsewhere)? A held pass result is nobody's
1565
+ // proposal: a setter reaching it composes on the committed frame it was
1566
+ // written against and becomes `prev` for the hold's re-derivation, instead
1567
+ // of replacing it. A write made under the hold carries the mark and keeps
1568
+ // (1)'s last-write-wins. Only the user setter asks; an async landing
1569
+ // writing a held node is the transaction's own work.
1570
+ const n = e.l & CONFIG_HELD && !(e.R & REACTIVE_MANUAL_WRITE) && txOf(e) !== flushTransaction;
1571
+ // The writer read the committed frame; its updater composes on it.
1572
+ if (n && typeof t === "function") {
1573
+ const n = t(e.le);
1574
+ t = () => n;
1575
+ }
1576
+ // #4110: the write replaces a failed derivation's outcome. It lands without
1577
+ // re-running `_fn`, and nothing else clears STATUS_ERROR, so without this
1578
+ // every later read keeps throwing the error the derivation left behind.
1579
+ // The error stays the display until the write commits (D-41).
1580
+ if (e.m & STATUS_ERROR) {
1581
+ e.T.Te = e.T.U;
1582
+ notifyStatus(e, STATUS_NONE);
1583
+ }
1584
+ t = setSignal(e, t);
1585
+ // A write replaces an error a pass staged (D-41): it is a value again.
1586
+ e.l &= ~CONFIG_STAGED_ERROR;
1587
+ if (n) {
1588
+ // The one write that re-runs: the staging it replaced was derived from
1589
+ // inputs the writer never saw, so the hold re-derives over the write.
1590
+ e.R = e.R & ~REACTIVE_CHECK | REACTIVE_DIRTY;
1591
+ insertIntoHeap(e, dirtyQueue);
1592
+ schedule();
1593
+ } else if (e.ue !== NOT_PENDING) e.R |= REACTIVE_MANUAL_WRITE;
1594
+ return t;
839
1595
  }
840
1596
 
841
1597
  /**
@@ -858,79 +1614,41 @@ function setSignal(e, t) {
858
1614
  * ```
859
1615
  */ function runWithOwner(e, t) {
860
1616
  const n = context;
861
- const i = tracking;
1617
+ const o = tracking;
862
1618
  context = e;
863
1619
  tracking = false;
864
1620
  try {
865
1621
  return t();
866
1622
  } finally {
867
1623
  context = n;
868
- tracking = i;
1624
+ tracking = o;
869
1625
  }
870
1626
  }
871
1627
 
872
1628
  function staleValues(e, t = true) {
873
- const n = stale;
874
- stale = t;
875
1629
  try {
876
1630
  return e();
877
- } finally {
878
- stale = n;
879
- }
1631
+ } finally {}
880
1632
  }
881
1633
 
882
1634
  /**
883
- * Invalidates one reactive source, forcing it to re-execute even if its inputs
884
- * haven't changed.
885
- *
886
- * Pass either a Solid-created accessor or a projected store created from
887
- * `createStore(fn, ...)` / `createProjection(...)`. `refresh()` is a
888
- * write-like invalidation operation: it does not read the target's value, and
889
- * refreshing a plain signal accessor is a no-op.
890
- *
891
- * Use it to invalidate cached async values (e.g. force a re-fetch) without
892
- * tearing the consumer down.
893
- *
894
- * @example
895
- * ```ts
896
- * const user = createMemo(async () => fetch(`/users/${id()}`).then(r => r.json()));
897
- *
898
- * // Re-fetch on demand
899
- * <button onClick={() => refresh(user)}>Reload</button>
900
- * ```
901
- */ function refresh(e) {
902
- const t = e?.[$REFRESH];
903
- if (!t) {
904
- return;
905
- }
906
- if (typeof t.Se === "function" && !(t.ie & REACTIVE_DISPOSED)) {
907
- if (t.ie & REACTIVE_MANUAL_WRITE) {
908
- // A manual write in the CURRENT tick wins over the refresh (#2692).
909
- // A mask stamped in an earlier tick only survives because a
910
- // transaction (action) is holding the pending drain open; there the
911
- // refresh is a later, explicit re-ask and lifts the mask — otherwise
912
- // any setStore early in an action silently swallows every refresh()
913
- // for the rest of the transaction (#3026).
914
- if (t.kt === clock) return;
915
- t.ie &= ~REACTIVE_MANUAL_WRITE;
916
- // No REASK below: the batch carries a manual value change, so the
917
- // recompute is not a quiet re-ask of an unchanged question.
918
- }
919
- // A refresh with no value-change dirt already queued is a re-ask of the
920
- // same question: mark it so the recompute classifies any resulting
921
- // pending window as quiet (not pending). If the node is already dirty
922
- // from a real input change, the question changed — don't mark.
923
- // REACTIVE_IN_HEAP counts as dirt: insertSubs schedules subscribers by
924
- // heap insertion alone (no DIRTY/CHECK flag), so a same-batch value
925
- // change followed by refresh() must not be laundered into a quiet re-ask.
926
- else if (!(t.ie & (REACTIVE_DIRTY | REACTIVE_CHECK | REACTIVE_IN_HEAP))) {
927
- t.ie |= REACTIVE_REASK;
928
- armReaskClear();
929
- }
930
- t.ie = t.ie & ~REACTIVE_CHECK | REACTIVE_DIRTY;
931
- insertIntoHeap(t, queueFor(t));
1635
+ * Core marking half of `refresh()` (the public wrapper lives in signals.ts —
1636
+ * it validates the target, marks through here, then builds the quiescence
1637
+ * promise on the resolve()/until() effect machinery). Flags the node's next
1638
+ * recompute as a quiet re-ask and schedules it; no-ops for non-derived or
1639
+ * disposed targets.
1640
+ */ function markRefresh(e) {
1641
+ if (typeof e.X === "function" && !(e.R & REACTIVE_DISPOSED)) {
1642
+ // A manual write never refuses the re-run (A34 rule B): a refresh in the
1643
+ // write's own frame re-asks with the write as `prev`, like any source
1644
+ // change (#3026 generalized).
1645
+ // The re-ask of an answered question (A19 exc. 2): the next pass, going
1646
+ // pending, classifies its flight quiet for the verdict. Not when an
1647
+ // input already changed this tick — that pass asks a new question.
1648
+ e.R = e.R & ~REACTIVE_CHECK | REACTIVE_DIRTY | (e.R & (REACTIVE_DIRTY | REACTIVE_CHECK | REACTIVE_IN_HEAP) ? 0 : REACTIVE_REASK);
1649
+ insertIntoHeap(e, dirtyQueue);
932
1650
  schedule();
933
1651
  }
934
1652
  }
935
1653
 
936
- export { READ_SLOW, clearSnapshots, computed, context, createEffectNode, currentOptimisticLane, ext, isEqual, latestReadActive, markSnapshotScope, optimisticComputed, optimisticSignal, pendingCheckActive, prepareComputed, read, readNodeFast, recompute, refresh, releaseSnapshotScope, runWithOwner, setContextInternal, setLatestReadActive, setMemo, setPendingCheckActive, setSignal, setSnapshotCapture, signal, snapshotCaptureActive, snapshotSources, stale, staleValues, suppressComputedRecompute, tracking, untrack };
1654
+ export { bornInto, captureWriteSnapshot, clearSnapshots, computed, context, createEffectNode, creatorPass, creatorReads, effectStatusNotify, ext, isEqual, isSame, joinPass, joinPassAt, markLateLinker, markRefresh, markSnapshotScope, markUnflushedStaged, notePromotedWrite, prepareComputed, pullComputed, read, readerSeesCommitted, recompute, releaseSnapshotScope, resyncUnflushedCompanions, runWithOwner, serve, setEffectStatusNotify, setMemo, setSignal, setSnapshotCapture, setVerdict, setWriteHook, signal, slotSignal, snapshotCaptureActive, snapshotSources, spectating, stagedRead, stagedScreen, staleValues, stashFlushed, statusNotifierOf, tracking, unflushedStaged, unflushedValue, untrack, verdict, writeHook };