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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/dist/dev-shared.js +882 -405
  2. package/dist/dev.attribution.js +172 -66
  3. package/dist/dev.js +1529 -745
  4. package/dist/observe/affects-marks.js +86 -0
  5. package/dist/observe/affects.js +32 -80
  6. package/dist/observe/boundaries.js +202 -166
  7. package/dist/observe/core/async.js +192 -141
  8. package/dist/observe/core/attribution-queries.js +1 -1
  9. package/dist/observe/core/attribution.js +315 -214
  10. package/dist/observe/core/constants.js +23 -4
  11. package/dist/observe/core/context.js +3 -3
  12. package/dist/observe/core/core.js +532 -360
  13. package/dist/observe/core/dev.js +63 -35
  14. package/dist/observe/core/effect.js +47 -47
  15. package/dist/observe/core/error.js +5 -3
  16. package/dist/observe/core/external.js +5 -6
  17. package/dist/observe/core/graph.js +56 -42
  18. package/dist/observe/core/heap.js +33 -33
  19. package/dist/observe/core/identities.js +62 -0
  20. package/dist/observe/core/lanes.js +242 -229
  21. package/dist/observe/core/owner.js +62 -50
  22. package/dist/observe/core/scheduler.js +322 -168
  23. package/dist/observe/core/truth.js +45 -0
  24. package/dist/observe/core/verdict.js +114 -64
  25. package/dist/observe/deferred.js +184 -0
  26. package/dist/observe/index.js +7 -1
  27. package/dist/observe/map.js +339 -254
  28. package/dist/observe/reveal.js +77 -77
  29. package/dist/observe/signals.js +54 -33
  30. package/dist/observe/store/affects.js +76 -69
  31. package/dist/observe/store/edges.js +39 -0
  32. package/dist/observe/store/hooks.js +13 -0
  33. package/dist/observe/store/index.js +0 -2
  34. package/dist/observe/store/optimistic.js +82 -44
  35. package/dist/observe/store/projection.js +83 -64
  36. package/dist/observe/store/reconcile.js +32 -30
  37. package/dist/observe/store/store.js +219 -123
  38. package/dist/prod/affects-marks.js +86 -0
  39. package/dist/prod/affects.js +33 -79
  40. package/dist/prod/boundaries.js +185 -149
  41. package/dist/prod/core/async.js +176 -135
  42. package/dist/prod/core/constants.js +23 -4
  43. package/dist/prod/core/context.js +3 -3
  44. package/dist/prod/core/core.js +521 -353
  45. package/dist/prod/core/effect.js +66 -66
  46. package/dist/prod/core/error.js +5 -3
  47. package/dist/prod/core/external.js +5 -6
  48. package/dist/prod/core/graph.js +56 -42
  49. package/dist/prod/core/heap.js +38 -38
  50. package/dist/prod/core/identities.js +62 -0
  51. package/dist/prod/core/lanes.js +237 -224
  52. package/dist/prod/core/owner.js +62 -51
  53. package/dist/prod/core/scheduler.js +312 -158
  54. package/dist/prod/core/truth.js +45 -0
  55. package/dist/prod/core/verdict.js +113 -63
  56. package/dist/prod/deferred.js +182 -0
  57. package/dist/prod/index.js +7 -1
  58. package/dist/prod/map.js +366 -281
  59. package/dist/prod/reveal.js +77 -77
  60. package/dist/prod/signals.js +59 -38
  61. package/dist/prod/store/affects.js +76 -69
  62. package/dist/prod/store/edges.js +39 -0
  63. package/dist/prod/store/hooks.js +13 -0
  64. package/dist/prod/store/index.js +0 -2
  65. package/dist/prod/store/optimistic.js +121 -84
  66. package/dist/prod/store/projection.js +46 -34
  67. package/dist/prod/store/reconcile.js +32 -30
  68. package/dist/prod/store/store.js +205 -116
  69. package/dist/types/affects-marks.d.ts +14 -0
  70. package/dist/types/boundaries.d.ts +5 -1
  71. package/dist/types/core/async.d.ts +10 -0
  72. package/dist/types/core/attribution.d.ts +6 -2
  73. package/dist/types/core/constants.d.ts +18 -1
  74. package/dist/types/core/core.d.ts +77 -5
  75. package/dist/types/core/dev.d.ts +43 -13
  76. package/dist/types/core/graph.d.ts +11 -0
  77. package/dist/types/core/identities.d.ts +17 -0
  78. package/dist/types/core/index.d.ts +2 -1
  79. package/dist/types/core/lanes.d.ts +2 -4
  80. package/dist/types/core/owner.d.ts +5 -3
  81. package/dist/types/core/scheduler.d.ts +59 -34
  82. package/dist/types/core/truth.d.ts +16 -0
  83. package/dist/types/core/types.d.ts +21 -6
  84. package/dist/types/core/verdict.d.ts +32 -0
  85. package/dist/types/deferred.d.ts +28 -0
  86. package/dist/types/index.d.ts +6 -0
  87. package/dist/types/map.d.ts +2 -2
  88. package/dist/types/signals.d.ts +28 -7
  89. package/dist/types/store/affects.d.ts +11 -0
  90. package/dist/types/store/edges.d.ts +9 -0
  91. package/dist/types/store/hooks.d.ts +48 -0
  92. package/dist/types/store/index.d.ts +10 -4
  93. package/dist/types/store/optimistic.d.ts +3 -2
  94. package/dist/types/store/projection.d.ts +4 -3
  95. package/dist/types/store/store.d.ts +4 -39
  96. package/dist/types/store/target.d.ts +16 -8
  97. package/dist/types/store/types.d.ts +14 -0
  98. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { STATUS_PENDING, STATUS_ERROR, CONFIG_REDERIVE, REACTIVE_DISPOSED, STATUS_UNINITIALIZED, CONFIG_HELD, REACTIVE_ZOMBIE, CONFIG_VERDICT, REACTIVE_LANE_READ, CONFIG_OVERRIDE, EFFECT_USER, EFFECT_TRACKED } from "./core/constants.js";
1
+ import { STATUS_PENDING, STATUS_ERROR, CONFIG_REDERIVE, STATUS_UNINITIALIZED, CONFIG_HELD, EFFECT_RENDER, CONFIG_VERDICT, REACTIVE_DISPOSED, REACTIVE_LANE_READ, CONFIG_OVERRIDE, EFFECT_USER, EFFECT_TRACKED, REACTIVE_ZOMBIE, CAUGHT_REARMED } from "./core/constants.js";
2
2
 
3
3
  import { signal, runWithOwner, computed, read, setSignal, recompute } from "./core/core.js";
4
4
 
@@ -12,9 +12,9 @@ import { link } from "./core/graph.js";
12
12
 
13
13
  import { enqueueSub } from "./core/heap.js";
14
14
 
15
- import { createOwner, getOwner, cleanup } from "./core/owner.js";
15
+ import { createOwner, getOwner, cleanup, dispose } from "./core/owner.js";
16
16
 
17
- import { globalQueue, haltReactivity, passLane, joinFuture, GlobalQueue, flushTransaction, schedule, setPassLane, passTx, resolveTx, txOf } from "./core/scheduler.js";
17
+ import { globalQueue, haltReactivity, passLane, joinFuture, GlobalQueue, flushTransaction, heldUnder, setPassLane, passTx, resolveTx, schedule, txOf, unhold, laneStageHook, installBoundaryHooks } from "./core/scheduler.js";
18
18
 
19
19
  import { attrHooks } from "./core/attribution-hooks.js";
20
20
 
@@ -44,7 +44,7 @@ import { accessor } from "./signals.js";
44
44
  * the root holds the frame as for any reader. The tree's own status takes
45
45
  * the same route from the output's pass (`read(tree)` throws it).
46
46
  *
47
- * The hold (`blocked`, scheduler.ts) asks `hidden`: a reader behind a
47
+ * The hold (`blocked`, scheduler.ts) asks `hiddenBy`: a reader behind a
48
48
  * fallback — any boundary on its chain whose last pass chose the fallback —
49
49
  * is not on screen and holds nothing. An error boundary flipping to its
50
50
  * fallback is the same as a loading one resetting: every reader under it
@@ -77,34 +77,38 @@ function setRevealHooks(e) {
77
77
 
78
78
  /** Boundaries with readers to sweep (or an arm to resolve) at the seam. */ const collecting = new Set;
79
79
 
80
- const boundaryOf = e => e.U[BOUNDARY];
80
+ const boundaryOf = e => e.A[BOUNDARY];
81
81
 
82
82
  /** Catches what reaches it: an error boundary always; a loading one until it
83
- * has shown content, or while re-armed. */ const isCollecting = e => e.i === STATUS_ERROR || !e.k || e.L;
83
+ * has shown content, or while re-armed. */ const isCollecting = e => e.i === STATUS_ERROR || !e.D || e.h;
84
+
85
+ /** A reader `b` waits on: recorded for its `on`, and `b` visited at the seam. */ function record(e, r) {
86
+ e.U.add(r);
87
+ collecting.add(e);
88
+ }
84
89
 
85
90
  /** The reader goes to the fallback: recorded, and the output re-derives
86
91
  * (a no-op while the output's own pass is running — its post-check
87
- * decides). */ function caught(e, n, r) {
92
+ * decides). */ function caught(e, r, n) {
88
93
  if (e.i === STATUS_ERROR) {
89
94
  // An error boundary waits on what THREW (`StatusError.source`), not on
90
95
  // the reader that saw it: the reader recovers the moment a refetch
91
96
  // starts (its boundary catches the pending), while the error is settled
92
97
  // only when its source is neither errored nor in flight. `reset` re-runs
93
98
  // the same node.
94
- r ??= n.m?.v;
95
- n = r?.source ?? n;
96
- const t = unwrapStatusError(r);
97
- setSignal(e.v, t);
98
- reportClientError(t, e.H, n);
99
+ n ??= r.T?.F;
100
+ r = n?.source ?? r;
101
+ const t = unwrapStatusError(n);
102
+ setSignal(e.F, t);
103
+ reportClientError(t, e.H, r);
99
104
  }
100
- e.V.add(n);
101
- collecting.add(e);
102
- if (e.L) flip(e);
105
+ record(e, r);
106
+ if (e.h) flip(e);
103
107
  // Already showing the fallback: one more reader to wait on changes
104
108
  // nothing on screen. The fallback is instantiated once per show — a later
105
109
  // error reaches it through the `error` accessor, which is why it is one.
106
- if (!e.P) redraw(e);
107
- if (e.B !== null) revealHooks.j(e.B);
110
+ if (!e.L) redraw(e);
111
+ if (e.v !== null) revealHooks.P(e.v);
108
112
  }
109
113
 
110
114
  /** A re-armed boundary found something pending: fresh again, and the swap
@@ -112,22 +116,24 @@ const boundaryOf = e => e.U[BOUNDARY];
112
116
  * the lane's work from its next pass's head — not a re-pass of the
113
117
  * transaction holding its pending content, which would entangle that
114
118
  * transaction with the arming frame's (#3540 `on: () => latest(dep)`). */ function flip(e) {
115
- e.k = false;
116
- e.Z = e.W;
117
- if (e.Z !== null && e.K !== null) GlobalQueue.M(e.K, e.Z, true, false);
119
+ e.D = false;
120
+ e.V = e.B;
121
+ const r = e.j;
122
+ if (!e.V && r.l & CONFIG_HELD && txOf(r) !== flushTransaction) unhold(r);
123
+ if (e.V !== null && e.j !== null) laneStageHook(e.j, e.V, true, false);
118
124
  }
119
125
 
120
126
  function redraw(e) {
121
- if (e.K !== null && !(e.K.R & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE))) {
122
- enqueueSub(e.K);
127
+ if (e.j !== null && !(e.j.R & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE))) {
128
+ enqueueSub(e.j);
123
129
  schedule();
124
130
  }
125
131
  }
126
132
 
127
- /** GlobalQueue._catch: status from a frame reader, nearest boundary first.
133
+ /** `catchHook` (scheduler.ts): status from a frame reader, nearest boundary first.
128
134
  * A loading boundary on the way records a pending reader whether or not it
129
- * catches it (its `on` may collect it later). */ function catchStatus(e, n, r) {
130
- if (n === 0) {
135
+ * catches it (its `on` may collect it later). */ function catchStatus(e, r, n) {
136
+ if (r === 0) {
131
137
  // A status cleared. Judged by the node's status now, against each
132
138
  // boundary's own rule (`unsettled`) — the error path clears pending
133
139
  // first and the node is still errored then. The last reader a fallback
@@ -135,34 +141,35 @@ function redraw(e) {
135
141
  // held reader's landing has joined its transaction, so the content
136
142
  // lands with its run, not a round after). Others still waiting: nothing
137
143
  // on screen changes, the fallback stays as it is.
138
- if (collecting.size !== 0) for (let n = boundaryOf(e); n !== undefined; n = n._parent ?? undefined) if (!(e.G & unsettled(n)) && n.V.delete(e) && n.V.size === 0) {
139
- if (n.P) redraw(n);
140
- if (n.B !== null) revealHooks.j(n.B);
144
+ if (collecting.size !== 0) for (let r = boundaryOf(e); r; r = r._parent) if (!(e.m & unsettled(r)) && r.U.delete(e) && r.U.size === 0) {
145
+ if (r.L) redraw(r);
146
+ if (r.v !== null) revealHooks.P(r.v);
141
147
  }
142
148
  return false;
143
149
  }
144
- for (let t = boundaryOf(e); t !== undefined; t = t._parent ?? undefined) {
145
- if (!(t.i & n)) continue;
150
+ for (let t = boundaryOf(e); t; t = t._parent) {
151
+ if (!(t.i & r)) continue;
146
152
  if (isCollecting(t)) {
147
- caught(t, e, r);
148
- return true;
153
+ caught(t, e, n);
154
+ return t.h ? CAUGHT_REARMED : true;
149
155
  }
150
- t.V.add(e);
151
- collecting.add(t);
156
+ record(t, e);
152
157
  }
153
158
  return false;
154
159
  }
155
160
 
156
- /** The nearest boundary on `r`'s chain showing its fallback, if any. */ function hiddenBy(e) {
157
- for (let n = boundaryOf(e); n !== undefined; n = n._parent ?? undefined) if (n.P) return n;
158
- return undefined;
161
+ /** The nearest loading boundary that would catch `node` was re-armed this
162
+ * flush. */ function armedOver(e) {
163
+ for (let r = boundaryOf(e); r; r = r._parent) if (r.i & STATUS_PENDING && isCollecting(r)) return r.h;
159
164
  }
160
165
 
161
- /** GlobalQueue._hidden: behind a fallback. */ function hidden(e) {
162
- return hiddenBy(e) !== undefined;
166
+ /** The nearest boundary on `r`'s chain showing its fallback, if any
167
+ * (`hiddenHook`: `r` is behind a fallback). */ function hiddenBy(e) {
168
+ for (let r = boundaryOf(e); r; r = r._parent) if (r.L) return r;
169
+ return undefined;
163
170
  }
164
171
 
165
- /** GlobalQueue._heldRun: a queued run behind a fallback waits for the reveal
172
+ /** `heldRunHook` (scheduler.ts): a queued run behind a fallback waits for the reveal
166
173
  * (maintainer, 2026-10-02: the queue is held, the synchronous first render
167
174
  * goes through). A user effect's, which would read a DOM that is not
168
175
  * attached ("many depend on reading the DOM, something that can't happen
@@ -173,21 +180,21 @@ function redraw(e) {
173
180
  * synchronous on creation and never comes here. The run's `_modified` stays
174
181
  * set; the reveal re-queues it by type (`release`), and a boundary above
175
182
  * still showing its fallback holds it again then. */ function heldRun(e) {
176
- const n = hiddenBy(e);
177
- if (n === undefined) return false;
178
- (n.Y ??= new Set).add(e);
183
+ const r = hiddenBy(e);
184
+ if (r === undefined) return false;
185
+ (r.Z ??= new Set).add(e);
179
186
  return true;
180
187
  }
181
188
 
182
189
  /** The reveal: the runs held behind the fallback are this flush's, each in
183
190
  * its own phase — render updates with the frame that attaches the content,
184
191
  * user effects after it. */ function release(e) {
185
- if (e.Y === null) return;
186
- for (const n of e.Y) {
187
- const e = n.i;
188
- globalQueue.enqueue(e === EFFECT_TRACKED ? EFFECT_USER : e, e === EFFECT_TRACKED ? n.q : n.J ??= GlobalQueue.X.bind(null, n));
192
+ if (e.Z === null) return;
193
+ for (const r of e.Z) {
194
+ const e = r.i;
195
+ globalQueue.enqueue(e === EFFECT_TRACKED ? EFFECT_USER : e, e === EFFECT_TRACKED ? r.M : r.W ??= GlobalQueue.K.bind(null, r));
189
196
  }
190
- e.Y = null;
197
+ e.Z = null;
191
198
  }
192
199
 
193
200
  /** Drop the readers that settled or died. Settled: a loading reader no
@@ -197,70 +204,70 @@ function redraw(e) {
197
204
  * it and enters the transaction itself (its committed value shows until the
198
205
  * landing). */ const unsettled = e => e.i === STATUS_ERROR ? STATUS_ERROR | STATUS_PENDING : STATUS_PENDING;
199
206
 
200
- function prune(e, n) {
201
- const r = unsettled(e);
202
- for (const t of e.V) {
203
- if (t.R & REACTIVE_DISPOSED) e.V.delete(t); else if (!(t.G & r)) {
204
- if (t !== e.$ && (t.u & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD) {
207
+ function prune(e, r) {
208
+ const n = unsettled(e);
209
+ for (const t of e.U) {
210
+ if (t.R & REACTIVE_DISPOSED) e.U.delete(t); else if (!(t.m & n)) {
211
+ if ((t !== e.Y || e.h) && (t.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD) {
205
212
  // Landed, held: the content is that transaction's — the output's
206
213
  // pass enters it and the reveal lands with the reader's run. The
207
214
  // seam, past its park decision, waits for the commit instead. (A
208
- // lane's reader reveals with the lane, this round.)
209
- if (!n) continue;
215
+ // lane's reader reveals with the lane, this round.) A boundary that
216
+ // has not shown content catches it instead: the fallback stays until
217
+ // the reader is committed (#3540: what a fresh boundary catches holds
218
+ // nothing outside it).
219
+ if (!r || !e.D) continue;
210
220
  joinFuture(txOf(t));
211
221
  }
212
- e.V.delete(t);
222
+ e.U.delete(t);
213
223
  }
214
224
  }
215
- return e.V.size;
225
+ return e.U.size;
216
226
  }
217
227
 
218
228
  /** Reveal order: a slot is ready when its content can show — it has shown
219
229
  * content, or nothing under it is unready (the tree neither pending nor
220
230
  * born held, no reader waited on). */ function ready(e) {
221
- return e.k || !(e.$.G & STATUS_PENDING) && !(e.$.u & CONFIG_HELD && (e.K === null || e.K.G & STATUS_UNINITIALIZED)) && prune(e, false) === 0;
231
+ return e.D || !(e.Y.m & STATUS_PENDING) && !(e.Y.l & CONFIG_HELD && (e.j === null || e.j.m & STATUS_UNINITIALIZED)) && prune(e, false) === 0;
222
232
  }
223
233
 
224
- /** GlobalQueue._boundarySeam, end of the seam: readers that settled, died,
234
+ /** `boundarySeamHook` (scheduler.ts), end of the seam: readers that settled, died,
225
235
  * or landed and committed are dropped; a fallback with none left reveals
226
236
  * next round. An arm resolves: nothing collected, nothing happened. */ function boundarySeam() {
227
237
  for (const e of collecting) {
228
- const n = e.K.R;
229
- if (n & REACTIVE_DISPOSED) {
238
+ const r = e.j.R;
239
+ if (r & REACTIVE_DISPOSED) {
230
240
  collecting.delete(e);
231
241
  continue;
232
242
  }
233
243
  // A parked frame's boundary (zombie) keeps its state for its revival.
234
- if (n & REACTIVE_ZOMBIE) continue;
244
+ if (r & REACTIVE_ZOMBIE) continue;
235
245
  prune(e, false);
236
- e.L = false;
237
- if (e.V.size === 0) {
246
+ const n = e.j;
247
+ if (e.h && !e.D && n.l & CONFIG_HELD && [ ...e.U ].every(e => !(e.m & STATUS_PENDING) && e.l & CONFIG_HELD && txOf(e) === txOf(n))) {
248
+ e.U.clear();
249
+ e.D = true;
250
+ }
251
+ e.h = false;
252
+ if (e.U.size === 0) {
238
253
  collecting.delete(e);
239
- if (e.P) redraw(e);
240
- if (e.B !== null) revealHooks.j(e.B);
254
+ if (e.L) redraw(e);
255
+ if (e.v !== null) revealHooks.P(e.v);
241
256
  }
242
257
  }
243
258
  }
244
259
 
245
- /** The `on` pass is a verdict's: it probed itself (CONFIG_VERDICT), or it
246
- * read a memo that did (#3528's `createMemo(() => isPending(m2))`) — its
247
- * swap is display-ahead either way. */ function verdictDerived(e) {
248
- if (e.u & CONFIG_VERDICT) return true;
249
- for (let n = e._; n !== null; n = n.p) if (n.I.u & CONFIG_VERDICT) return true;
250
- return false;
251
- }
252
-
253
260
  /** `on` changed after the mount: fresh again if anything under it is
254
261
  * pending now; collecting for the rest of the flush either way. */ function arm(e) {
255
- if (!e.k) {
262
+ if (!e.D) {
256
263
  // Already re-armed and on its fallback, as the frame's work — and the
257
264
  // `on` pass arms again as lane work: its verdict was provisional (a
258
265
  // memo of `isPending(m2)` is a watcher until the flush parks, then the
259
266
  // holder's verdict lane's — verdict.ts), and the swap it asked for is
260
267
  // display-ahead after all (#3528). Re-home the swap: the output re-runs
261
268
  // as the lane's, shown now beside the held frame.
262
- if (e.P && e.Z === null && e.W === null && passLane !== null) {
263
- e.W = passLane;
269
+ if (e.L && e.V === null && e.B === null && passLane !== null) {
270
+ e.B = passLane;
264
271
  // The swap the frame's pass staged (held with the frame) is void: the
265
272
  // lane's pass stages it anew (`flip` lists the output in the lane,
266
273
  // which voids its staging), as a watcher's provisional value is
@@ -270,20 +277,28 @@ function prune(e, n) {
270
277
  }
271
278
  return;
272
279
  }
273
- e.L = true;
274
- // A verdict read in `on` (`isPending`) whose frame has no transaction
275
- // yet: the swap is still display-ahead — the frame gets one now (it lands
280
+ e.h = true;
281
+ // A verdict read in `on` (`isPending`, or a memo of one — #3528's
282
+ // `createMemo(() => isPending(m2))`: the mark carries, core.ts `read`)
283
+ // whose frame has no transaction yet: the swap is still display-ahead — the frame gets one now (it lands
276
284
  // at this very seam if nothing holds it), and the swap is its verdict
277
285
  // lane's work, shown ahead of the frame's own runs (#3528).
278
- let n = passLane;
279
- if (n === null && globalQueue.ee && verdictDerived(getOwner())) {
286
+ let r = passLane;
287
+ if (r === null && globalQueue.q && getOwner().l & CONFIG_VERDICT) {
280
288
  joinFuture(null);
281
- n = GlobalQueue.ne(flushTransaction);
289
+ r = GlobalQueue.J(flushTransaction);
282
290
  }
283
- e.W = n;
291
+ e.B = r;
284
292
  collecting.add(e);
285
- for (const n of e.V) {
286
- if (n.G & STATUS_PENDING && !(n.R & REACTIVE_DISPOSED)) {
293
+ const n = e.U.size;
294
+ heldUnder(e.H, flushTransaction, r => record(e, r));
295
+ for (const r of e.U) {
296
+ if (e.U.size !== n || r.m & STATUS_PENDING && !(r.R & REACTIVE_DISPOSED)) {
297
+ // What it collects was forwarded through it to the boundaries above
298
+ // too (`catchStatus` records a reader in every boundary on the way).
299
+ // It is this boundary's alone now (B5, the nearest catcher): an outer
300
+ // re-armed by the same tick does not wait for it (D-14, U-4).
301
+ for (let r = e._parent; r !== null; r = r._parent) for (const n of e.U) r.U.delete(n);
287
302
  flip(e);
288
303
  redraw(e);
289
304
  return;
@@ -294,35 +309,36 @@ function prune(e, n) {
294
309
  /** The error boundary's `reset`: re-run what threw (a comparator throw
295
310
  * names a signal — nothing to re-run). */ function reset(e) {
296
311
  // A snapshot: a source failing again is caught — and re-added — mid-walk.
297
- for (const n of [ ...e.V ]) if (n.re !== undefined && !(n.R & REACTIVE_DISPOSED)) recompute(n);
312
+ for (const r of [ ...e.U ]) if (r.X !== undefined && !(r.R & REACTIVE_DISPOSED)) recompute(r);
298
313
  schedule();
299
314
  }
300
315
 
301
- function createBoundary(e, n, r, t) {
316
+ function createBoundary(e, r, n, t) {
302
317
  const o = createOwner();
303
318
  const l = {
304
319
  i: e,
305
320
  _parent: boundaryOf(o) ?? null,
306
321
  H: o,
307
- $: null,
308
- K: null,
309
- V: new Set,
310
- k: false,
311
- L: false,
312
- W: null,
313
- Z: null,
314
- P: false,
315
322
  Y: null,
323
+ j: null,
324
+ U: new Set,
325
+ D: false,
326
+ h: false,
316
327
  B: null,
317
- te: false,
318
- oe: false,
319
- v: e === STATUS_ERROR ? signal(undefined, ERROR_SIGNAL) : null,
320
- le: r
328
+ V: null,
329
+ L: false,
330
+ Z: null,
331
+ v: null,
332
+ $: false,
333
+ ee: false,
334
+ F: e === STATUS_ERROR ? signal(undefined, ERROR_SIGNAL) : null,
335
+ re: n,
336
+ ne: null
321
337
  };
322
- const u = e === STATUS_PENDING ? o.U[REVEAL] : null;
338
+ const u = e === STATUS_PENDING ? o.A[REVEAL] : null;
323
339
  if (getOwner()) cleanup(() => {
324
340
  collecting.delete(l);
325
- if (u) revealHooks.ue(u, l);
341
+ if (u) revealHooks.te(u, l);
326
342
  });
327
343
  // The `on` dependencies live OUTSIDE the boundary, as the condition of a
328
344
  // `<Show>` wrapping it would: created before the owner's context names it.
@@ -344,42 +360,42 @@ function createBoundary(e, n, r, t) {
344
360
  }
345
361
  }
346
362
  if (e) arm(l); else e = true;
347
- })).u |= CONFIG_REDERIVE;
363
+ })).l |= CONFIG_REDERIVE;
348
364
  }
349
365
  const a = {
350
- ...o.U,
366
+ ...o.A,
351
367
  [BOUNDARY]: l
352
368
  };
353
369
  if (revealHooks !== null) a[REVEAL] = null;
354
- o.U = a;
370
+ o.A = a;
355
371
  const i = runWithOwner(o, () => {
356
- const e = true ? computed(n, {
372
+ const e = true ? computed(r, {
357
373
  name: "children"
358
- }) : computed(n);
374
+ }) : computed(r);
359
375
  return true ? computed(() => flatten(read(e)), {
360
376
  name: "boundary"
361
377
  }) : computed(() => flatten(read(e)));
362
378
  });
363
- l.$ = i;
379
+ l.Y = i;
364
380
  // A slot of the reveal order in its context (reveal.ts): gated until the
365
381
  // controller releases it — before the output's first pass.
366
- if (u) revealHooks.ae(u, l);
367
- const f = computed(() => {
382
+ if (u) revealHooks.oe(u, l);
383
+ const s = computed(() => {
368
384
  // Reveal order (reveal.ts): a gated slot shows its fallback — or, in a
369
385
  // collapsed tail, nothing — whatever its content's state; the
370
386
  // controller is told the pass ran (its readiness may have changed) and
371
387
  // releases it in turn.
372
- if (l.te) {
388
+ if (l.$) {
373
389
  // Linked without being read: the tree settling (or changing)
374
390
  // re-derives this pass, which is how the controller hears of it.
375
391
  // It may release this very slot (the frontier whose content just
376
392
  // settled): the pass goes on to show it — the redraw it asks for is
377
393
  // refused mid-pass and not needed.
378
394
  link(i, getOwner());
379
- if (l.B !== null) revealHooks.j(l.B);
380
- if (l.te) {
381
- if (l.oe) {
382
- l.P = true;
395
+ if (l.v !== null) revealHooks.P(l.v);
396
+ if (l.$) {
397
+ if (l.ee) {
398
+ l.L = true;
383
399
  return undefined;
384
400
  }
385
401
  return fallback(l);
@@ -392,22 +408,21 @@ function createBoundary(e, n, r, t) {
392
408
  // display-ahead `on` causes is the lane's, and must still be the
393
409
  // fallback. Linked, so its recovery re-derives this pass; read again
394
410
  // once clean.
395
- const n = i.G & (STATUS_PENDING | STATUS_ERROR);
396
- if (n !== 0) {
411
+ const r = i.m & (STATUS_PENDING | STATUS_ERROR);
412
+ if (r !== 0) {
397
413
  link(i, getOwner());
398
- if (n & e) {
414
+ if (r & e) {
399
415
  if (isCollecting(l)) {
400
416
  caught(l, i, undefined);
401
417
  return fallback(l);
402
418
  }
403
419
  // Forwarded: a loading boundary showing content keeps it and
404
420
  // holds the frame like any reader (recorded for its `on`).
405
- l.V.add(i);
406
- collecting.add(l);
421
+ record(l, i);
407
422
  }
408
423
  // A pending passes an error boundary by (A6); an error, a loading
409
424
  // one; a loading boundary showing content forwards its pending.
410
- throw i.m.v;
425
+ throw i.T.F;
411
426
  }
412
427
  if (isCollecting(l)) {
413
428
  // A29's boundary exemption (#3540): a boundary MOUNTED over a held
@@ -422,82 +437,111 @@ function createBoundary(e, n, r, t) {
422
437
  // which reveals the content — a fallback staged earlier is replaced
423
438
  // ahead of the commit and never shown.
424
439
  const e = getOwner();
425
- if (l.V.size !== 0 && e.G & STATUS_UNINITIALIZED && !(e.u & CONFIG_HELD)) return fallback(l);
440
+ if (l.U.size !== 0 && e.m & STATUS_UNINITIALIZED && !(e.l & CONFIG_HELD)) return fallback(l);
426
441
  // Readers under it still unready: the fallback, the tree untouched.
427
442
  // The seam re-derives this pass when they settle.
428
443
  if (prune(l, true) !== 0) return fallback(l);
429
444
  }
430
- let r;
445
+ let n;
431
446
  try {
432
- r = read(i);
433
- } catch (n) {
434
- if (n instanceof NotReadyError ? e === STATUS_PENDING : e === STATUS_ERROR) {
447
+ n = read(i);
448
+ } catch (r) {
449
+ if (r instanceof NotReadyError ? e === STATUS_PENDING : e === STATUS_ERROR) {
435
450
  if (isCollecting(l)) {
436
- caught(l, i, n);
451
+ caught(l, i, r);
437
452
  return fallback(l);
438
453
  }
439
454
  // Forwarded: a loading boundary showing content keeps it and
440
455
  // holds the frame like any reader (recorded for its `on`).
441
- l.V.add(i);
442
- collecting.add(l);
456
+ record(l, i);
443
457
  }
444
458
  // A pending passes an error boundary by (A6).
445
- throw n;
459
+ throw r;
446
460
  }
447
461
  // Readers under it still unready (caught before, or while the read
448
462
  // ran the content at a mount): the fallback.
449
- if (isCollecting(l) && l.V.size !== 0) return fallback(l);
450
- l.k = true;
463
+ if (isCollecting(l) && l.U.size !== 0) return fallback(l);
464
+ l.D = true;
451
465
  // Observe: the fallback stops showing (a hide before its commit means
452
466
  // it never displayed — the engine knows the swap it was staged with).
453
- if (l.P && l.i & STATUS_PENDING && attrHooks !== null) attrHooks.boundaryFallback(l, l.$, false);
454
- l.P = false;
467
+ if (l.L && l.i & STATUS_PENDING && attrHooks !== null) attrHooks.boundaryFallback(l, l.Y, false);
468
+ l.L = false;
469
+ // The fallback dies when the content that replaces it is on screen —
470
+ // with this flush's commits when the pass is the frame's, at the
471
+ // landing when the frame parks (the run is held with it, as the old
472
+ // children of a held pass are) — not now: a held swap keeps the
473
+ // fallback on screen, live, until it commits. A pass back to the
474
+ // fallback before then finds it still built and keeps it.
475
+ const t = l.ne;
476
+ if (t !== null) globalQueue.enqueue(EFFECT_RENDER, () => {
477
+ if (!l.L && l.ne === t) {
478
+ dispose(t);
479
+ l.ne = null;
480
+ }
481
+ });
455
482
  release(l);
456
483
  // Shown: the slot leaves its reveal order (a later re-arm gates nobody
457
484
  // else; the frontier moves on).
458
- if (l.B !== null) {
459
- const e = l.B;
460
- l.B = null;
461
- revealHooks.j(e);
485
+ if (l.v !== null) {
486
+ const e = l.v;
487
+ l.v = null;
488
+ revealHooks.P(e);
462
489
  }
463
- return r;
490
+ return n;
464
491
  },
465
492
  // Boundary structure, not a user source: its value is fallback-or-content
466
493
  // and legitimately swaps mid-hydration (resume), so it must never be
467
494
  // frozen by snapshot capture.
468
495
  {
469
496
  name: "value",
470
- ie: true
497
+ le: true
471
498
  });
472
- f.u |= CONFIG_REDERIVE;
473
- l.K = f;
474
- return accessor(f);
499
+ s.l |= CONFIG_REDERIVE;
500
+ l.j = s;
501
+ return accessor(s);
475
502
  }
476
503
 
477
504
  function fallback(e) {
478
505
  // The swap a display-ahead re-arm asked for: this pass is the lane's — a
479
506
  // lane read by construction (REACTIVE_LANE_READ: `recompute`'s tail keeps
480
507
  // the pass in the lane).
481
- if (e.Z !== null) {
482
- setPassLane(e.Z);
483
- e.K.R |= REACTIVE_LANE_READ;
484
- e.Z = null;
508
+ if (e.V !== null) {
509
+ setPassLane(e.V);
510
+ e.j.R |= REACTIVE_LANE_READ;
511
+ e.V = null;
485
512
  }
486
513
  // Observe: a loading boundary starts showing its fallback — the swap lands
487
514
  // with the frame's transaction, or this drain commits it (lane work, or no
488
515
  // transaction). A mount's synchronous first show has no drain of its own:
489
516
  // one is scheduled so the engine's display instant (`flushEnd`) comes.
490
- if (!e.P && e.i & STATUS_PENDING && attrHooks !== null) {
491
- attrHooks.boundaryFallback(e, e.$, true, passLane !== null ? null : flushTransaction !== null ? resolveTx(flushTransaction) : passTx);
492
- if (!globalQueue.ee) schedule();
517
+ if (!e.L && e.i & STATUS_PENDING && attrHooks !== null) {
518
+ attrHooks.boundaryFallback(e, e.Y, true, passLane !== null ? null : flushTransaction !== null ? resolveTx(flushTransaction) : passTx);
519
+ if (!globalQueue.q) schedule();
493
520
  }
494
- e.P = true;
495
- return e.le(e);
521
+ e.L = true;
522
+ // Built in its own scope, like `Show`'s function child, once per show
523
+ // (D-39: the fallback is the display until a new answer replaces the
524
+ // error): a re-derive of the output while it shows — a second reader
525
+ // caught, the same error propagated again through a sibling's re-run, a
526
+ // retry pending after `reset()`, a later error — keeps the built
527
+ // fallback, its DOM and focus with it (#3991). A later error reaches it
528
+ // through the error accessor; only a fallback reading the accessor at its
529
+ // top level is rebuilt by it. Created beside the output — in its owner,
530
+ // outside the boundary, as the output is: its errors and runs are the
531
+ // enclosing boundary's — with the output's id, so what it builds gets the
532
+ // ids a build under the output would; disposed once content is on screen.
533
+ if (e.ne === null) {
534
+ const r = getOwner();
535
+ e.ne = runWithOwner(r._parent, () => computed(() => e.re(e), {
536
+ id: r.id
537
+ }));
538
+ }
539
+ return read(e.ne);
496
540
  }
497
541
 
498
542
  const ERROR_SIGNAL = {
499
543
  ownedWrite: true,
500
- ie: true
544
+ le: true
501
545
  };
502
546
 
503
547
  /**
@@ -541,8 +585,8 @@ const ERROR_SIGNAL = {
541
585
  * ) as unknown as JSX.Element;
542
586
  * }
543
587
  * ```
544
- */ function createLoadingBoundary(e, n, r) {
545
- return createBoundary(STATUS_PENDING, e, () => n(), r?.on);
588
+ */ function createLoadingBoundary(e, r, n) {
589
+ return createBoundary(STATUS_PENDING, e, () => r(), n?.on);
546
590
  }
547
591
 
548
592
  /**
@@ -567,20 +611,12 @@ const ERROR_SIGNAL = {
567
611
  * ) as unknown as JSX.Element;
568
612
  * }
569
613
  * ```
570
- */ function createErrorBoundary(e, n) {
571
- return createBoundary(STATUS_ERROR, e, e => n(accessor(e.v), () => reset(e)));
614
+ */ function createErrorBoundary(e, r) {
615
+ return createBoundary(STATUS_ERROR, e, e => r(accessor(e.F), () => reset(e)));
572
616
  }
573
617
 
574
618
  // Installed at module evaluation — present exactly when something imports a
575
619
  // boundary. An app without one pays the three null checks and nothing else.
576
- GlobalQueue.fe = catchStatus;
577
-
578
- GlobalQueue.ce = e => e.G & STATUS_UNINITIALIZED && catchStatus(e, STATUS_PENDING);
579
-
580
- GlobalQueue.se = hidden;
581
-
582
- GlobalQueue.Ee = boundarySeam;
583
-
584
- GlobalQueue.de = heldRun;
620
+ installBoundaryHooks(catchStatus, (e, r) => !r && (e.m & STATUS_UNINITIALIZED ? catchStatus(e, STATUS_PENDING) : armedOver(e)), hiddenBy, boundarySeam, heldRun);
585
621
 
586
622
  export { REVEAL, createErrorBoundary, createLoadingBoundary, prune, ready, redraw, revealHooks, setRevealHooks };