@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
@@ -0,0 +1,1157 @@
1
+ import { EFFECT_RENDER, EFFECT_USER, CONFIG_HELD, CONFIG_OVERRIDE, NOT_PENDING, STATUS_UNINITIALIZED, STATUS_PENDING, CONFIG_STAGED, REACTIVE_DISPOSED, REACTIVE_ZOMBIE, CONFIG_VERDICT, CONFIG_HAS_SNAPSHOT, REACTIVE_RECOMPUTING_DEPS, REACTIVE_MISSED_WAKE, CONFIG_IN_SNAPSHOT_SCOPE, REACTIVE_SNAPSHOT_STALE, CONFIG_HELD_FRAME, REACTIVE_LANE_DIRTY, CONFIG_STAGED_ERROR, CONFIG_INPUTS_PUBLISHED, STATUS_ERROR, EFFECT_TRACKED, REACTIVE_MANUAL_WRITE, CONFIG_GUESS, REACTIVE_FRAME_READ, CONFIG_REDERIVE, CONFIG_FRAME } from "./constants.js";
2
+
3
+ import { attrHooks } from "./attribution-hooks.js";
4
+
5
+ import { notifyStatus } from "./async.js";
6
+
7
+ import { resyncUnflushedCompanions, isSame, recompute, markUnflushedStaged, ext } from "./core.js";
8
+
9
+ import { DEV, GRAPH_SIZE_WARN_AT, noteFanOut } from "./dev.js";
10
+
11
+ import { sweepDormant, trimStaleDeps } from "./graph.js";
12
+
13
+ import { runHeap, enqueueSub, deleteFromHeap } from "./heap.js";
14
+
15
+ import { devCheckQuiescent } from "./invariants.js";
16
+
17
+ import { rethrowCleanupError } from "./owner.js";
18
+
19
+ // Optional-module hooks: module bindings, not `GlobalQueue` statics, so a
20
+ // bundle that never imports the installing module never assigns them — its
21
+ // minifier folds every guarded call site (and the branch behind it) away.
22
+ // Each module installs its own set at evaluation (pay-for-use).
23
+ // Lanes (lanes.ts; installed when `createOptimistic` — or verdict.ts — is
24
+ // imported; every call site is behind a bit or a `passLane` nothing else sets).
25
+ let laneReadHook = null;
26
+
27
+ let laneStageHook = null;
28
+
29
+ let laneOutcomeHook = null;
30
+
31
+ let laneWriteHook = null;
32
+
33
+ let applyGuessesHook = null;
34
+
35
+ let laneSeamsHook = null;
36
+
37
+ let laneCorrectionsHook = null;
38
+
39
+ let endLanesHook = null;
40
+
41
+ let lanesBlockedHook = null;
42
+
43
+ /** R5 (lanes.ts `laneSeams` parks a frame flight): the landing's re-derive
44
+ * of a reader adopts the flight instead (true: adopted, nothing to queue). */ let laneAdoptHook = null;
45
+
46
+ /** core.ts `frameRead` served lane work or a verdict reader a held node's
47
+ * committed value (lanes.ts `laneServed`). */ let laneServedHook = null;
48
+
49
+ /** A guess was written (lanes.ts `optimisticWrite`): the arms only a guess
50
+ * reaches (CONFIG_GUESS) — a build without `createOptimistic` or
51
+ * `createOptimisticStore` never sets it, and its bundler folds them. */ let guessing = false;
52
+
53
+ function armGuessing() {
54
+ guessing = true;
55
+ }
56
+
57
+ function installLaneHooks(e, o, n, t, l, s, u, r, i, a, f) {
58
+ laneReadHook = e;
59
+ laneStageHook = o;
60
+ laneOutcomeHook = n;
61
+ laneWriteHook = t;
62
+ applyGuessesHook = l;
63
+ laneSeamsHook = s;
64
+ laneCorrectionsHook = u;
65
+ endLanesHook = r;
66
+ lanesBlockedHook = i;
67
+ laneAdoptHook = a;
68
+ laneServedHook = f;
69
+ }
70
+
71
+ // Boundaries (boundaries.ts): the display consumers between an observer
72
+ // and the root. `catch` — status from a frame reader, nearest boundary
73
+ // first (true: caught, the root never hears of it; a clear — flags 0 —
74
+ // settles the reader there); `fresh` — a pass that read a hold (true: a
75
+ // first pass a loading boundary that has not shown content caught,
76
+ // `joinPass`; `held`: the flush's hold owns it, nothing catches it);
77
+ // `hidden` — a frame reader behind a fallback (the boundary showing it)
78
+ // is not on screen and holds nothing; `boundarySeam` — the seam's sweep (readers
79
+ // gone or settled without a pass reveal; an `on` re-arm resolves);
80
+ // `heldRun` — a queued run under a fallback-showing boundary waits for
81
+ // the reveal (true: held; the boundary re-queues it by type). The
82
+ // synchronous first render on creation builds the subtree, attached or
83
+ // not; its updates and the user effects wait.
84
+ let catchHook = null;
85
+
86
+ let freshHook = null;
87
+
88
+ let hiddenHook = null;
89
+
90
+ let boundarySeamHook = null;
91
+
92
+ let heldRunHook = null;
93
+
94
+ function installBoundaryHooks(e, o, n, t, l) {
95
+ catchHook = e;
96
+ freshHook = o;
97
+ hiddenHook = n;
98
+ boundarySeamHook = t;
99
+ heldRunHook = l;
100
+ }
101
+
102
+ /** Store (store/store.ts): `park` — the flush parked into `t`: the store
103
+ * materializes and holds the container node of every staging that had none
104
+ * (a staging needs the node as its home only when something can read the
105
+ * frame through it; a hold can); `commit` — fold the pending backings whose
106
+ * container nodes this flush committed (the owned-raw model's one mutation
107
+ * point). */ let storeParkHook = null;
108
+
109
+ let storeCommitHook = null;
110
+
111
+ function installStoreHooks(e, o) {
112
+ storeParkHook = e;
113
+ storeCommitHook = o;
114
+ }
115
+
116
+ /** `affects()` marks (affects.ts): the releases at a landing and at the seam
117
+ * (ambient marks). */ let releaseMarksHook = null;
118
+
119
+ let releaseAmbientMarksHook = null;
120
+
121
+ function installMarkHooks(e, o) {
122
+ releaseMarksHook = e;
123
+ releaseAmbientMarksHook = o;
124
+ }
125
+
126
+ /** Verdicts (verdict.ts). */ let observeFlightHook = null;
127
+
128
+ /** D-25's hold work (verdict.ts `holdWork`): on `source` going pending,
129
+ * whether verdict reader `n` waits on it instead of re-deriving. */ let holdWorkHook = null;
130
+
131
+ function installVerdictHooks(e, o) {
132
+ observeFlightHook = e;
133
+ holdWorkHook = o;
134
+ }
135
+
136
+ /** External-source bridge (wired by enableExternalSource(); undefined while
137
+ * no config is active — including after _resetExternalSourceConfig()). */ let wireExternalSourceHook = undefined;
138
+
139
+ let externalUntrackHook = undefined;
140
+
141
+ function setExternalHooks(e, o) {
142
+ wireExternalSourceHook = e;
143
+ externalUntrackHook = o;
144
+ }
145
+
146
+ /** Live transactions — opened, unmerged, not landed. Scanned at every seam
147
+ * while non-empty; nothing on the plain path. */ const transactions = [];
148
+
149
+ /** Observe tier: this flush parked its transaction (`holdStart` fired). */ let holding = false;
150
+
151
+ /** A transaction (listed here) or a lane (lanes.ts lists its own). */ function newTransaction(e, o = null) {
152
+ const n = {
153
+ Ye: [],
154
+ rn: [ [], [] ],
155
+ En: [],
156
+ eo: null,
157
+ _parent: o,
158
+ V: e,
159
+ it: null,
160
+ tt: 0,
161
+ nt: false,
162
+ nn: null,
163
+ Nt: false,
164
+ ln: false,
165
+ I: null
166
+ };
167
+ if (!e) transactions.push(n);
168
+ return n;
169
+ }
170
+
171
+ // Lanes (plan sec. 28, 2026-10-02; lanes.ts). A lane is "a new base of a transition":
172
+ // the sub-frame an optimistic write opens in the transition of the frame it
173
+ // is made in. It sees the screen plus its own guesses (plan sec. 19), breaks out of
174
+ // the parent's hold — its effects run now — and holds itself if its own
175
+ // derivations hit async. It ends when the parent lands: a guess reverts to
176
+ // the truth it covered, or lands as the truth that superseded it. A frame
177
+ // that does not park has nothing to be optimistic over: the write is void.
178
+
179
+ // The seat of a pass is its node's: a node carrying a lane's value runs as
180
+ // the lane's whoever dirtied it; a derivation's tracked read of a lane's
181
+ // value moves its pass into the lane; a leaf's never does (it reads the
182
+ // screen). Lane work's values go to the lane slot, its runs to the lane's
183
+ // queues; the rest of the frame is the flush's.
184
+ /** The lane the running pass is work of; set at `recompute`'s head from the
185
+ * node, moved by a derivation's lane read, saved and restored around the
186
+ * pass. */ let passLane = null;
187
+
188
+ function setPassLane(e) {
189
+ passLane = e;
190
+ }
191
+
192
+ /** The transaction this flush parks into: set by the first join, merged
193
+ * with every further one. Consumed at the seam. A join outside a flush (a
194
+ * mount's pending observer, a write to a held node, an action opening)
195
+ * schedules the seam that consumes it: membership is the tick's — the same
196
+ * synchronous frame — and the next tick's writes are not this one's. */ let flushTransaction = null;
197
+
198
+ /** The flush's transaction while only a `latest()` read of a staging opened
199
+ * it (verdict.ts `provisionalVerdict`): it holds nothing, and a verdict
200
+ * reader's plain read of a staging in it is the frame's read (D-22). */ let latestOpened = null;
201
+
202
+ /** The transaction the passes running OUTSIDE a flush this tick derive from
203
+ * (a mainline mount, a mainline re-pass): the passes' own (A29, creation-
204
+ * time form — "the entry is the pass's alone"): their results are staged
205
+ * into it, and nothing else in the tick is — a write made after the mount
206
+ * is a mainline write, a render effect mounted beside it a stale reader.
207
+ * Inside a flush, or in a tick that already has its transaction (an
208
+ * action's body), the frame joins instead (`flushTransaction`) — except a
209
+ * first pass a loading boundary that has not shown content catches (`own`,
210
+ * A29's boundary exemption, #3540): it is the boundary's, not the tick's,
211
+ * and a flush that has joined nothing keeps it pass-scoped as outside one.
212
+ * Cleared by the flush the join schedules, and at the end of a flush that
213
+ * set it. */ let passTx = null;
214
+
215
+ function joinPassTx(e, o) {
216
+ if (globalQueue.Y && !o || flushTransaction !== null) return joinFuture(e);
217
+ if (passTx === null) passTx = resolveTx(e); else merge(resolveTx(e), passTx);
218
+ schedule();
219
+ }
220
+
221
+ /** The transaction holding a node (CONFIG_HELD set), resolved through
222
+ * merges; the path is compressed. */ function txOf(e) {
223
+ return e.T.Je = resolveTx(e.T.Je);
224
+ }
225
+
226
+ /** `t` resolved through merges while it is still parked or open; null once
227
+ * it has landed. */ function liveTx(e) {
228
+ e = resolveTx(e);
229
+ return transactions.indexOf(e) !== -1 ? e : null;
230
+ }
231
+
232
+ function resolveTx(e) {
233
+ while (e.eo !== null) e = e.eo;
234
+ return e;
235
+ }
236
+
237
+ /** The flush joins transaction `t` — or needs one of its own (`null`: a pass
238
+ * went pending with nothing held). Joining a second one merges it into the
239
+ * flush's: the work that touched both entangles them (A15). */ function joinFuture(e) {
240
+ const o = flushTransaction;
241
+ if (o === null) {
242
+ flushTransaction = e === null ? newTransaction(false) : resolveTx(e);
243
+ } else if (e !== null) merge(resolveTx(e), o);
244
+ if (!globalQueue.Y) schedule();
245
+ }
246
+
247
+ /** `t` merges into `f`: the work that touched both entangles them (A15).
248
+ * A lane never merges: it is a sub-frame, not a peer — its work is routed
249
+ * per pass (`passLane`). */ function merge(e, o) {
250
+ // Both ends resolved: a stale pointer to a transaction already merged
251
+ // into the other would close a cycle `resolveTx` never leaves.
252
+ e = resolveTx(e);
253
+ o = resolveTx(o);
254
+ if (e === o || e.V || o.V) return;
255
+ if (attrHooks !== null) attrHooks.transitionMerged(o, e);
256
+ e.eo = o;
257
+ o.tt += e.tt;
258
+ o.nt ||= e.nt;
259
+ append(o.Ye, e.Ye);
260
+ append(o.rn[0], e.rn[0]);
261
+ append(o.rn[1], e.rn[1]);
262
+ append(o.En, e.En);
263
+ if (e.I !== null) append(o.I ??= [], e.I);
264
+ transactions.splice(transactions.indexOf(e), 1);
265
+ }
266
+
267
+ /** A15: a hold is a property of the async node — observed pending by a
268
+ * render reader. A transaction is blocked while a node it holds, in flight,
269
+ * has one: a pending render effect (the frame itself), or a pending held
270
+ * node a render effect reads. A stale reader served the committed value
271
+ * never went pending, but derives from the flight all the same (#3494) and
272
+ * is still linked to it; a memo between them is pending, and held, too — so
273
+ * one hop of `_subs` is the chain. Reads of the reader's LAST pass only (the
274
+ * link's generation is the pass's): a tail kept for A30 — the committed
275
+ * frame's dependency, awaiting the run that retires it — is not a read of
276
+ * the flight, and a reader that stopped reading releases it (O3, #3494).
277
+ * An errored pass (NotReady included) did not stop: it never got there, and
278
+ * its full list stands (A30) — the reads it did not reach still observe. A
279
+ * flight nobody renders holds nothing (A29). Only the nodes `t` still owns
280
+ * count: one a lane took over since is the lane's to wait on. A guess whose
281
+ * own truth is in flight blocks the lane's parent, not the lane — it stands
282
+ * in for the flight (A17: "visible until its own fetch settles"). */ function blocked(e) {
283
+ // The transaction being judged (a lane consulted on its behalf judges for
284
+ // it): a zombie whose removal it stages is moot for it alone (`onScreen`).
285
+ const o = judge;
286
+ judge ??= e;
287
+ // An action still running in it holds it open (action.ts). Its lanes
288
+ // (lanes.ts): a lane under `t` blocks it while blocked itself.
289
+ const n = e.tt !== 0 || blockedBy(e.Ye, e) || !!lanesBlockedHook?.(e);
290
+ judge = o;
291
+ return n;
292
+ }
293
+
294
+ let judge = null;
295
+
296
+ /** Observe tier: the flights `t` waits on — its pending held sources a
297
+ * frame reader observes (`blockedBy`'s predicate, collected; the readers
298
+ * themselves are not sources). */ function blockersOf(e) {
299
+ const o = [];
300
+ for (let n = 0; n < e.Ye.length; n++) {
301
+ const t = e.Ye[n];
302
+ if (!t.i && t.m & STATUS_PENDING && !(t.R & REACTIVE_DISPOSED) && (t.T.Je === null || txOf(t) === e) && blockedBy([ t ], e)) o.push(t);
303
+ }
304
+ return o;
305
+ }
306
+
307
+ /** `t`'s own flights only — not its lanes': a flight the frame asked for
308
+ * (a refetch, a plain load) is authoritative; a lane's derivation flight is
309
+ * not (lanes.ts, the body-end corollary). A pending reader counts only if
310
+ * one of the sources it waits on is `t`'s (a render effect marked pending
311
+ * by a lane flight, never re-run, is the lane's wait, not the frame's). */ function ownFlights(e) {
312
+ return blockedBy(e.Ye, e, true);
313
+ }
314
+
315
+ /** A source the pending node waits on that is `t`'s (held by it, or by no
316
+ * one) — not another transaction's or lane's work. Itself, when it is the
317
+ * source. */ function ownSource(e, o) {
318
+ const n = e.T?.se;
319
+ if (n === undefined) return true;
320
+ for (const e of n) {
321
+ const n = e.T?.Je;
322
+ if (n == null || resolveTx(n) === o) return true;
323
+ }
324
+ return false;
325
+ }
326
+
327
+ function blockedBy(e, o, n = false) {
328
+ for (let t = 0; t < e.length; t++) {
329
+ const l = e[t];
330
+ if (!(l.m & STATUS_PENDING) || l.R & REACTIVE_DISPOSED || l.l & CONFIG_GUESS || l.T.Je !== null && txOf(l) !== o || n && !ownSource(l, o)) continue;
331
+ // The frame's own pending observer (off screen: this one holds nothing;
332
+ // the next listed node may).
333
+ if (l.i === EFFECT_RENDER) {
334
+ if (onScreen(l, judge ?? o)) return true;
335
+ continue;
336
+ }
337
+ for (let e = l.o; e !== null; e = e.t) {
338
+ const n = e.u;
339
+ // A frame reader: a render effect, or a stale reader of the flight
340
+ // (REACTIVE_FRAME_READ — a verdict reader, `observeFlight`; lane work,
341
+ // `frameRead`): served committed instead of going pending, it derives
342
+ // from the flight all the same and is re-derived at the landing — the
343
+ // frame's observation survives its reader becoming a lane's (a guess
344
+ // over a held window, V5/A17). A probe alone does not hold. A
345
+ // boundary showing content forwards the pending (its output pending,
346
+ // CONFIG_REDERIVE): transparent, its readers hold through it (A33; the
347
+ // lane-membership ruling, 2026-10-05). An output that never committed
348
+ // shows nothing — a mount the frame has not revealed — and holds
349
+ // nothing.
350
+ if ((n.i === EFFECT_RENDER && n.m & STATUS_PENDING || n.R & REACTIVE_FRAME_READ || n.l & CONFIG_REDERIVE && n.m === STATUS_PENDING) && (e.de === n.Ne || n.T?.U != null) && !(n.R & REACTIVE_DISPOSED) && onScreen(n, judge ?? o)) return true;
351
+ }
352
+ }
353
+ return false;
354
+ }
355
+
356
+ /** A frame reader whose say counts for `t`'s hold — `t` the transaction
357
+ * being judged: not a zombie whose removal `t` stages (A15 #3463: a re-ask
358
+ * in the unmount frame lands its pending on the zombie it unmounts — the
359
+ * compiled <Show> of #3372; a zombie blocks every other judgment while it
360
+ * is visible, a lane's reveal included: only the commit that disposes it
361
+ * makes its say moot), and not behind a fallback (A33: a boundary showing its fallback is the display of
362
+ * everything under it, so a reader there is not on screen and holds
363
+ * nothing — boundaries.ts). */ function onScreen(e, o) {
364
+ return !(e.R & REACTIVE_ZOMBIE && removalStagedBy(e, o)) && !hiddenHook?.(e);
365
+ }
366
+
367
+ /** A15 (#3463): a reader whose removal is staged — a zombie, parked by a
368
+ * pass awaiting its commit — is still on screen and live for every hold,
369
+ * except the transaction staging its removal: for that one its say is moot
370
+ * (done, and the commit disposes it; not done, and it stays parked). The
371
+ * staging transaction is the one holding the pass that parked the frame:
372
+ * the nearest non-zombie ancestor. */ function removalStagedBy(e, o) {
373
+ let n = e;
374
+ while (n?.R & REACTIVE_ZOMBIE) n = n._parent;
375
+ return n?.l & CONFIG_HELD && n.T?.Je != null && txOf(n) === resolveTx(o);
376
+ }
377
+
378
+ /** Unchanged passes this flush whose stale dependency tail awaits the
379
+ * flush's verdict (A30, #3469): trimmed when the flush commits, kept when
380
+ * it parks — the committed frame still derives from the previous pass's
381
+ * dependencies, and a write to one of them must reach the node until a
382
+ * committing pass replaces it. */ const heldTrims = [];
383
+
384
+ function heldTrim(e) {
385
+ heldTrims.push(e);
386
+ }
387
+
388
+ /** Zombies popped from the dirty heap this flush (`deferZombie`). The seam
389
+ * decides their fate: a park cancels them (the writes that dirtied them are
390
+ * held with it); a commit runs the survivors — the ones whose owner is still
391
+ * awaiting its commit — in height order, after the commits that would have
392
+ * disposed them (#3546). No second heap: the list is the deferral. */ const deferredZombies = [];
393
+
394
+ function deferZombie(e) {
395
+ deleteFromHeap(e, dirtyQueue);
396
+ e.R &= -4;
397
+ deferredZombies.push(e);
398
+ }
399
+
400
+ /** Put a staged node in a transaction: flagged, stamped, on its landing
401
+ * list. */ function holdNode(e, o) {
402
+ e.l |= CONFIG_HELD;
403
+ list(e, o);
404
+ }
405
+
406
+ /** Put a node on a transaction's list, once: a node's `_transaction` is the
407
+ * transaction whose `_nodes` it is on (every site that sets one does the
408
+ * other), so a re-pass, a re-landing or a re-staging adds nothing. */ function list(e, o) {
409
+ const n = ext(e);
410
+ if (n.Je === o) return;
411
+ n.Je = o;
412
+ o.Ye.push(e);
413
+ }
414
+
415
+ /** Hold the (uncommitted) frame under a held pass: every computed in the
416
+ * live child chain, recursively. Plain owners are walked through, not held
417
+ * — nothing reads or re-passes them. Once per park, over the parked frames
418
+ * only; recomputes pay nothing for it. A lane's node in the frame (listed on
419
+ * a lane: a verdict reader broken out of this very hold — display-ahead — or
420
+ * a derivation of a guess, CONFIG_OVERRIDE; a lane pass's effect) is the
421
+ * lane's, as the seam's own loop has it: "lane work never makes its node
422
+ * transaction work" (A15, #3698). Re-listed here it would carry the lane's
423
+ * value on the transaction's list, where the landing nulls its transaction
424
+ * and `dissolveLane` no longer finds it — a lane node with no lane, which the
425
+ * next read of it trips over (`laneRead` → `txOf`). Its frame is the lane's
426
+ * too (ruling A: a lane pass's children are the lane's), revealed and
427
+ * retired at the lane's seam. */ function holdFrame(e, o) {
428
+ for (let n = e.ze; n !== null; n = n.Xe) {
429
+ if (n.T?.Je?.V) continue;
430
+ if (n.X !== undefined) {
431
+ if (!(n.l & CONFIG_HELD)) holdNode(n, o);
432
+ n.l |= CONFIG_HELD_FRAME;
433
+ }
434
+ holdFrame(n, o);
435
+ }
436
+ }
437
+
438
+ /** The nodes under `owner` a transaction other than `t` holds (boundaries.ts:
439
+ * what an earlier hold staged under a re-armed boundary). */ function heldUnder(e, o, n) {
440
+ for (let t = e.ze; t !== null; t = t.Xe) {
441
+ if ((t.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && txOf(t) !== o) n(t);
442
+ heldUnder(t, o, n);
443
+ }
444
+ }
445
+
446
+ /** `n` leaves the hold it was staged in: its next pass publishes mainline. */ function unhold(e) {
447
+ e.T.Je = null;
448
+ e.l &= ~CONFIG_HELD;
449
+ }
450
+
451
+ const dirtyQueue = {
452
+ jt: new Array(2e3).fill(undefined),
453
+ p: false,
454
+ Et: 0,
455
+ yt: 0
456
+ };
457
+
458
+ let clock = 0;
459
+
460
+ let scheduled = false;
461
+
462
+ let halted = false;
463
+
464
+ let haltNotified = false;
465
+
466
+ let syncDepth = 0;
467
+
468
+ /**
469
+ * Toggles the dev-mode "must be inside a `<Loading>` boundary" enforcement
470
+ * window. Only `render()` calls this — wrapping the initial mount so that a
471
+ * top-level uncaught async read surfaces the diagnostic. Not part of the
472
+ * user-facing API.
473
+ *
474
+ * @internal
475
+ */ function enforceLoadingBoundary(e) {}
476
+
477
+ // Dev-only marker for the effect half of createEffect/createRenderEffect, so
478
+ // flush() can report the no-op instead of failing silently (React parity).
479
+ /** Action bodies on the stack (action.ts): `flush()` is refused inside one —
480
+ * the body's writes are its transaction's and commit when it settles; a
481
+ * drain mid-step cannot reveal them, and dev says so. */ let actionDepth = 0;
482
+
483
+ function enterAction() {
484
+ actionDepth++;
485
+ }
486
+
487
+ function exitAction() {
488
+ actionDepth--;
489
+ }
490
+
491
+ /** Provenance (A18, #3331): the question being asked — an action's
492
+ * sequence while a slice of its body and the flush that carries it run, 0
493
+ * otherwise. A guess is stamped with it (lanes.ts; a mainline guess takes a
494
+ * fresh sequence: the newest intent), a flight with the question that
495
+ * started it (async.ts; mainline is always the current question). An
496
+ * answer from an older question than the guess it lands on is stale: held
497
+ * silently, it does not move the graph. */ let question = 0;
498
+
499
+ let questions = 0;
500
+
501
+ function setQuestion(e) {
502
+ question = e;
503
+ }
504
+
505
+ function nextQuestion() {
506
+ return ++questions;
507
+ }
508
+
509
+ const MAINLINE_QUESTION = 2147483647;
510
+
511
+ function schedule() {
512
+ if (halted) {
513
+ notifyHalted();
514
+ return;
515
+ }
516
+ if (scheduled) return;
517
+ scheduled = true;
518
+ if (!syncDepth && !globalQueue.Y) {
519
+ queueMicrotask(flush);
520
+ }
521
+ }
522
+
523
+ /**
524
+ * Permanently halts the reactive system. Called when a user error escapes
525
+ * every boundary — app state is undefined at that point, so scheduling stops
526
+ * entirely rather than limping along with a half-applied update.
527
+ */
528
+ /**
529
+ * The key a root owner carries its client error hook under (`render`'s
530
+ * `onError`) — registered, so a runtime writes it with no import of the hook
531
+ * module (core/error-hooks.ts) and no property mangling in the way. Defined
532
+ * HERE, not there: a runtime that only writes the key must not retain the
533
+ * hook machinery (pay-for-use).
534
+ */ const ROOT_ERROR_HOOK = Symbol.for("solid-js/root-error-hook");
535
+
536
+ const HALTED = "[REACTIVITY_HALTED]";
537
+
538
+ function haltReactivity(e) {
539
+ if (halted) return;
540
+ halted = true;
541
+ // A cleanup error collected before the halt is dropped (owner.ts).
542
+ rethrowCleanupError(true);
543
+ let o = HALTED;
544
+ // Surface the cause here too: callers rethrow it, but a creation-time throw
545
+ // unwinds through ancestor recomputes that convert it to status instead of
546
+ // surfacing it (#2884), so the rethrow alone cannot guarantee visibility.
547
+ // Where the platform has one, hand the cause to its uncaught-error channel
548
+ // (`reportError` → `error` event → window.onerror / error monitoring): a
549
+ // halt that only reaches console.error leaves a page that LOOKS alive with
550
+ // nothing an app or its telemetry can act on (#3338 — an uncaught throw
551
+ // during the hydration render). The rethrow may reach the top as well in
552
+ // the non-swallowed cases; a duplicate report beats a silent one.
553
+ const n = e !== undefined && globalThis.reportError;
554
+ n || e === undefined ? console.error(o) : console.error(o, e);
555
+ n && n(e);
556
+ }
557
+
558
+ /**
559
+ * Dev: `haltReactivity`, located at `subject` — the computation the uncaught
560
+ * error escaped from — for the REACTIVITY_HALTED message's owner path. Call
561
+ * sites pick it in dev builds only (`haltReactivity(e)` in production), so
562
+ * production keeps the one-argument halt and its bytes unchanged.
563
+ */ function haltReactivityAt(e, o) {
564
+ haltReactivity(e);
565
+ }
566
+
567
+ // Logs on the first write after a halt so a frozen interaction is traceable.
568
+ function notifyHalted() {
569
+ if (haltNotified) return;
570
+ haltNotified = true;
571
+ console.error(HALTED);
572
+ }
573
+
574
+ /** @internal Test/dev-reload hook. Revives scheduling after a halt. */ function resetErrorHalt() {
575
+ halted = false;
576
+ haltNotified = false;
577
+ }
578
+
579
+ class GlobalQueue {
580
+ rn=[ [], [] ];
581
+ Y=false;
582
+ flush() {
583
+ if (this.Y) return;
584
+ this.Y = true;
585
+ // A28: companions of nodes written since the last flush mirror the
586
+ // flushed world — re-synced inside the running window (the write is
587
+ // "flushed" from here on).
588
+ resyncUnflushedCompanions();
589
+ passLane = null;
590
+ passTx = null;
591
+ try {
592
+ // Sweep before the heap: unobserved() pulls swept nodes out of it, so
593
+ // a dormant memo dirtied in the same tick is reclaimed instead of
594
+ // recomputed; late subscribers (an effect reading a swept memo this
595
+ // flush) revive it, which is the pay-for-use contract.
596
+ sweepDormant();
597
+ runHeap(dirtyQueue, GlobalQueue.Ze);
598
+ this.settle();
599
+ clock++;
600
+ // A write the commit sweep staged with no subscriber to dirty is work
601
+ // too — the next round commits it.
602
+ scheduled = dirtyQueue.yt >= dirtyQueue.Et || pendingNodes.length !== 0;
603
+ this.run(EFFECT_RENDER);
604
+ this.run(EFFECT_USER);
605
+ if (true && holding) {
606
+ holding = false;
607
+ attrHooks.holdEnd();
608
+ }
609
+ if (false && !scheduled) ;
610
+ if (false) ;
611
+ } finally {
612
+ this.Y = false;
613
+ passTx = null;
614
+ }
615
+ }
616
+ /** L2 — the seam: end of the pure phase. Commit this flush's staged nodes,
617
+ * or park them with the transaction the flush joined; then land every
618
+ * transaction no frame is waiting on. The landing flush is an ordinary
619
+ * flush: the settle walk (async.ts) re-ran the parked readers in its pure
620
+ * phase, and here no flight the transaction holds has a frame deriving
621
+ * from it (`blocked`). */ settle() {
622
+ // Lanes: an ended action body judges its guesses now that their passes
623
+ // have run (lanes.ts); a correction's re-derivations run in one more
624
+ // pure round — the parent's flights before the parent is judged below.
625
+ if (laneCorrectionsHook?.()) runHeap(dirtyQueue, GlobalQueue.Ze);
626
+ const e = flushTransaction;
627
+ flushTransaction = latestOpened = null;
628
+ // The frame's verdict is in: the guesses written since the last seam
629
+ // open their lane under the frame's transaction (a lane's own flush
630
+ // parents a nested lane to it), or are dropped — the frame commits, and
631
+ // there is nothing to be optimistic over. Their passes run next round;
632
+ // a parent landing at this very seam ends the lane before they do.
633
+ applyGuessesHook?.(e);
634
+ // A lane's own flush (its flight landing, its reveal) parks nothing: the
635
+ // lane's work reached it per pass; the frame's plain writes commit.
636
+ // (Resolved: a guess over another transaction's guess merged them.)
637
+ const o = e !== null && !e.V ? resolveTx(e) : null;
638
+ if (o !== null) {
639
+ // `t._nodes` grows under the walk (holdFrame pushes); the bound is
640
+ // this flush's list, which does not.
641
+ const e = pendingNodes.length;
642
+ for (let n = 0; n < e; n++) {
643
+ const e = pendingNodes[n];
644
+ // Already held: the pass that re-staged it joined its transaction,
645
+ // which is `t` (or merged into it). Lane work is the lane's (a
646
+ // pending mark propagated onto it queued it here too).
647
+ if (!(e.l & (CONFIG_HELD | CONFIG_OVERRIDE)) && (e.T === null || e.T.Je === null || !e.T.Je.V)) {
648
+ // A loading source — pending with nothing staged and no committed
649
+ // value (A16, A19 exc. 1: uninitialized is loading, not pending)
650
+ // that is not an observer — is nobody's frame: not held. Its
651
+ // first landing is commit #0, not a resumption of this frame, and
652
+ // a write that reaches it later overlaps nothing (#2937: the
653
+ // stale stamp was the bridge that entangled unrelated updates
654
+ // with a never-resolving flight). Its observers are held and
655
+ // wait on its flight (`blocked`).
656
+ if (e.ue === NOT_PENDING && e.m & STATUS_UNINITIALIZED && e.i !== EFFECT_RENDER) continue;
657
+ // A34 (2): a tick whose writes net to the committed value made no
658
+ // proposal (`setShow(false); setShow(true)` on a committed true) —
659
+ // not staged, not held, pends nothing; a later write to it is
660
+ // plain mainline. (A node already held stays held when written
661
+ // back: A34 (1).)
662
+ if (e.ue !== NOT_PENDING && !(e.m & (STATUS_PENDING | STATUS_UNINITIALIZED)) && e.T?.ft == null && e.T?.Tt == null &&
663
+ // The comparator last: a pass that created children (an object
664
+ // owning a fresh async memo, #4111) must not have it read them.
665
+ // A NotReady read there is a change (`isSame`).
666
+ isSame(e, e.le, e.ue)) {
667
+ e.ue = NOT_PENDING;
668
+ e.l &= ~CONFIG_STAGED;
669
+ continue;
670
+ }
671
+ holdNode(e, o);
672
+ }
673
+ // A held pass's frame is held with it (ruling A, O2): the children it
674
+ // created are the transaction's — they compute, and a pass over them
675
+ // or a write that reaches them joins; they effect at the landing.
676
+ if (e.l & CONFIG_STAGED) holdFrame(e, o);
677
+ }
678
+ pendingNodes.length = 0;
679
+ storeParkHook?.(o);
680
+ // (This flush's runs are stashed with `t` below — after the landings,
681
+ // so a `t` that lands at this very seam runs them first, ahead of
682
+ // the runs it held from earlier flushes.)
683
+ // The batch joins: the writes that dirtied these are held with it — a
684
+ // zombie never displays that world. A verdict reader among them does
685
+ // (#3444: `latest()` inside a branch a held Show is removing peers
686
+ // through like the same read outside): it re-runs now, as the
687
+ // holder's verdict lane's work — before the lane seams, which reveal
688
+ // it (nothing commits this seam that could dispose it).
689
+ // (Verdict readers exist only with the verdict engine installed.)
690
+ const n = deferredZombies.splice(0);
691
+ if (observeFlightHook) {
692
+ n.sort((e, o) => e.lt - o.lt);
693
+ for (let e = 0; e < n.length; e++) if ((n[e].R & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE)) === REACTIVE_ZOMBIE && n[e].l & CONFIG_VERDICT) recompute(n[e]);
694
+ }
695
+ // The unchanged passes' tails stay linked (A30, #3469): their inputs
696
+ // are held, and the committed frame still derives from them — no trim.
697
+ } else {
698
+ commitPendingNodes();
699
+ // The flush committed: an unchanged pass's stale tail goes now. Not
700
+ // after a later pass this flush that threw (NotReady included — it
701
+ // keeps its full list, `_depsTail` marking where it stopped) or that
702
+ // left an effect owing a run (`runEffect` trims when the run applies):
703
+ // `recompute`'s own gates.
704
+ for (let e = 0; e < heldTrims.length; e++) {
705
+ const o = heldTrims[e];
706
+ if (o.T?.U == null && !o.Qe) trimStaleDeps(o);
707
+ }
708
+ }
709
+ // The list is the flush's either way. Reset only when there is something
710
+ // to reset (`stagedReaders` below likewise): `length = 0` is a runtime
711
+ // call even on an empty array, and the plain flush — no unchanged pass,
712
+ // no lane work — has nothing in either.
713
+ if (heldTrims.length) heldTrims.length = 0;
714
+ // Lanes: a blocked one parks its frame (its own flight is up); an
715
+ // unblocked one reveals this round's work. Then land every transaction
716
+ // no frame is waiting on — its lanes end with it. Backwards: a landing
717
+ // removes its entry.
718
+ // This flush's own runs, the lanes' reveals, and the landings' held runs
719
+ // are collected apart and ordered below.
720
+ const n = this.rn;
721
+ const t = [ [], [] ];
722
+ // The passes that read this frame's stagings as the screen read held
723
+ // writes if it parked: the lane seam re-derives them on the committed
724
+ // world (a lane's runs wait this round).
725
+ if (laneSeamsHook) {
726
+ this.rn = t;
727
+ laneSeamsHook(o);
728
+ if (stagedReaders.length) stagedReaders.length = 0;
729
+ }
730
+ this.rn = [ [], [] ];
731
+ for (let e = transactions.length - 1; e >= 0; e--) {
732
+ const o = transactions[e];
733
+ if (blocked(o)) continue;
734
+ transactions.splice(e, 1);
735
+ endLanesHook?.(o);
736
+ land(o);
737
+ // A landing's commits can dispose a zombie a transaction judged above
738
+ // was blocked on (#3463: live "until the commit that disposes it") —
739
+ // judge them again.
740
+ e = transactions.length;
741
+ }
742
+ // The store folds the pending backings whose container nodes committed
743
+ // — this flush's, or a landing's (store/store.ts installs it).
744
+ storeCommitHook?.();
745
+ // Marks declared outside a transaction: the flush that carried them
746
+ // parked (something async below) — the window is the frame's, the marks
747
+ // are its transaction's; else they release at the seam, verdict-only,
748
+ // nothing to show (affects.ts).
749
+ const l = o && liveTx(o);
750
+ releaseAmbientMarksHook?.(l);
751
+ // The effect phase: lane work first (displayed ahead of the frame), then
752
+ // this flush's runs, then the runs the landings held from earlier
753
+ // flushes — as one pass would have queued them (#3540: a shell
754
+ // re-derived at the landing before a boundary swap held since the
755
+ // write; #3528: a display-ahead swap before the count it is shown
756
+ // beside). A parked frame's runs wait with its transaction instead: the
757
+ // effect phase applies a committed frame.
758
+ const s = l !== null;
759
+ // Attribution hook: this flush found its transaction incomplete — its
760
+ // writes stay staged, its runs are stashed below. Before the effect
761
+ // phase (the lanes' display-ahead runs are the visible acknowledgers);
762
+ // `holdEnd` fires after it (`flush`).
763
+ if (s && attrHooks !== null) {
764
+ attrHooks.holdStart(l);
765
+ holding = true;
766
+ }
767
+ for (let e = 0; e < 2; e++) {
768
+ if (s) append(l.rn[e], n[e]);
769
+ // The plain flush — no reveal, no landing, not parked — has nothing to
770
+ // order around its own runs: they are the queue as they stand (the
771
+ // same array, not a copy; `run` takes it whole). Every other seam
772
+ // builds the ordered queue above.
773
+ this.rn[e] = s || t[e].length || this.rn[e].length ? t[e].concat(s ? [] : n[e], this.rn[e]) : n[e];
774
+ }
775
+ if (deferredZombies.length !== 0) {
776
+ // Survivors are the displayed frame of a held pass: they rerun for the
777
+ // live write, parents before children.
778
+ const e = deferredZombies.splice(0).sort((e, o) => e.lt - o.lt);
779
+ for (let o = 0; o < e.length; o++) {
780
+ if (!(e[o].R & REACTIVE_DISPOSED)) recompute(e[o]);
781
+ }
782
+ }
783
+ // Boundaries: readers that settled, landed or died this flush are
784
+ // dropped; a fallback with none left reveals next round.
785
+ boundarySeamHook?.();
786
+ }
787
+ run(e) {
788
+ const o = this.rn[e - 1];
789
+ if (o.length) {
790
+ this.rn[e - 1] = [];
791
+ runQueue(o, e);
792
+ }
793
+ }
794
+ enqueue(e, o, n = passLane) {
795
+ // Lane work's runs are the lane's: they run at its reveal, not with
796
+ // the frame (which may park). A lane pass that read the frame's own
797
+ // staging is the frame's (`recompute` passes null): its run waits with
798
+ // the frame.
799
+ if (e) (n !== null ? n : this).rn[e - 1].push(o);
800
+ schedule();
801
+ }
802
+ /** Status reaching the root: pending is absorbed (the root mount defers —
803
+ * dev records it for the ASYNC_OUTSIDE_LOADING_BOUNDARY FYI); an error is
804
+ * unhandled (`false` → the caller halts).
805
+ *
806
+ * L2: this is where a transaction is registered. Pending that reaches an
807
+ * observer (a render effect: the frame) is a frame that cannot show — the
808
+ * flush parks, and the transaction waits on the flight while a frame
809
+ * derives from it (`blocked`). Pending nobody observes holds nothing: a
810
+ * memo in flight that no frame reads is not a frame waiting. (Base: the
811
+ * queue's `_asyncReporters`, INV-3's one registration site.)
812
+ *
813
+ * A boundary between the observer and the root (boundaries.ts) is asked
814
+ * first: a fallback catches what is not ready under it, and the root
815
+ * never hears of it. */ notify(e, o, n, t) {
816
+ if (catchHook?.(e, n & o, t)) return true;
817
+ if (o & STATUS_PENDING) {
818
+ if (n & STATUS_PENDING) {
819
+ // A pass that went pending holds its frame: the async's own
820
+ // synchronous frame is not ready (A15 reveal corollary: a reveal
821
+ // that discovers a flight whose inputs are visible holds and joins
822
+ // it; one whose inputs are held is a stale reader and never gets
823
+ // here — `read` serves it the committed value). The frame's, not the
824
+ // observer's: a render effect belongs to whoever dirtied its pass
825
+ // (A15 shared-hole, #3407) — one held by T1 and re-run by T2's write
826
+ // pends for T2's flight, and the two stay parallel; a frame that is
827
+ // T1's already joined T1 through the write or the memo that made it
828
+ // so. Outside a flush — root setup, a mount — the frame is the
829
+ // mount's: its pending observers are one transaction, and a flight
830
+ // started in it resumes it when it lands (maintainer, #3461: "part
831
+ // of the same one"); what the mount published synchronously stays
832
+ // published. Lane work going pending holds the lane, not the frame:
833
+ // `blocked(lane)` finds it in the lane's staging ("if further async
834
+ // downstream is hit it holds like its own transition").
835
+ if (passLane === null) joinFuture(null);
836
+ }
837
+ return true;
838
+ }
839
+ return false;
840
+ }
841
+ }
842
+
843
+ /** A15's stale readers: a frame whose LAST pass read a held node as
844
+ * committed re-derives on the landed world — next round, after the commits.
845
+ * One re-derived since (any later pass cleared the bit: in the transaction,
846
+ * or without the held read) owes nothing. A run this transaction stashed
847
+ * for it is void (#3322: an effect has one value slot, and the stale pass
848
+ * overwrote the value that run was for) — the re-derivation's run replaces
849
+ * it. Truthy when an effect re-derives (`land`). */ function reruns(e) {
850
+ let o;
851
+ for (let n = 0; n < e.En.length; n++) {
852
+ const t = e.En[n];
853
+ // An errored reader's display is its error (D-39): the landing gives it
854
+ // nothing to re-derive, and a re-run would be a read that retries the
855
+ // errored source (#3945) — unless that source is errored no longer: the
856
+ // reader was served a shown error the landing replaces (`throwShown`).
857
+ // Nor is the reader's own error: its pass threw over the committed value
858
+ // of what the landing carries, served as the screen (A10; a lane's node
859
+ // reading its parent's hold, or a pass with no value yet reading a
860
+ // staging, `stagedScreen`) — the landing is the new answer, and the
861
+ // reader re-derives on it (A15; D-39). A first load read so has no other
862
+ // run: its error would stay the screen (async-gen 91501 #1158, #119).
863
+ // A boundary's structure (its tree, its output: CONFIG_FRAME, no effect
864
+ // type) re-derives all the same: what it read as the screen is the
865
+ // content the landing replaces, and only its pass links the new content
866
+ // whose recovery must reach it (D-3, D-39, #4405).
867
+ const l = t.T?.U?.source;
868
+ if (t.R & REACTIVE_DISPOSED || t.m & STATUS_ERROR && (t.i || !(t.l & CONFIG_FRAME)) && l !== t && (l?.m ?? STATUS_ERROR) & STATUS_ERROR) continue;
869
+ if (t.R & REACTIVE_FRAME_READ || observeFlightHook && t.l & CONFIG_VERDICT) {
870
+ // A memo re-deriving its own error holds the release as an effect
871
+ // does (`land`): run first, the held runs showed that error beside a
872
+ // frame about to replace it (#4407; #4284: the grouping never changes
873
+ // the screen).
874
+ o ||= t.i || l === t;
875
+ // A lane's reveal leaves the mark to the transaction: a reader that
876
+ // also read a held node as committed (`staleReader`) still observes
877
+ // that flight until it re-runs — cleared here, the transaction landed
878
+ // at this seam with the flight up and voided the reveal's run (#4397).
879
+ if (!e.V) t.R &= ~REACTIVE_FRAME_READ;
880
+ t.Qe = false;
881
+ // Its frame is replaced by the re-derivation: a run one of its
882
+ // children queued this flush (re-run by the same landing) is for the
883
+ // frame being replaced — void too (#3404).
884
+ for (let e = t.ze; e !== null; e = e.Xe) if (e.i) e.Qe = false;
885
+ // A frame flight the seam parked (lanes.ts `laneSeams`, R5) asked the
886
+ // question this landing would: adopted, it is asked once (F-9).
887
+ if (!laneAdoptHook?.(t)) enqueueSub(t);
888
+ }
889
+ }
890
+ e.En.length = 0;
891
+ return o;
892
+ }
893
+
894
+ /** The landing: the held frame becomes the frame. */ function land(e) {
895
+ // Attribution hook: judged complete — its held writes commit next
896
+ // (`_nodes` still lists them).
897
+ if (attrHooks !== null) attrHooks.transitionSettled(e);
898
+ const o = reruns(e);
899
+ if (releaseMarksHook && e.I !== null) releaseMarksHook(e.I);
900
+ // Old children die in the commits (cleanups first), then the stashed
901
+ // effects run ahead of this flush's own. A node a lane took over since
902
+ // (a guess written over the staged truth) is the lane's to land.
903
+ for (let o = 0; o < e.Ye.length; o++) {
904
+ const n = e.Ye[o];
905
+ if (n.T.Je === null || txOf(n) !== e) continue;
906
+ n.T.Je = null;
907
+ commitPendingNode(n);
908
+ }
909
+ // A cleanup those commits ran that wrote a node `u` still held joined
910
+ // `u`, which has left `transactions`: the write is this landing's, and
911
+ // the next flush must not park in it (#4089).
912
+ if (flushTransaction === e) flushTransaction = null;
913
+ // An effect re-derives next round (`reruns`): the held runs wait for it
914
+ // (D-9, the A15 reveal corollary, #4071). Its re-derivation can discover a
915
+ // flight — a stale reader whose mainline pass stopped reading it, now
916
+ // reading the landed world — and a landing that showed its runs first
917
+ // would reveal the held write beside a frame that can only show its old
918
+ // value. Queued last in this effect phase, the release hands them to the
919
+ // next round's own runs, ahead of the re-derivation's: they show with it,
920
+ // or park with it in the hold its pending opens. The commits stand.
921
+ if (o) globalQueue.rn[1].push(() => releaseQueues(e)); else releaseQueues(e);
922
+ }
923
+
924
+ /** The held runs join this flush's effect phase after its own runs (the
925
+ * landing's re-passes, in height order): the frame's effects run as one
926
+ * pass would have queued them — a shell re-derived at the landing before a
927
+ * boundary swap held since the write (#3540's log order). */ function releaseQueues(e) {
928
+ for (let o = 0; o < 2; o++) {
929
+ append(globalQueue.rn[o], e.rn[o]);
930
+ e.rn[o] = [];
931
+ }
932
+ }
933
+
934
+ /** `a.push(...b)` without the argument-count limit. */ function append(e, o) {
935
+ for (let n = 0; n < o.length; n++) e.push(o[n]);
936
+ }
937
+
938
+ /** Nodes staged this flush (`_pendingValue` set), committed at its end. */ const pendingNodes = [];
939
+
940
+ /** Passes that read a staging of this flush as the screen — lane work
941
+ * (core.ts `stagedRead`), a verdict reader before the frame's verdict
942
+ * (verdict.ts): if the frame parks they read a held write — re-derived next
943
+ * round (lanes.ts; a lane's runs wait that round). */ const stagedReaders = [];
944
+
945
+ /** A15's stale reader: `c` was served the committed value of a node `t`
946
+ * holds (a render effect, a verdict reader, lane work) and is re-derived
947
+ * after `t`'s landing (`_reruns`). Once per pass. */ function staleReader(e, o) {
948
+ if (!(e.R & REACTIVE_FRAME_READ)) {
949
+ e.R |= REACTIVE_FRAME_READ;
950
+ o.En.push(e);
951
+ }
952
+ }
953
+
954
+ function queuePendingNode(e) {
955
+ pendingNodes.push(e);
956
+ if (!globalQueue.Y) markUnflushedStaged();
957
+ // A28
958
+ }
959
+
960
+ /** §12d: bumped by every recompute and every new subscriber edge. A node's
961
+ * staged-rewrite skip is sound only while NOTHING recomputed or linked since
962
+ * its last notify — a mid-batch pull can clean a marked subscriber, and a
963
+ * skipped re-write would leave it stale. */ let notifyEpoch = 0;
964
+
965
+ function bumpNotifyEpoch() {
966
+ notifyEpoch++;
967
+ }
968
+
969
+ function insertSubs(e) {
970
+ // §12d: stamp before walking — setSignal's staged-rewrite fast path skips
971
+ // the next walk for this node while the epoch holds (marking is idempotent).
972
+ e.Dt = notifyEpoch;
973
+ // Presence bits gate the optional-slot probes (see constants.ts): one
974
+ // masked read of the always-present _config instead of missing-property
975
+ // lookups in the hottest notify loop.
976
+ const o = e.l;
977
+ const n = (o & CONFIG_HAS_SNAPSHOT) !== 0 && e.T?.st !== undefined;
978
+ // Observe-tier fan-out: this walk visits every subscriber edge anyway, so
979
+ // the graph-size count is one local increment here and no field anywhere.
980
+ let t = 0;
981
+ for (let o = e.o; o !== null; o = o.t) {
982
+ const e = o.u;
983
+ t++;
984
+ // Missed-wake latch (#3037): this write is landing while the subscriber
985
+ // is mid-recompute (a nested pull committing beneath its reads), and the
986
+ // heap refuses RECOMPUTING nodes. A gen-current link means the pass
987
+ // already validated this dep — the value it read is now stale — so latch
988
+ // for recompute's tail to reschedule. Untouched links need no latch (the
989
+ // pass either re-reads them fresh or trims them), and neither does the
990
+ // tail link: it is the read IN FLIGHT — read() links before it pulls, so
991
+ // this very commit is what that read returns.
992
+ if (e.R & REACTIVE_RECOMPUTING_DEPS && o.de === e.Ne && o !== e._) e.R |= REACTIVE_MISSED_WAKE;
993
+ if (n && e.l & CONFIG_IN_SNAPSHOT_SCOPE) {
994
+ e.R |= REACTIVE_SNAPSHOT_STALE;
995
+ continue;
996
+ }
997
+ enqueueSub(e);
998
+ }
999
+ if (t >= GRAPH_SIZE_WARN_AT) noteFanOut(e, t);
1000
+ }
1001
+
1002
+ /** Lane work re-staged (lanes.ts; `recompute`): the lane's members among
1003
+ * its subscribers re-derive as the lane's work (REACTIVE_LANE_DIRTY), their
1004
+ * runs held with it — not as stale readers republishing the committed view
1005
+ * (#3460). After `insertSubs`. A render effect on no lane is marked too:
1006
+ * inert for its pass (`recompute` seats a lane only from its own `_x`), it
1007
+ * is a live write if the effect becomes a zombie later this flush — an
1008
+ * on-screen reader whose removal an adopted write stages follows the lane
1009
+ * (the lane-membership ruling, 2026-10-05; `_update`).
1010
+ *
1011
+ * A render effect re-run by a verdict memo's change (`createMemo(() =>
1012
+ * latest(x))`, CONFIG_VERDICT) is the verdict reader the same read inline
1013
+ * makes it, for that pass (F-6; D-26 `[s(), L()]`, #4284): a frame reader
1014
+ * of its other reads — a staging's screen, a flight's committed value. One
1015
+ * an earlier propagation this flush seated in another lane leaves that
1016
+ * seat: its pass starts from the frame's, as the inline effect's does, and
1017
+ * `laneRead` re-derives it on the screen if that lane holds (`entered`). */ function laneDirty(e, o) {
1018
+ for (let n = e.o; n !== null; n = n.t) {
1019
+ const t = n.u, l = t.T?.Je;
1020
+ if (t.i === EFFECT_RENDER && e.l & CONFIG_VERDICT) t.l |= CONFIG_VERDICT, t.R &= ~REACTIVE_LANE_DIRTY;
1021
+ if (l?.V ? sameLane(l, o) : t.i) t.R |= REACTIVE_LANE_DIRTY;
1022
+ }
1023
+ }
1024
+
1025
+ /** One lane, or two of one link group (#3335): one reveal unit. */ function sameLane(e, o) {
1026
+ return e === o || e.nn !== null && e.nn === o.nn;
1027
+ }
1028
+
1029
+ function commitPendingNode(e) {
1030
+ const o = e;
1031
+ // L2: the commit is where a pass, held or not, stops being uncommitted —
1032
+ // unless it has no answer yet. A pass still in flight (pending, nothing
1033
+ // staged) stays uncommitted: a re-pass disposes its children on the spot,
1034
+ // and the frame it parked stays the frame until a pass of this node
1035
+ // commits a value.
1036
+ if (!o.X) {
1037
+ e.l &= -385;
1038
+ if (e.ue !== NOT_PENDING) {
1039
+ e.le = e.ue;
1040
+ e.ue = NOT_PENDING;
1041
+ }
1042
+ return;
1043
+ }
1044
+ // Computeds only from here (`_statusFlags` is not a signal field). A
1045
+ // commit beneath a flight publishes its inputs (A15 reveal corollary,
1046
+ // #3305): a reveal of it must observe, not show the pre-flight value.
1047
+ const n = e.ue === NOT_PENDING && (o.m & STATUS_PENDING) !== 0;
1048
+ const t = e.l & CONFIG_STAGED_ERROR;
1049
+ // An answer commits: the error it replaces leaves the display (D-41).
1050
+ if (!n && o.T) o.T.Te = null;
1051
+ e.l = n ? e.l & -1048833 | CONFIG_INPUTS_PUBLISHED : e.l & -3147137;
1052
+ // D-41: the staged answer is an error — published now, when a value would
1053
+ // show, through the ordinary error path (`stageError`). Its readers first
1054
+ // see it in the cycle after this seam: a tracked re-read there is not a
1055
+ // retry (`read`), or a boundary re-derived by it would retry it forever.
1056
+ if (t) {
1057
+ const n = e.ue;
1058
+ e.ue = NOT_PENDING;
1059
+ e.ie = clock + 1;
1060
+ notifyStatus(o, STATUS_ERROR, n);
1061
+ } else if (e.ue !== NOT_PENDING) {
1062
+ e.le = e.ue;
1063
+ e.ue = NOT_PENDING;
1064
+ // A node born staged (recompute) initializes at this commit.
1065
+ o.m &= ~STATUS_UNINITIALIZED;
1066
+ // A quiet re-ask's classification survives its landing and dies with
1067
+ // the landed value's commit (A19 exc. 2, #3178): verdict-quiet through
1068
+ // the reveal. (The sweep of a node still in flight is not a landing.)
1069
+ // The A28 stash goes with it.
1070
+ if (o.T != null) o.T.ut = false, o.T.Ft = NOT_PENDING;
1071
+ // An effect with a staged value was born held (L2): its first run is
1072
+ // this commit's, not its creation's (A29) — queue it now.
1073
+ if (e.i && e.i !== EFFECT_TRACKED) {
1074
+ e.Qe = true;
1075
+ globalQueue.enqueue(e.i, e.W ??= GlobalQueue.q.bind(null, o));
1076
+ }
1077
+ }
1078
+ // The committed value is the first observable answer for a loading-window
1079
+ // node — the window closes here, not at compute time (#2990). Unconditional
1080
+ // store to an always-present computed slot.
1081
+ o.ce = false;
1082
+ o.R &= ~REACTIVE_MANUAL_WRITE;
1083
+ // The dependencies of the pass that produced the value are the frame's now:
1084
+ // the previous frame's tail goes (A30, #3410; `recompute` left it for a
1085
+ // staged pass). Only after a clean pass: `_error` is cleared by a clean
1086
+ // pass or by the node's own landing (whose pass was clean), so a set
1087
+ // `_error` means the last pass threw, kept its full list, and `_depsTail`
1088
+ // marks where it stopped.
1089
+ if (o.T?.U == null) trimStaleDeps(o);
1090
+ if (!(o.m & STATUS_PENDING)) o.m &= ~STATUS_UNINITIALIZED;
1091
+ // L2: the children this commit publishes are the frame's now — the frame
1092
+ // they replace, parked by the pass (`recompute`), goes.
1093
+ if (!n && o.T != null && (o.T.ft !== null || o.T.Tt !== null)) GlobalQueue.et(o, false, true);
1094
+ }
1095
+
1096
+ function commitPendingNodes() {
1097
+ for (let e = 0; e < pendingNodes.length; e++) {
1098
+ const o = pendingNodes[e];
1099
+ // A node a lane pass re-staged this round is the lane's to reveal; one
1100
+ // a transaction holds (born held by a mainline pass, `passTx`) is that
1101
+ // transaction's to commit, at its landing.
1102
+ if (!(o.l & (CONFIG_OVERRIDE | CONFIG_HELD))) commitPendingNode(o);
1103
+ }
1104
+ pendingNodes.length = 0;
1105
+ }
1106
+
1107
+ const globalQueue = new GlobalQueue;
1108
+
1109
+ function flush(e) {
1110
+ if (actionDepth > 0) {
1111
+ return e ? e() : undefined;
1112
+ }
1113
+ if (e) {
1114
+ syncDepth++;
1115
+ try {
1116
+ return e();
1117
+ } finally {
1118
+ // Decrement even if the drain throws (a throwing effect): a leaked
1119
+ // syncDepth would stop `schedule()` from ever queuing a microtask again.
1120
+ try {
1121
+ flush();
1122
+ } finally {
1123
+ syncDepth--;
1124
+ }
1125
+ }
1126
+ }
1127
+ if (globalQueue.Y) {
1128
+ return;
1129
+ }
1130
+ if (halted) return;
1131
+ // Attribution: whether this call drained anything, so `flushEnd` fires once
1132
+ // per real drain and never for a no-op call. The declaration is dead in
1133
+ // prod (its only write is behind true) and rollup drops it.
1134
+ let o = false;
1135
+ // The drain's opening instant, under the loop's own condition so it fires
1136
+ // exactly when `flushEnd` below will. Outside every try (see the rule in
1137
+ // attribution-hooks.ts).
1138
+ if (attrHooks !== null && scheduled) attrHooks.flushStart();
1139
+ while (scheduled) {
1140
+ globalQueue.flush();
1141
+ o = true;
1142
+ }
1143
+ // Outside every try in this function (see the rule in attribution-hooks.ts):
1144
+ // the drain loop above is the one place all scheduled work funnels through,
1145
+ // so this is the "committed and effects ran" instant for everything the
1146
+ // loop processed.
1147
+ if (o && attrHooks !== null) attrHooks.flushEnd();
1148
+ // A cleanup that threw during the drain, now that the drain is complete
1149
+ // (owner.ts).
1150
+ rethrowCleanupError();
1151
+ }
1152
+
1153
+ function runQueue(e, o) {
1154
+ for (let n = 0; n < e.length; n++) e[n](o);
1155
+ }
1156
+
1157
+ export { GlobalQueue, MAINLINE_QUESTION, ROOT_ERROR_HOOK, actionDepth, applyGuessesHook, armGuessing, blocked, blockersOf, boundarySeamHook, bumpNotifyEpoch, catchHook, clock, commitPendingNode, deferZombie, dirtyQueue, endLanesHook, enforceLoadingBoundary, enterAction, exitAction, externalUntrackHook, flush, flushTransaction, freshHook, globalQueue, guessing, haltReactivity, haltReactivityAt, heldRunHook, heldTrim, heldUnder, hiddenHook, holdNode, holdWorkHook, insertSubs, installBoundaryHooks, installLaneHooks, installMarkHooks, installStoreHooks, installVerdictHooks, joinFuture, joinPassTx, laneAdoptHook, laneCorrectionsHook, laneDirty, laneOutcomeHook, laneReadHook, laneSeamsHook, laneServedHook, laneStageHook, laneWriteHook, lanesBlockedHook, latestOpened, list, liveTx, merge, newTransaction, nextQuestion, notifyEpoch, observeFlightHook, ownFlights, passLane, passTx, pendingNodes, question, queuePendingNode, releaseAmbientMarksHook, releaseMarksHook, releaseQueues, reruns, resetErrorHalt, resolveTx, sameLane, schedule, setExternalHooks, setPassLane, setQuestion, stagedReaders, staleReader, storeCommitHook, storeParkHook, txOf, unhold, wireExternalSourceHook };