@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,696 @@
1
+ import { STATUS_PENDING, STATUS_ERROR, CONFIG_REDERIVE, CONFIG_HELD, CONFIG_FRAME, STATUS_UNINITIALIZED, EFFECT_RENDER, CONFIG_VERDICT, REACTIVE_DISPOSED, REACTIVE_LANE_READ, REACTIVE_PROBED, CONFIG_STAGED_ERROR, CONFIG_OVERRIDE, EFFECT_USER, EFFECT_TRACKED, REACTIVE_ZOMBIE, CAUGHT_REARMED } from "./core/constants.js";
2
+
3
+ import { signal, runWithOwner, computed, read, setSignal, recompute } from "./core/core.js";
4
+
5
+ import { reportDiagnostic, emitDiagnostic } from "./core/dev.js";
6
+
7
+ import { NotReadyError, unwrapStatusError } from "./core/error.js";
8
+
9
+ import { reportClientError } from "./core/error-hooks.js";
10
+
11
+ import { link } from "./core/graph.js";
12
+
13
+ import { enqueueSub } from "./core/heap.js";
14
+
15
+ import { createOwner, getOwner, cleanup, dispose } from "./core/owner.js";
16
+
17
+ import { globalQueue, haltReactivityAt, haltReactivity, schedule, passLane, joinFuture, GlobalQueue, flushTransaction, heldUnder, setPassLane, passTx, resolveTx, txOf, unhold, laneStageHook, installBoundaryHooks } from "./core/scheduler.js";
18
+
19
+ import { attrHooks } from "./core/attribution-hooks.js";
20
+
21
+ import { flatten } from "./flatten.js";
22
+
23
+ import { accessor } from "./signals.js";
24
+
25
+ /**
26
+ * Boundaries — the display consumers between a frame reader and the root:
27
+ * `createLoadingBoundary` (`<Loading>`) and `createErrorBoundary`
28
+ * (`<Errored>`).
29
+ *
30
+ * A boundary is an owner whose context names it (`BOUNDARY`), so every node
31
+ * created under it finds it in O(1) and the enclosing boundaries through
32
+ * `_parent`; a `tree` (the content, flattened) and an `output` that shows
33
+ * the tree or the fallback. The output is a frame reader (CONFIG_REDERIVE):
34
+ * status reaching it from its content re-derives it instead of marking it,
35
+ * and its pass decides.
36
+ *
37
+ * Status protocol (B5). A frame reader under a boundary that goes pending or
38
+ * errors tells `GlobalQueue.notify`, which asks `catchStatus` before the
39
+ * root: the nearest boundary of that status that is collecting — an error
40
+ * boundary always; a loading boundary that has not shown content yet, or
41
+ * whose `on` just changed — records the reader and shows its fallback, and
42
+ * the root never hears of it (no transaction is opened: A33, a fallback-
43
+ * caught flight holds nothing). A loading boundary showing content forwards:
44
+ * the root holds the frame as for any reader. The tree's own status takes
45
+ * the same route from the output's pass (`read(tree)` throws it).
46
+ *
47
+ * The hold (`blocked`, scheduler.ts) asks `hiddenBy`: a reader behind a
48
+ * fallback — any boundary on its chain whose last pass chose the fallback —
49
+ * is not on screen and holds nothing. An error boundary flipping to its
50
+ * fallback is the same as a loading one resetting: every reader under it
51
+ * stops counting, and a flight whose only visible reader that was loses its
52
+ * hold (maintainer, 2026-10-01).
53
+ *
54
+ * Reveal. The seam (`boundarySeam`) drops readers that settled, died, or
55
+ * landed and committed; a fallback with none left re-derives its output,
56
+ * which reads the tree again. A reader that landed but is held (CONFIG_HELD,
57
+ * its value staged in a transaction) is not settled: the content reveals at
58
+ * the commit, with the rest of the frame (#3540).
59
+ *
60
+ * `on` (loading only): a tracked function whose reads re-arm the boundary.
61
+ * A pass of it after the mount arms the boundary for the flush: anything
62
+ * pending under it — already (its forwarded readers), or going pending later
63
+ * in the same heap — is collected and the fallback shows now, with the
64
+ * change's frame; nothing pending, and the arm is a no-op at the seam.
65
+ */
66
+ /** Context key: the nearest boundary of a node, inherited at creation. */ const BOUNDARY = Symbol("");
67
+
68
+ /** Context key: the reveal controller a Loading boundary created here is a
69
+ * slot of (reveal.ts); a boundary clears it for its content — only direct
70
+ * children are slots. */ const REVEAL = Symbol("");
71
+
72
+ // Every call site checks the hooks themselves first (not only the slot or
73
+ // controller, which are never set without them): a build that does not
74
+ // import `createRevealOrder` folds the reveal-order arms away.
75
+ let revealHooks = null;
76
+
77
+ function setRevealHooks(e) {
78
+ revealHooks = e;
79
+ }
80
+
81
+ /** Boundaries with readers to sweep (or an arm to resolve) at the seam. */ const collecting = new Set;
82
+
83
+ const boundaryOf = e => e.A[BOUNDARY];
84
+
85
+ /** Catches what reaches it: an error boundary always; a loading one until it
86
+ * has shown content, or while re-armed. */ const isCollecting = e => e.i === STATUS_ERROR || !e.D || e.h;
87
+
88
+ /** A reader `b` waits on: recorded for its `on`, and `b` visited at the seam. */ function record(e, n, r = n) {
89
+ e.F.set(n, r);
90
+ collecting.add(e);
91
+ }
92
+
93
+ /** The reader goes to the fallback: recorded, and the output re-derives
94
+ * (a no-op while the output's own pass is running — its post-check
95
+ * decides). */ function caught(e, n, r, o = n) {
96
+ if (e.i === STATUS_ERROR) {
97
+ // An error boundary waits on what THREW (`StatusError.source`), not on
98
+ // the reader that saw it, while the error is settled only when its
99
+ // source is neither errored nor in flight and the reader is not pending
100
+ // on the answer that replaces it (`prune`) — or when the
101
+ // reader is disposed: a source no live reader holds holds nothing
102
+ // (#4373). A source that died (a memo its reader owned, kept past the
103
+ // pass that disposed it) settles nothing: the reader is waited on.
104
+ // `reset` re-runs the source.
105
+ r ??= n.T?.U;
106
+ o = r?.source ?? n;
107
+ if (o.R & REACTIVE_DISPOSED) o = n;
108
+ const t = unwrapStatusError(r);
109
+ setSignal(e.U, t);
110
+ reportClientError(t, e.H, o);
111
+ }
112
+ record(e, n, o);
113
+ // A loading fallback behind an error fallback is hidden: the error stays
114
+ // the display until what the fallback waits on answers (D-39) — the
115
+ // error boundary waits on it too.
116
+ // (`_type`: STATUS_PENDING 1 < STATUS_ERROR 2 — an error boundary above a
117
+ // loading one.)
118
+ for (let r = e; r = r._parent; ) if (r.L && r.i > e.i) record(r, n);
119
+ if (e.h) flip(e);
120
+ // Already showing the fallback: one more reader to wait on changes
121
+ // nothing on screen. The fallback is instantiated once per show — a later
122
+ // error reaches it through the `error` accessor, which is why it is one.
123
+ if (!e.L) redraw(e);
124
+ if (revealHooks && e.v !== null) revealHooks.P(e.v);
125
+ }
126
+
127
+ /** A re-armed boundary found something pending: fresh again, and the swap
128
+ * the arming pass asked for is that pass's lane's (if any): the output is
129
+ * the lane's work from its next pass's head — not a re-pass of the
130
+ * transaction holding its pending content, which would entangle that
131
+ * transaction with the arming frame's (#3540 `on: () => latest(dep)`). */ function flip(e) {
132
+ e.D = false;
133
+ e.V = e.B;
134
+ const n = e.j;
135
+ if (!e.V && n.l & CONFIG_HELD && txOf(n) !== flushTransaction) unhold(n);
136
+ if (laneStageHook && e.V !== null && e.j !== null) laneStageHook(e.j, e.V, true, false);
137
+ }
138
+
139
+ function redraw(e) {
140
+ if (e.j !== null && !(e.j.R & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE))) {
141
+ enqueueSub(e.j);
142
+ schedule();
143
+ }
144
+ }
145
+
146
+ /** `catchHook` (scheduler.ts): status from a frame reader, nearest boundary first.
147
+ * A loading boundary on the way records a pending reader whether or not it
148
+ * catches it (its `on` may collect it later). */ function catchStatus(e, n, r) {
149
+ if (n === 0) {
150
+ // A status cleared. Judged by the node's status now, against each
151
+ // boundary's own rule (`prune`'s mask) — the error path clears pending
152
+ // first and the node is still errored then. The last reader a fallback
153
+ // waited on settling re-derives the output now, in the same heap (a
154
+ // held reader's landing has joined its transaction, so the content
155
+ // lands with its run, not a round after). Others still waiting: nothing
156
+ // on screen changes, the fallback stays as it is. `node` here is an
157
+ // effect reporting its own status. An error boundary records an effect
158
+ // only while it is errored, and a staged error keeps STATUS_ERROR on a
159
+ // node that had it (`stageError`), so `prune`'s CONFIG_STAGED_ERROR case
160
+ // cannot reach this path (#4364).
161
+ if (collecting.size !== 0) for (let n = boundaryOf(e); n; n = n._parent) if (!(n.F.get(e)?.m & (n.i | STATUS_PENDING)) && n.F.delete(e) && n.F.size === 0) {
162
+ if (n.L) redraw(n);
163
+ if (revealHooks && n.v !== null) revealHooks.P(n.v);
164
+ }
165
+ return false;
166
+ }
167
+ for (let o = boundaryOf(e); o; o = o._parent) {
168
+ if (!(o.i & n)) continue;
169
+ if (isCollecting(o)) {
170
+ caught(o, e, r);
171
+ return o.h ? CAUGHT_REARMED : true;
172
+ }
173
+ record(o, e);
174
+ }
175
+ return false;
176
+ }
177
+
178
+ /** The nearest loading boundary that would catch `node` was re-armed this
179
+ * flush. */ function armedOver(e) {
180
+ for (let n = boundaryOf(e); n; n = n._parent) if (n.i & STATUS_PENDING && isCollecting(n)) return n.h;
181
+ }
182
+
183
+ /** The nearest boundary on `r`'s chain showing its fallback, if any
184
+ * (`hiddenHook`: `r` is behind a fallback). */ function hiddenBy(e) {
185
+ for (let n = boundaryOf(e); n; n = n._parent) if (n.L) return n;
186
+ return undefined;
187
+ }
188
+
189
+ /** `heldRunHook` (scheduler.ts): a queued run behind a fallback waits for the reveal
190
+ * (maintainer, 2026-10-02: the queue is held, the synchronous first render
191
+ * goes through). A user effect's, which would read a DOM that is not
192
+ * attached ("many depend on reading the DOM, something that can't happen
193
+ * off screen"); a render effect's update too — a Portal or a head-tag
194
+ * library writes outside the hidden subtree, and a queued render run is
195
+ * what mounts them. A user effect's first run is scheduled like every other,
196
+ * so a mount under a fallback defers it too; a render effect's first run is
197
+ * synchronous on creation and never comes here. The run's `_modified` stays
198
+ * set; the reveal re-queues it by type (`release`), and a boundary above
199
+ * still showing its fallback holds it again then. */ function heldRun(e) {
200
+ const n = hiddenBy(e);
201
+ if (n === undefined) return false;
202
+ (n.M ??= new Set).add(e);
203
+ return true;
204
+ }
205
+
206
+ /** The reveal: the runs held behind the fallback are this flush's, each in
207
+ * its own phase — render updates with the frame that attaches the content,
208
+ * user effects after it. */ function release(e) {
209
+ if (e.M === null) return;
210
+ for (const n of e.M) {
211
+ const e = n.i;
212
+ globalQueue.enqueue(e === EFFECT_TRACKED ? EFFECT_USER : e, e === EFFECT_TRACKED ? n.Z : n.W ??= GlobalQueue.q.bind(null, n));
213
+ }
214
+ e.M = null;
215
+ }
216
+
217
+ /** Drop the readers that settled or died. Settled: a loading reader no
218
+ * longer pending; an error's source neither errored nor in flight. A reader
219
+ * that landed but is held (its value staged in a transaction) is not: the
220
+ * content reveals at the commit, with its run. The tree is: the output reads
221
+ * it and enters the transaction itself (its committed value shows until the
222
+ * landing). The statuses that keep a reader: pending — and its error, for an
223
+ * error boundary (`_type` is the status it catches) — on the reader or its
224
+ * source. A reader pending again after the source that threw answered (it
225
+ * re-asks through a memo that carried the error) has no new answer yet: the
226
+ * error stays the display (D-39: a pending is not a value; async-gen 91501
227
+ * #1831). */ function prune(e, n) {
228
+ const r = e.i | STATUS_PENDING;
229
+ for (const [o, t] of e.F) {
230
+ if (!((o.R | t.R) & REACTIVE_DISPOSED)) {
231
+ // An error staged on its source (D-41, `stageError`) is not settled:
232
+ // it publishes with the commit. A reader that threw it now (a lane
233
+ // pass, a reader of a node born staged) reached the boundary first
234
+ // (#4364).
235
+ if ((o.m | t.m) & r || r & STATUS_ERROR && t.l & CONFIG_STAGED_ERROR) continue;
236
+ if ((t !== e.K || e.h) && (t.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD) {
237
+ // Landed, held: the content is that transaction's — the output's
238
+ // pass enters it and the reveal lands with the reader's run. The
239
+ // seam, past its park decision, waits for the commit instead. (A
240
+ // lane's reader reveals with the lane, this round.) A boundary that
241
+ // has not shown content catches it instead: the fallback stays until
242
+ // the reader is committed (#3540: what a fresh boundary catches holds
243
+ // nothing outside it).
244
+ if (!n || !e.D) continue;
245
+ joinFuture(txOf(t));
246
+ }
247
+ }
248
+ e.F.delete(o);
249
+ }
250
+ return e.F.size;
251
+ }
252
+
253
+ /** Reveal order: a slot is ready when its content can show — it has shown
254
+ * content, or nothing under it is unready (the tree neither pending nor
255
+ * born held, no reader waited on). */ function ready(e) {
256
+ return e.D || !(e.K.m & STATUS_PENDING) && !(e.K.l & CONFIG_HELD && (e.j === null || e.j.m & STATUS_UNINITIALIZED)) && prune(e, false) === 0;
257
+ }
258
+
259
+ /** `boundarySeamHook` (scheduler.ts), end of the seam: readers that settled, died,
260
+ * or landed and committed are dropped; a fallback with none left reveals
261
+ * next round. An arm resolves: nothing collected, nothing happened. */ function boundarySeam() {
262
+ for (const e of collecting) {
263
+ const n = e.j.R;
264
+ if (n & REACTIVE_DISPOSED) {
265
+ collecting.delete(e);
266
+ continue;
267
+ }
268
+ // A parked frame's boundary (zombie) keeps its state for its revival.
269
+ if (n & REACTIVE_ZOMBIE) continue;
270
+ prune(e, false);
271
+ const r = e.j;
272
+ // A fallback staged into the very hold its readers landed in folds back
273
+ // (D-16, U-9): the content is ready when that hold commits (D-15). Not
274
+ // only at the re-arm's own seam: readers pending then land, held, a
275
+ // flush later (#4379 — the staged fallback otherwise commits with the
276
+ // hold, and a quiet refresh then caught keeps it up, F-9).
277
+ if (!e.D && r.l & CONFIG_HELD && [ ...e.F ].every(([, e]) => !(e.m & STATUS_PENDING) && e.l & CONFIG_HELD && txOf(e) === txOf(r))) {
278
+ e.F.clear();
279
+ e.D = true;
280
+ }
281
+ e.h = false;
282
+ if (e.F.size === 0) {
283
+ collecting.delete(e);
284
+ if (e.L) redraw(e);
285
+ if (revealHooks && e.v !== null) revealHooks.P(e.v);
286
+ }
287
+ }
288
+ }
289
+
290
+ /** `on` changed after the mount: fresh again if anything under it is
291
+ * pending now; collecting for the rest of the flush either way. */ function arm(e) {
292
+ if (!e.D) {
293
+ // Already re-armed and on its fallback, as the frame's work — and the
294
+ // `on` pass arms again as lane work: its verdict was provisional (a
295
+ // memo of `isPending(m2)` is a watcher until the flush parks, then the
296
+ // holder's verdict lane's — verdict.ts), and the swap it asked for is
297
+ // display-ahead after all (#3528). Re-home the swap: the output re-runs
298
+ // as the lane's, shown now beside the held frame.
299
+ if (e.L && e.V === null && e.B === null && passLane !== null) {
300
+ e.B = passLane;
301
+ // The swap the frame's pass staged (held with the frame) is void: the
302
+ // lane's pass stages it anew (`flip` lists the output in the lane,
303
+ // which voids its staging), as a watcher's provisional value is
304
+ // voided at the seam (verdict.ts `verdictSeam`).
305
+ flip(e);
306
+ redraw(e);
307
+ }
308
+ return;
309
+ }
310
+ e.h = true;
311
+ // The swap is the change's work: the output joins it (D-13, #3540).
312
+ e.j.l &= ~CONFIG_FRAME;
313
+ // A verdict read in `on` (`isPending`, or a memo of one — #3528's
314
+ // `createMemo(() => isPending(m2))`: the mark carries, core.ts `read`)
315
+ // whose frame has no transaction yet: the swap is still display-ahead — the frame gets one now (it lands
316
+ // at this very seam if nothing holds it), and the swap is its verdict
317
+ // lane's work, shown ahead of the frame's own runs (#3528).
318
+ let n = passLane;
319
+ if (n === null && globalQueue.Y && getOwner().l & CONFIG_VERDICT) {
320
+ joinFuture(null);
321
+ n = GlobalQueue.J(flushTransaction);
322
+ }
323
+ e.B = n;
324
+ collecting.add(e);
325
+ const r = e.F.size;
326
+ heldUnder(e.H, flushTransaction, n => record(e, n));
327
+ for (const [n] of e.F) {
328
+ if (e.F.size !== r || n.m & STATUS_PENDING && !(n.R & REACTIVE_DISPOSED)) {
329
+ // What it collects was forwarded through it to the boundaries above
330
+ // too (`catchStatus` records a reader in every boundary on the way).
331
+ // It is this boundary's alone now (B5, the nearest catcher): an outer
332
+ // re-armed by the same tick does not wait for it (D-14, U-4).
333
+ for (let n = e._parent; n !== null; n = n._parent)
334
+ // An outer error boundary keys its readers too, but waits on their
335
+ // sources: a re-armed Loading taking the pending does not settle it.
336
+ if (n.i & STATUS_PENDING) for (const [r] of e.F) n.F.delete(r);
337
+ flip(e);
338
+ redraw(e);
339
+ return;
340
+ }
341
+ }
342
+ }
343
+
344
+ /** The error boundary's `reset`: re-run what threw (a comparator throw
345
+ * names a signal — nothing to re-run). */ function reset(e) {
346
+ // A snapshot: a source failing again is caught — and re-added — mid-walk.
347
+ for (const n of new Set(e.F.values())) if (n.X !== undefined && !(n.R & REACTIVE_DISPOSED)) recompute(n);
348
+ schedule();
349
+ }
350
+
351
+ /**
352
+ * Dev CHECK: the `on` dependencies evaluated to a function — `<Loading
353
+ * on={route}>` or `on={() => route()}` passes the accessor itself, a stable
354
+ * value that is never called, so nothing is read and the boundary never
355
+ * re-arms. Checked on the first run only: the shape is static.
356
+ */ function reportOnAccessor(e) {
357
+ reportDiagnostic(emitDiagnostic({
358
+ code: "LOADING_ON_ACCESSOR",
359
+ kind: "async",
360
+ severity: "warn",
361
+ message: "[LOADING_ON_ACCESSOR] A Loading boundary's `on` received a function. `on` takes the " + "dependency's value — `on={route()}`, or `on={[query(), page()]}` for several — and an " + "accessor passed instead is never called, so nothing is read and the boundary never re-arms."
362
+ }, e.H));
363
+ }
364
+
365
+ function createBoundary(e, n, r, o) {
366
+ const t = createOwner();
367
+ const l = {
368
+ i: e,
369
+ _parent: boundaryOf(t) ?? null,
370
+ H: t,
371
+ K: null,
372
+ j: null,
373
+ F: new Map,
374
+ D: false,
375
+ h: false,
376
+ B: null,
377
+ V: null,
378
+ L: false,
379
+ M: null,
380
+ v: null,
381
+ $: false,
382
+ ee: false,
383
+ U: e === STATUS_ERROR ? signal(undefined, ERROR_SIGNAL) : null,
384
+ ne: r,
385
+ re: null
386
+ };
387
+ const a = e === STATUS_PENDING ? t.A[REVEAL] : null;
388
+ if (getOwner()) cleanup(() => {
389
+ collecting.delete(l);
390
+ if (revealHooks && a) revealHooks.oe(a, l);
391
+ });
392
+ // The `on` dependencies live OUTSIDE the boundary, as the condition of a
393
+ // `<Show>` wrapping it would: created before the owner's context names it.
394
+ // A source of it going pending re-runs it (CONFIG_REDERIVE) rather than
395
+ // marking it: the pass catches the NotReady — it is a notification, it
396
+ // does not suspend the parent — and re-arms.
397
+ if (o) {
398
+ let e = false;
399
+ runWithOwner(t, () => computed(() => {
400
+ try {
401
+ const n = o();
402
+ if (false && !e && typeof n === "function") ;
403
+ } catch (e) {
404
+ if (!(e instanceof NotReadyError)) {
405
+ if (!globalQueue.notify(getOwner(), STATUS_ERROR, STATUS_ERROR, e)) {
406
+ if (false) ; else haltReactivity(e);
407
+ throw e;
408
+ }
409
+ return;
410
+ }
411
+ }
412
+ if (e) arm(l); else e = true;
413
+ })).l |= CONFIG_REDERIVE;
414
+ }
415
+ const u = {
416
+ ...t.A,
417
+ [BOUNDARY]: l
418
+ };
419
+ if (revealHooks !== null) u[REVEAL] = null;
420
+ t.A = u;
421
+ const s = runWithOwner(t, () => {
422
+ const e = true ? computed(n, {
423
+ name: "children"
424
+ }) : computed(n);
425
+ return true ? computed(() => flatten(read(e)), {
426
+ name: "boundary"
427
+ }) : computed(() => flatten(read(e)));
428
+ });
429
+ l.K = s;
430
+ // A slot of the reveal order in its context (reveal.ts): gated until the
431
+ // controller releases it — before the output's first pass.
432
+ if (revealHooks && a) revealHooks.te(a, l);
433
+ const i = computed(() => {
434
+ // Structure, not a derivation of its content (CONFIG_FRAME, #4283): it
435
+ // reads a hold it is not part of as the screen, as the render effect
436
+ // flattening the same children without a boundary would. Not while a
437
+ // re-arm's swap is the change's work (`arm`), until that swap commits
438
+ // (D-13, #3540).
439
+ const n = getOwner();
440
+ if (!l.h && !(n.l & CONFIG_HELD)) n.l |= CONFIG_FRAME;
441
+ // Reveal order (reveal.ts): a gated slot shows its fallback — or, in a
442
+ // collapsed tail, nothing — whatever its content's state; the
443
+ // controller is told the pass ran (its readiness may have changed) and
444
+ // releases it in turn.
445
+ if (l.$) {
446
+ // Linked without being read: the tree settling (or changing)
447
+ // re-derives this pass, which is how the controller hears of it.
448
+ // It may release this very slot (the frontier whose content just
449
+ // settled): the pass goes on to show it — the redraw it asks for is
450
+ // refused mid-pass and not needed.
451
+ link(s, n);
452
+ if (revealHooks && l.v !== null) revealHooks.P(l.v);
453
+ if (l.$) {
454
+ if (l.ee) {
455
+ l.L = true;
456
+ return undefined;
457
+ }
458
+ return fallback(l);
459
+ }
460
+ }
461
+ // A tree carrying a status is not read: an errored memo's reactive
462
+ // re-read retries it (`read`), and neither a fallback re-rendering nor
463
+ // passing the error by may re-run the content; a pending one read as
464
+ // lane work would serve its committed value (A31) — the swap a
465
+ // display-ahead `on` causes is the lane's, and must still be the
466
+ // fallback. Linked, so its recovery re-derives this pass; read again
467
+ // once clean.
468
+ const r = s.m & (STATUS_PENDING | STATUS_ERROR);
469
+ if (r !== 0) {
470
+ link(s, n);
471
+ if (r & e) {
472
+ if (isCollecting(l)) {
473
+ caught(l, s, undefined);
474
+ return fallback(l);
475
+ }
476
+ // Forwarded: a loading boundary showing content keeps it and
477
+ // holds the frame like any reader (recorded for its `on`).
478
+ record(l, s);
479
+ }
480
+ // A pending passes an error boundary by (A6); an error, a loading
481
+ // one; a loading boundary showing content forwards its pending.
482
+ throw s.T.U;
483
+ }
484
+ if (isCollecting(l)) {
485
+ // A29's boundary exemption (#3540): a boundary MOUNTED over a held
486
+ // value (its first pass; a first pass under it that read a hold was
487
+ // collected, `joinPass` — the tree's own, or a render effect's that
488
+ // binds the content) shows its fallback now and the content at the
489
+ // commit — entering the transaction would make the output itself
490
+ // born held, and nothing would show until the commit. The seam
491
+ // keeps a held reader until it is committed, then re-derives this
492
+ // pass. A boundary with a committed value reads a held tree and
493
+ // enters: the outside sees its committed value until the landing,
494
+ // which reveals the content — a fallback staged earlier is replaced
495
+ // ahead of the commit and never shown.
496
+ if (l.F.size !== 0 && n.m & STATUS_UNINITIALIZED && !(n.l & CONFIG_HELD)) return fallback(l);
497
+ // Readers under it still unready: the fallback, the tree untouched.
498
+ // The seam re-derives this pass when they settle.
499
+ if (prune(l, true) !== 0) return fallback(l);
500
+ }
501
+ let o;
502
+ try {
503
+ o = read(s);
504
+ } catch (n) {
505
+ if (n instanceof NotReadyError ? e === STATUS_PENDING : e === STATUS_ERROR) {
506
+ if (isCollecting(l)) {
507
+ caught(l, s, n);
508
+ return fallback(l);
509
+ }
510
+ // Forwarded: a loading boundary showing content keeps it and
511
+ // holds the frame like any reader (recorded for its `on`).
512
+ record(l, s);
513
+ }
514
+ // A pending passes an error boundary by (A6).
515
+ throw n;
516
+ }
517
+ // Readers under it still unready (caught before, or while the read
518
+ // ran the content at a mount): the fallback.
519
+ if (isCollecting(l) && l.F.size !== 0) return fallback(l);
520
+ l.D = true;
521
+ // Observe: the fallback stops showing (a hide before its commit means
522
+ // it never displayed — the engine knows the swap it was staged with).
523
+ if (l.L && l.i & STATUS_PENDING && attrHooks !== null) attrHooks.boundaryFallback(l, l.K, false);
524
+ l.L = false;
525
+ // The fallback dies when the content that replaces it is on screen —
526
+ // with this flush's commits when the pass is the frame's, at the
527
+ // landing when the frame parks (the run is held with it, as the old
528
+ // children of a held pass are) — not now: a held swap keeps the
529
+ // fallback on screen, live, until it commits. A pass back to the
530
+ // fallback before then finds it still built and keeps it. A fallback
531
+ // the screen does not show (a swap staged with a held frame, replaced
532
+ // before it commits) dies now: what it reads holds nothing (S-22 row
533
+ // 3′, #4268).
534
+ const t = l.re;
535
+ const drop = () => {
536
+ if (!l.L && l.re === t) {
537
+ // A fallback that threw (a rethrow the boundary does not
538
+ // handle) is what an enclosing boundary waits on: it died, and
539
+ // the seam of another round drops it there (#4270).
540
+ if (t.m & STATUS_ERROR) schedule();
541
+ dispose(t);
542
+ l.re = null;
543
+ }
544
+ };
545
+ if (t !== null) l.j.le !== t.le ? drop() : globalQueue.enqueue(EFFECT_RENDER, drop);
546
+ release(l);
547
+ // Shown: the slot leaves its reveal order (a later re-arm gates nobody
548
+ // else; the frontier moves on).
549
+ if (revealHooks && l.v !== null) {
550
+ const e = l.v;
551
+ l.v = null;
552
+ revealHooks.P(e);
553
+ }
554
+ return o;
555
+ },
556
+ // Boundary structure, not a user source: its value is fallback-or-content
557
+ // and legitimately swaps mid-hydration (resume), so it must never be
558
+ // frozen by snapshot capture.
559
+ {
560
+ name: "value",
561
+ ae: true
562
+ });
563
+ i.l |= CONFIG_REDERIVE;
564
+ // The tree is structure too (the output's pass marks the output).
565
+ s.l |= CONFIG_FRAME;
566
+ l.j = i;
567
+ return accessor(i);
568
+ }
569
+
570
+ function fallback(e) {
571
+ // (The output's pass.)
572
+ const n = getOwner();
573
+ // The swap a display-ahead re-arm asked for: this pass is the lane's — a
574
+ // lane read by construction (REACTIVE_LANE_READ: `recompute`'s tail keeps
575
+ // the pass in the lane).
576
+ if (e.V !== null) {
577
+ setPassLane(e.V);
578
+ n.R |= REACTIVE_LANE_READ;
579
+ e.V = null;
580
+ }
581
+ // Observe: a loading boundary starts showing its fallback — the swap lands
582
+ // with the frame's transaction, or this drain commits it (lane work, or no
583
+ // transaction). A mount's synchronous first show has no drain of its own:
584
+ // one is scheduled so the engine's display instant (`flushEnd`) comes.
585
+ if (!e.L && e.i & STATUS_PENDING && attrHooks !== null) {
586
+ attrHooks.boundaryFallback(e, e.K, true, passLane !== null ? null : flushTransaction !== null ? resolveTx(flushTransaction) : passTx);
587
+ if (!globalQueue.Y) schedule();
588
+ }
589
+ e.L = true;
590
+ // Built in its own scope, like `Show`'s function child, once per show
591
+ // (D-39: the fallback is the display until a new answer replaces the
592
+ // error): a re-derive of the output while it shows — a second reader
593
+ // caught, the same error propagated again through a sibling's re-run, a
594
+ // retry pending after `reset()`, a later error — keeps the built
595
+ // fallback, its DOM and focus with it (#3991). A later error reaches it
596
+ // through the error accessor; only a fallback reading the accessor at its
597
+ // top level is rebuilt by it. Created beside the output — in its owner,
598
+ // outside the boundary, as the output is: its errors and runs are the
599
+ // enclosing boundary's — with the output's id, so what it builds gets the
600
+ // ids a build under the output would; disposed once content is on screen.
601
+ if (e.re === null) e.re = runWithOwner(n._parent, () => computed(() => e.ne(e), {
602
+ id: n.id
603
+ }));
604
+ // The swap is structure (#3540): what the fallback reads ahead — a
605
+ // verdict (`latest()`), a lane — is the fallback's display-ahead, not the
606
+ // swap's: the output's pass stays where it was (F-6 stays inside the
607
+ // fallback, #4337).
608
+ const r = passLane;
609
+ const o = n.R;
610
+ const t = read(e.re);
611
+ setPassLane(r);
612
+ n.R ^= (n.R ^ o) & (REACTIVE_PROBED | REACTIVE_LANE_READ);
613
+ return t;
614
+ }
615
+
616
+ const ERROR_SIGNAL = {
617
+ ownedWrite: true,
618
+ ae: true
619
+ };
620
+
621
+ /**
622
+ * Lower-level primitive that backs the `<Loading>` flow control. Catches
623
+ * pending async reads inside `fn` and renders `fallback` until they settle.
624
+ *
625
+ * App code should use `<Loading fallback={...}>` instead — reach for this only
626
+ * when authoring custom boundary components.
627
+ *
628
+ * @param fn the tracked subtree
629
+ * @param fallback the fallback shown while async reads in `fn` are unresolved
630
+ * @param options `on` — a dependency list: a tracked function whose reads
631
+ * re-arm the boundary. Its return value is irrelevant (never compared);
632
+ * what matters is what it reads. Without `on`, a boundary that has shown
633
+ * content keeps it through a refetch (the pending holds with the
634
+ * transaction). With `on`, a write to anything it reads makes the boundary
635
+ * fresh again: it stops waiting on its current content, and if something
636
+ * under it is pending it shows `fallback` until the new content is ready;
637
+ * if nothing is pending, the notification is a no-op. The fallback lands
638
+ * with the same frame as the change that caused it — now, when nothing
639
+ * else holds that frame; together with the rest of the new page during a
640
+ * held navigation, not before it. If the same data is also read outside
641
+ * the boundary, the frame waits on it and the fallback can never be seen
642
+ * (DEV warns `LOADING_ON_OUTSIDE_HOLD`); the fix is structural — move the
643
+ * outside read under the boundary so one hold owns the data. A frame held
644
+ * past the content's landing by something else (the write's action, other
645
+ * pending data) also shows no fallback; that is a race the fallback may
646
+ * lose, a legitimate outcome, and not reported. A display-ahead read in
647
+ * `on` (`latest()`) shows the fallback now, beside the held frame; that
648
+ * is a capability, not the recommended shape. Optimistic writes and a
649
+ * source going pending notify like any other. The children are not
650
+ * re-created — they stay alive behind the fallback.
651
+ *
652
+ * @example
653
+ * ```tsx
654
+ * // Custom boundary component built on top of the primitive.
655
+ * function MyLoading(props: { fallback: JSX.Element; children: JSX.Element }) {
656
+ * return createLoadingBoundary(
657
+ * () => props.children,
658
+ * () => props.fallback
659
+ * ) as unknown as JSX.Element;
660
+ * }
661
+ * ```
662
+ */ function createLoadingBoundary(e, n, r) {
663
+ return createBoundary(STATUS_PENDING, e, () => n(), r?.on);
664
+ }
665
+
666
+ /**
667
+ * Lower-level primitive that backs the `<Errored>` flow control. Catches
668
+ * thrown errors inside `fn` and invokes `fallback(error, reset)` instead.
669
+ * `error` is an accessor for the latest captured error; `reset()` recomputes
670
+ * the failing sources so the boundary can attempt to recover.
671
+ *
672
+ * App code should use `<Errored fallback={...}>` instead — reach for this only
673
+ * when authoring custom boundary components.
674
+ *
675
+ * @example
676
+ * ```tsx
677
+ * // Custom boundary that wraps the primitive and adds telemetry.
678
+ * function TracedErrored(props: { fallback: (e: () => unknown) => JSX.Element; children: JSX.Element }) {
679
+ * return createErrorBoundary(
680
+ * () => props.children,
681
+ * (err, reset) => {
682
+ * reportError(err());
683
+ * return props.fallback(err);
684
+ * }
685
+ * ) as unknown as JSX.Element;
686
+ * }
687
+ * ```
688
+ */ function createErrorBoundary(e, n) {
689
+ return createBoundary(STATUS_ERROR, e, e => n(accessor(e.U), () => reset(e)));
690
+ }
691
+
692
+ // Installed at module evaluation — present exactly when something imports a
693
+ // boundary. An app without one pays the three null checks and nothing else.
694
+ installBoundaryHooks(catchStatus, (e, n) => !n && (e.m & STATUS_UNINITIALIZED ? catchStatus(e, STATUS_PENDING) : armedOver(e)), hiddenBy, boundarySeam, heldRun);
695
+
696
+ export { REVEAL, createErrorBoundary, createLoadingBoundary, prune, ready, redraw, revealHooks, setRevealHooks };