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

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 (66) hide show
  1. package/dist/dev-shared.js +248 -189
  2. package/dist/dev.attribution.js +124 -41
  3. package/dist/dev.js +553 -322
  4. package/dist/observe/affects.js +34 -31
  5. package/dist/observe/boundaries.js +166 -132
  6. package/dist/observe/core/async.js +90 -83
  7. package/dist/observe/core/attribution-queries.js +1 -1
  8. package/dist/observe/core/attribution.js +281 -197
  9. package/dist/observe/core/constants.js +7 -2
  10. package/dist/observe/core/context.js +3 -3
  11. package/dist/observe/core/core.js +277 -245
  12. package/dist/observe/core/dev.js +3 -3
  13. package/dist/observe/core/effect.js +21 -21
  14. package/dist/observe/core/external.js +2 -2
  15. package/dist/observe/core/graph.js +25 -25
  16. package/dist/observe/core/heap.js +9 -9
  17. package/dist/observe/core/lanes.js +206 -160
  18. package/dist/observe/core/owner.js +27 -27
  19. package/dist/observe/core/scheduler.js +92 -86
  20. package/dist/observe/core/verdict.js +66 -54
  21. package/dist/observe/map.js +35 -35
  22. package/dist/observe/reveal.js +24 -24
  23. package/dist/observe/signals.js +38 -28
  24. package/dist/observe/store/affects.js +75 -68
  25. package/dist/observe/store/hooks.js +13 -0
  26. package/dist/observe/store/index.js +0 -2
  27. package/dist/observe/store/optimistic.js +67 -34
  28. package/dist/observe/store/projection.js +37 -31
  29. package/dist/observe/store/reconcile.js +32 -30
  30. package/dist/observe/store/store.js +128 -85
  31. package/dist/prod/affects.js +34 -31
  32. package/dist/prod/boundaries.js +143 -109
  33. package/dist/prod/core/async.js +64 -67
  34. package/dist/prod/core/constants.js +7 -2
  35. package/dist/prod/core/context.js +3 -3
  36. package/dist/prod/core/core.js +302 -270
  37. package/dist/prod/core/effect.js +38 -38
  38. package/dist/prod/core/external.js +4 -4
  39. package/dist/prod/core/graph.js +27 -27
  40. package/dist/prod/core/heap.js +30 -30
  41. package/dist/prod/core/lanes.js +257 -211
  42. package/dist/prod/core/owner.js +39 -39
  43. package/dist/prod/core/scheduler.js +125 -119
  44. package/dist/prod/core/verdict.js +70 -58
  45. package/dist/prod/map.js +73 -73
  46. package/dist/prod/reveal.js +38 -38
  47. package/dist/prod/signals.js +30 -20
  48. package/dist/prod/store/affects.js +75 -68
  49. package/dist/prod/store/hooks.js +13 -0
  50. package/dist/prod/store/index.js +0 -2
  51. package/dist/prod/store/optimistic.js +114 -81
  52. package/dist/prod/store/projection.js +30 -24
  53. package/dist/prod/store/reconcile.js +32 -30
  54. package/dist/prod/store/store.js +136 -93
  55. package/dist/types/boundaries.d.ts +4 -0
  56. package/dist/types/core/attribution.d.ts +6 -2
  57. package/dist/types/core/constants.d.ts +5 -0
  58. package/dist/types/core/core.d.ts +33 -3
  59. package/dist/types/core/lanes.d.ts +10 -0
  60. package/dist/types/core/scheduler.d.ts +12 -6
  61. package/dist/types/core/types.d.ts +0 -6
  62. package/dist/types/store/affects.d.ts +11 -0
  63. package/dist/types/store/hooks.d.ts +48 -0
  64. package/dist/types/store/index.d.ts +0 -1
  65. package/dist/types/store/store.d.ts +3 -38
  66. package/package.json +1 -1
@@ -4,7 +4,9 @@ import { ext } from "./core/core.js";
4
4
 
5
5
  import { enqueueSub } from "./core/heap.js";
6
6
 
7
- import { GlobalQueue, schedule, resolveTx, flushTransaction } from "./core/scheduler.js";
7
+ import { schedule, resolveTx, flushTransaction, GlobalQueue } from "./core/scheduler.js";
8
+
9
+ import { storeMarks, installStoreAffects, releaseMarkScope } from "./store/affects.js";
8
10
 
9
11
  import { $TARGET } from "./store/types.js";
10
12
 
@@ -36,12 +38,12 @@ let active = 0;
36
38
  /** The verdict readers downstream of `node` — through derivations, stopping
37
39
  * at effects — re-derive: the mark's one notification (a probe's answer
38
40
  * changed with no value notifying anyone). */ function repoll(e, t = new Set) {
39
- for (let n = e.t; n !== null; n = n.l) {
40
- const e = n.o;
41
+ for (let r = e.t; r !== null; r = r.o) {
42
+ const e = r.l;
41
43
  if (t.has(e)) continue;
42
44
  t.add(e);
43
- if (e.u & CONFIG_VERDICT) enqueueSub(e);
44
- if (!e.i) repoll(e, t);
45
+ if (e.i & CONFIG_VERDICT) enqueueSub(e);
46
+ if (!e.u) repoll(e, t);
45
47
  }
46
48
  }
47
49
 
@@ -51,13 +53,13 @@ let active = 0;
51
53
  * (a direct mark on one still reads pending). Mid-recompute, only the
52
54
  * validated prefix of the dependency list is this pass's. */ function marked(e, t) {
53
55
  if (e.m !== null && e.m.T !== 0) return true;
54
- const n = e;
55
- if (n.G & STATUS_ERROR || t.has(e)) return false;
56
+ const r = e;
57
+ if (r.S & STATUS_ERROR || t.has(e)) return false;
56
58
  t.add(e);
57
- const r = n.R & REACTIVE_RECOMPUTING_DEPS ? n.S : undefined;
58
- if (r !== null) for (let e = n._ ?? null; e !== null; e = e.p) {
59
+ const n = r.R & REACTIVE_RECOMPUTING_DEPS ? r.G : undefined;
60
+ if (n !== null) for (let e = r.p ?? null; e !== null; e = e._) {
59
61
  if (marked(e.I, t)) return true;
60
- if (e === r) break;
62
+ if (e === n) break;
61
63
  }
62
64
  return false;
63
65
  }
@@ -65,13 +67,13 @@ let active = 0;
65
67
  /** Releases one scope's marks (a landed transaction's, the ambient list at
66
68
  * the seam): a node whose last mark drops re-derives its verdict readers. */ function release(e) {
67
69
  for (let t = 0; t < e.length; t++) {
68
- const n = e[t];
70
+ const r = e[t];
69
71
  active--;
70
- if (--n.m.T === 0) {
71
- repoll(n);
72
+ if (--r.m.T === 0) {
73
+ repoll(r);
72
74
  // A store mark's carrier: its scope (and the marks the nodes born in
73
75
  // its window inherited) goes with it.
74
- GlobalQueue.N?.(n);
76
+ releaseMarkScope(r);
75
77
  }
76
78
  }
77
79
  e.length = 0;
@@ -82,8 +84,8 @@ let active = 0;
82
84
  mark(e);
83
85
  const t = flushTransaction;
84
86
  if (t === null) ambient.push(e); else {
85
- const n = resolveTx(t);
86
- (n.T ??= []).push(e);
87
+ const r = resolveTx(t);
88
+ (r.T ??= []).push(e);
87
89
  }
88
90
  repoll(e);
89
91
  }
@@ -96,28 +98,29 @@ function mark(e) {
96
98
  function affects(e, t) {
97
99
  // A store (the store half, store/affects.ts): the slot's leaf, or the
98
100
  // record's carrier and every live node under it.
99
- const n = e?.[$TARGET];
100
- if (n !== undefined) {
101
- const e = GlobalQueue.O(n, t);
101
+ const r = e?.[$TARGET];
102
+ if (r !== undefined) {
103
+ installStoreAffects();
104
+ const e = storeMarks(r, t);
102
105
  for (let t = 0; t < e.length; t++) register(e[t]);
103
106
  schedule();
104
107
  return;
105
108
  }
106
- const r = e?.[$REFRESH];
107
- if (!r) {
109
+ const n = e?.[$REFRESH];
110
+ if (!n) {
108
111
  return;
109
112
  }
110
- register(r);
113
+ register(n);
111
114
  schedule();
112
115
  }
113
116
 
114
- GlobalQueue.C = e => active !== 0 && marked(e, new Set);
117
+ GlobalQueue.N = e => active !== 0 && marked(e, new Set);
115
118
 
116
- GlobalQueue.h = mark;
119
+ GlobalQueue.O = mark;
117
120
 
118
- GlobalQueue.A = release;
121
+ GlobalQueue.k = release;
119
122
 
120
- GlobalQueue.D = e => {
123
+ GlobalQueue.A = e => {
121
124
  if (ambient.length === 0) return;
122
125
  // The flush parked: the declaration's window is the frame's — the marks
123
126
  // are its transaction's ("nothing async below ⇒ no window").
@@ -132,17 +135,17 @@ GlobalQueue.D = e => {
132
135
  // the window is the flight's; the mark stays to the seam after the
133
136
  // landing. The rest release: verdict-only, nothing to show.
134
137
  let t = 0;
135
- const n = [];
138
+ const r = [];
136
139
  for (let e = 0; e < ambient.length; e++) {
137
- const r = ambient[e];
138
- if (inFlight(r)) ambient[t++] = r; else n.push(r);
140
+ const n = ambient[e];
141
+ if (inFlight(n)) ambient[t++] = n; else r.push(n);
139
142
  }
140
143
  ambient.length = t;
141
- if (n.length !== 0) release(n);
144
+ if (r.length !== 0) release(r);
142
145
  };
143
146
 
144
147
  function inFlight(e) {
145
- return (e.G & STATUS_PENDING) !== 0 || (e.u & CONFIG_SLOT_NODE) !== 0 && GlobalQueue.F?.(e) === true;
148
+ return (e.S & STATUS_PENDING) !== 0 || (e.i & CONFIG_SLOT_NODE) !== 0 && GlobalQueue.C?.(e) === true;
146
149
  }
147
150
 
148
151
  export { affects };
@@ -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, REACTIVE_DISPOSED, STATUS_UNINITIALIZED, CONFIG_HELD, EFFECT_RENDER, REACTIVE_ZOMBIE, CONFIG_VERDICT, REACTIVE_LANE_READ, CONFIG_OVERRIDE, EFFECT_USER, EFFECT_TRACKED } from "./core/constants.js";
2
2
 
3
3
  import { signal, runWithOwner, computed, read, setSignal, recompute } from "./core/core.js";
4
4
 
@@ -10,7 +10,7 @@ import { link } from "./core/graph.js";
10
10
 
11
11
  import { enqueueSub } from "./core/heap.js";
12
12
 
13
- import { createOwner, getOwner, cleanup } from "./core/owner.js";
13
+ import { createOwner, getOwner, cleanup, dispose } from "./core/owner.js";
14
14
 
15
15
  import { globalQueue, haltReactivity, passLane, joinFuture, GlobalQueue, flushTransaction, schedule, setPassLane, txOf } from "./core/scheduler.js";
16
16
 
@@ -73,34 +73,34 @@ function setRevealHooks(e) {
73
73
 
74
74
  /** Boundaries with readers to sweep (or an arm to resolve) at the seam. */ const collecting = new Set;
75
75
 
76
- const boundaryOf = e => e.U[BOUNDARY];
76
+ const boundaryOf = e => e.D[BOUNDARY];
77
77
 
78
78
  /** Catches what reaches it: an error boundary always; a loading one until it
79
- * has shown content, or while re-armed. */ const isCollecting = e => e.i === STATUS_ERROR || !e.L || e.k;
79
+ * has shown content, or while re-armed. */ const isCollecting = e => e.u === STATUS_ERROR || !e.h || e.U;
80
80
 
81
81
  /** The reader goes to the fallback: recorded, and the output re-derives
82
82
  * (a no-op while the output's own pass is running — its post-check
83
83
  * decides). */ function caught(e, n, r) {
84
- if (e.i === STATUS_ERROR) {
84
+ if (e.u === STATUS_ERROR) {
85
85
  // An error boundary waits on what THREW (`StatusError.source`), not on
86
86
  // the reader that saw it: the reader recovers the moment a refetch
87
87
  // starts (its boundary catches the pending), while the error is settled
88
88
  // only when its source is neither errored nor in flight. `reset` re-runs
89
89
  // the same node.
90
- r ??= n.m?.V;
90
+ r ??= n.m?.F;
91
91
  n = r?.source ?? n;
92
92
  const t = unwrapStatusError(r);
93
- setSignal(e.V, t);
94
- reportClientError(t, e.v, n);
93
+ setSignal(e.F, t);
94
+ reportClientError(t, e.L, n);
95
95
  }
96
- e.H.add(n);
96
+ e.V.add(n);
97
97
  collecting.add(e);
98
- if (e.k) flip(e);
98
+ if (e.U) flip(e);
99
99
  // Already showing the fallback: one more reader to wait on changes
100
100
  // nothing on screen. The fallback is instantiated once per show — a later
101
101
  // error reaches it through the `error` accessor, which is why it is one.
102
- if (!e.P) redraw(e);
103
- if (e.B !== null) revealHooks.j(e.B);
102
+ if (!e.v) redraw(e);
103
+ if (e.H !== null) revealHooks.P(e.H);
104
104
  }
105
105
 
106
106
  /** A re-armed boundary found something pending: fresh again, and the swap
@@ -108,14 +108,14 @@ const boundaryOf = e => e.U[BOUNDARY];
108
108
  * the lane's work from its next pass's head — not a re-pass of the
109
109
  * transaction holding its pending content, which would entangle that
110
110
  * transaction with the arming frame's (#3540 `on: () => latest(dep)`). */ function flip(e) {
111
- e.L = false;
112
- e.Z = e.W;
113
- if (e.Z !== null && e.K !== null) GlobalQueue.M(e.K, e.Z, true, false);
111
+ e.h = false;
112
+ e.B = e.j;
113
+ if (e.B !== null && e.Z !== null) GlobalQueue.W(e.Z, e.B, true, false);
114
114
  }
115
115
 
116
116
  function redraw(e) {
117
- if (e.K !== null && !(e.K.R & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE))) {
118
- enqueueSub(e.K);
117
+ if (e.Z !== null && !(e.Z.R & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE))) {
118
+ enqueueSub(e.Z);
119
119
  schedule();
120
120
  }
121
121
  }
@@ -131,26 +131,26 @@ function redraw(e) {
131
131
  // held reader's landing has joined its transaction, so the content
132
132
  // lands with its run, not a round after). Others still waiting: nothing
133
133
  // on screen changes, the fallback stays as it is.
134
- if (collecting.size !== 0) for (let n = boundaryOf(e); n !== undefined; n = n._parent ?? undefined) if (!(e.G & unsettled(n)) && n.H.delete(e) && n.H.size === 0) {
135
- if (n.P) redraw(n);
136
- if (n.B !== null) revealHooks.j(n.B);
134
+ if (collecting.size !== 0) for (let n = boundaryOf(e); n !== undefined; n = n._parent ?? undefined) if (!(e.S & unsettled(n)) && n.V.delete(e) && n.V.size === 0) {
135
+ if (n.v) redraw(n);
136
+ if (n.H !== null) revealHooks.P(n.H);
137
137
  }
138
138
  return false;
139
139
  }
140
140
  for (let t = boundaryOf(e); t !== undefined; t = t._parent ?? undefined) {
141
- if (!(t.i & n)) continue;
141
+ if (!(t.u & n)) continue;
142
142
  if (isCollecting(t)) {
143
143
  caught(t, e, r);
144
144
  return true;
145
145
  }
146
- t.H.add(e);
146
+ t.V.add(e);
147
147
  collecting.add(t);
148
148
  }
149
149
  return false;
150
150
  }
151
151
 
152
152
  /** The nearest boundary on `r`'s chain showing its fallback, if any. */ function hiddenBy(e) {
153
- for (let n = boundaryOf(e); n !== undefined; n = n._parent ?? undefined) if (n.P) return n;
153
+ for (let n = boundaryOf(e); n !== undefined; n = n._parent ?? undefined) if (n.v) return n;
154
154
  return undefined;
155
155
  }
156
156
 
@@ -171,19 +171,19 @@ function redraw(e) {
171
171
  * still showing its fallback holds it again then. */ function heldRun(e) {
172
172
  const n = hiddenBy(e);
173
173
  if (n === undefined) return false;
174
- (n.Y ??= new Set).add(e);
174
+ (n.K ??= new Set).add(e);
175
175
  return true;
176
176
  }
177
177
 
178
178
  /** The reveal: the runs held behind the fallback are this flush's, each in
179
179
  * its own phase — render updates with the frame that attaches the content,
180
180
  * user effects after it. */ function release(e) {
181
- if (e.Y === null) return;
182
- for (const n of e.Y) {
183
- const e = n.i;
184
- globalQueue.enqueue(e === EFFECT_TRACKED ? EFFECT_USER : e, e === EFFECT_TRACKED ? n.q : n.J ??= GlobalQueue.X.bind(null, n));
181
+ if (e.K === null) return;
182
+ for (const n of e.K) {
183
+ const e = n.u;
184
+ globalQueue.enqueue(e === EFFECT_TRACKED ? EFFECT_USER : e, e === EFFECT_TRACKED ? n.M : n.Y ??= GlobalQueue.q.bind(null, n));
185
185
  }
186
- e.Y = null;
186
+ e.K = null;
187
187
  }
188
188
 
189
189
  /** Drop the readers that settled or died. Settled: a loading reader no
@@ -191,37 +191,40 @@ function redraw(e) {
191
191
  * that landed but is held (its value staged in a transaction) is not: the
192
192
  * content reveals at the commit, with its run. The tree is: the output reads
193
193
  * it and enters the transaction itself (its committed value shows until the
194
- * landing). */ const unsettled = e => e.i === STATUS_ERROR ? STATUS_ERROR | STATUS_PENDING : STATUS_PENDING;
194
+ * landing). */ const unsettled = e => e.u === STATUS_ERROR ? STATUS_ERROR | STATUS_PENDING : STATUS_PENDING;
195
195
 
196
196
  function prune(e, n) {
197
197
  const r = unsettled(e);
198
- for (const t of e.H) {
199
- if (t.R & REACTIVE_DISPOSED) e.H.delete(t); else if (!(t.G & r)) {
200
- if (t !== e.$ && (t.u & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD) {
198
+ for (const t of e.V) {
199
+ if (t.R & REACTIVE_DISPOSED) e.V.delete(t); else if (!(t.S & r)) {
200
+ if (t !== e.J && (t.i & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD) {
201
201
  // Landed, held: the content is that transaction's — the output's
202
202
  // pass enters it and the reveal lands with the reader's run. The
203
203
  // seam, past its park decision, waits for the commit instead. (A
204
- // lane's reader reveals with the lane, this round.)
205
- if (!n) continue;
204
+ // lane's reader reveals with the lane, this round.) A boundary that
205
+ // has not shown content catches it instead: the fallback stays until
206
+ // the reader is committed (#3540: what a fresh boundary catches holds
207
+ // nothing outside it).
208
+ if (!n || !e.h) continue;
206
209
  joinFuture(txOf(t));
207
210
  }
208
- e.H.delete(t);
211
+ e.V.delete(t);
209
212
  }
210
213
  }
211
- return e.H.size;
214
+ return e.V.size;
212
215
  }
213
216
 
214
217
  /** Reveal order: a slot is ready when its content can show — it has shown
215
218
  * content, or nothing under it is unready (the tree neither pending nor
216
219
  * born held, no reader waited on). */ function ready(e) {
217
- return e.L || !(e.$.G & STATUS_PENDING) && !(e.$.u & CONFIG_HELD && (e.K === null || e.K.G & STATUS_UNINITIALIZED)) && prune(e, false) === 0;
220
+ return e.h || !(e.J.S & STATUS_PENDING) && !(e.J.i & CONFIG_HELD && (e.Z === null || e.Z.S & STATUS_UNINITIALIZED)) && prune(e, false) === 0;
218
221
  }
219
222
 
220
223
  /** GlobalQueue._boundarySeam, end of the seam: readers that settled, died,
221
224
  * or landed and committed are dropped; a fallback with none left reveals
222
225
  * next round. An arm resolves: nothing collected, nothing happened. */ function boundarySeam() {
223
226
  for (const e of collecting) {
224
- const n = e.K.R;
227
+ const n = e.Z.R;
225
228
  if (n & REACTIVE_DISPOSED) {
226
229
  collecting.delete(e);
227
230
  continue;
@@ -229,11 +232,11 @@ function prune(e, n) {
229
232
  // A parked frame's boundary (zombie) keeps its state for its revival.
230
233
  if (n & REACTIVE_ZOMBIE) continue;
231
234
  prune(e, false);
232
- e.k = false;
233
- if (e.H.size === 0) {
235
+ e.U = false;
236
+ if (e.V.size === 0) {
234
237
  collecting.delete(e);
235
- if (e.P) redraw(e);
236
- if (e.B !== null) revealHooks.j(e.B);
238
+ if (e.v) redraw(e);
239
+ if (e.H !== null) revealHooks.P(e.H);
237
240
  }
238
241
  }
239
242
  }
@@ -241,22 +244,22 @@ function prune(e, n) {
241
244
  /** The `on` pass is a verdict's: it probed itself (CONFIG_VERDICT), or it
242
245
  * read a memo that did (#3528's `createMemo(() => isPending(m2))`) — its
243
246
  * swap is display-ahead either way. */ function verdictDerived(e) {
244
- if (e.u & CONFIG_VERDICT) return true;
245
- for (let n = e._; n !== null; n = n.p) if (n.I.u & CONFIG_VERDICT) return true;
247
+ if (e.i & CONFIG_VERDICT) return true;
248
+ for (let n = e.p; n !== null; n = n._) if (n.I.i & CONFIG_VERDICT) return true;
246
249
  return false;
247
250
  }
248
251
 
249
252
  /** `on` changed after the mount: fresh again if anything under it is
250
253
  * pending now; collecting for the rest of the flush either way. */ function arm(e) {
251
- if (!e.L) {
254
+ if (!e.h) {
252
255
  // Already re-armed and on its fallback, as the frame's work — and the
253
256
  // `on` pass arms again as lane work: its verdict was provisional (a
254
257
  // memo of `isPending(m2)` is a watcher until the flush parks, then the
255
258
  // holder's verdict lane's — verdict.ts), and the swap it asked for is
256
259
  // display-ahead after all (#3528). Re-home the swap: the output re-runs
257
260
  // as the lane's, shown now beside the held frame.
258
- if (e.P && e.Z === null && e.W === null && passLane !== null) {
259
- e.W = passLane;
261
+ if (e.v && e.B === null && e.j === null && passLane !== null) {
262
+ e.j = passLane;
260
263
  // The swap the frame's pass staged (held with the frame) is void: the
261
264
  // lane's pass stages it anew (`flip` lists the output in the lane,
262
265
  // which voids its staging), as a watcher's provisional value is
@@ -266,20 +269,20 @@ function prune(e, n) {
266
269
  }
267
270
  return;
268
271
  }
269
- e.k = true;
272
+ e.U = true;
270
273
  // A verdict read in `on` (`isPending`) whose frame has no transaction
271
274
  // yet: the swap is still display-ahead — the frame gets one now (it lands
272
275
  // at this very seam if nothing holds it), and the swap is its verdict
273
276
  // lane's work, shown ahead of the frame's own runs (#3528).
274
277
  let n = passLane;
275
- if (n === null && globalQueue.ee && verdictDerived(getOwner())) {
278
+ if (n === null && globalQueue.X && verdictDerived(getOwner())) {
276
279
  joinFuture(null);
277
- n = GlobalQueue.ne(flushTransaction);
280
+ n = GlobalQueue.$(flushTransaction);
278
281
  }
279
- e.W = n;
282
+ e.j = n;
280
283
  collecting.add(e);
281
- for (const n of e.H) {
282
- if (n.G & STATUS_PENDING && !(n.R & REACTIVE_DISPOSED)) {
284
+ for (const n of e.V) {
285
+ if (n.S & STATUS_PENDING && !(n.R & REACTIVE_DISPOSED)) {
283
286
  flip(e);
284
287
  redraw(e);
285
288
  return;
@@ -290,35 +293,36 @@ function prune(e, n) {
290
293
  /** The error boundary's `reset`: re-run what threw (a comparator throw
291
294
  * names a signal — nothing to re-run). */ function reset(e) {
292
295
  // A snapshot: a source failing again is caught — and re-added — mid-walk.
293
- for (const n of [ ...e.H ]) if (n.re !== undefined && !(n.R & REACTIVE_DISPOSED)) recompute(n);
296
+ for (const n of [ ...e.V ]) if (n.ee !== undefined && !(n.R & REACTIVE_DISPOSED)) recompute(n);
294
297
  schedule();
295
298
  }
296
299
 
297
300
  function createBoundary(e, n, r, t) {
298
301
  const l = createOwner();
299
302
  const o = {
300
- i: e,
303
+ u: e,
301
304
  _parent: boundaryOf(l) ?? null,
302
- v: l,
303
- $: null,
304
- K: null,
305
- H: new Set,
306
- L: false,
307
- k: false,
308
- W: null,
305
+ L: l,
306
+ J: null,
309
307
  Z: null,
310
- P: false,
311
- Y: null,
308
+ V: new Set,
309
+ h: false,
310
+ U: false,
311
+ j: null,
312
312
  B: null,
313
- te: false,
314
- le: false,
315
- V: e === STATUS_ERROR ? signal(undefined, ERROR_SIGNAL) : null,
316
- oe: r
313
+ v: false,
314
+ K: null,
315
+ H: null,
316
+ ne: false,
317
+ re: false,
318
+ F: e === STATUS_ERROR ? signal(undefined, ERROR_SIGNAL) : null,
319
+ te: r,
320
+ le: null
317
321
  };
318
- const u = e === STATUS_PENDING ? l.U[REVEAL] : null;
322
+ const u = e === STATUS_PENDING ? l.D[REVEAL] : null;
319
323
  if (getOwner()) cleanup(() => {
320
324
  collecting.delete(o);
321
- if (u) revealHooks.ue(u, o);
325
+ if (u) revealHooks.oe(u, o);
322
326
  });
323
327
  // The `on` dependencies live OUTSIDE the boundary, as the condition of a
324
328
  // `<Show>` wrapping it would: created before the owner's context names it.
@@ -340,14 +344,14 @@ function createBoundary(e, n, r, t) {
340
344
  }
341
345
  }
342
346
  if (e) arm(o); else e = true;
343
- })).u |= CONFIG_REDERIVE;
347
+ })).i |= CONFIG_REDERIVE;
344
348
  }
345
349
  const a = {
346
- ...l.U,
350
+ ...l.D,
347
351
  [BOUNDARY]: o
348
352
  };
349
353
  if (revealHooks !== null) a[REVEAL] = null;
350
- l.U = a;
354
+ l.D = a;
351
355
  const i = runWithOwner(l, () => {
352
356
  const e = false ? computed(n, {
353
357
  name: "children"
@@ -356,26 +360,26 @@ function createBoundary(e, n, r, t) {
356
360
  name: "boundary"
357
361
  }) : computed(() => flatten(read(e)));
358
362
  });
359
- o.$ = i;
363
+ o.J = i;
360
364
  // A slot of the reveal order in its context (reveal.ts): gated until the
361
365
  // controller releases it — before the output's first pass.
362
- if (u) revealHooks.ae(u, o);
366
+ if (u) revealHooks.ue(u, o);
363
367
  const f = computed(() => {
364
368
  // Reveal order (reveal.ts): a gated slot shows its fallback — or, in a
365
369
  // collapsed tail, nothing — whatever its content's state; the
366
370
  // controller is told the pass ran (its readiness may have changed) and
367
371
  // releases it in turn.
368
- if (o.te) {
372
+ if (o.ne) {
369
373
  // Linked without being read: the tree settling (or changing)
370
374
  // re-derives this pass, which is how the controller hears of it.
371
375
  // It may release this very slot (the frontier whose content just
372
376
  // settled): the pass goes on to show it — the redraw it asks for is
373
377
  // refused mid-pass and not needed.
374
378
  link(i, getOwner());
375
- if (o.B !== null) revealHooks.j(o.B);
376
- if (o.te) {
377
- if (o.le) {
378
- o.P = true;
379
+ if (o.H !== null) revealHooks.P(o.H);
380
+ if (o.ne) {
381
+ if (o.re) {
382
+ o.v = true;
379
383
  return undefined;
380
384
  }
381
385
  return fallback(o);
@@ -388,7 +392,7 @@ function createBoundary(e, n, r, t) {
388
392
  // display-ahead `on` causes is the lane's, and must still be the
389
393
  // fallback. Linked, so its recovery re-derives this pass; read again
390
394
  // once clean.
391
- const n = i.G & (STATUS_PENDING | STATUS_ERROR);
395
+ const n = i.S & (STATUS_PENDING | STATUS_ERROR);
392
396
  if (n !== 0) {
393
397
  link(i, getOwner());
394
398
  if (n & e) {
@@ -398,12 +402,12 @@ function createBoundary(e, n, r, t) {
398
402
  }
399
403
  // Forwarded: a loading boundary showing content keeps it and
400
404
  // holds the frame like any reader (recorded for its `on`).
401
- o.H.add(i);
405
+ o.V.add(i);
402
406
  collecting.add(o);
403
407
  }
404
408
  // A pending passes an error boundary by (A6); an error, a loading
405
409
  // one; a loading boundary showing content forwards its pending.
406
- throw i.m.V;
410
+ throw i.m.F;
407
411
  }
408
412
  if (isCollecting(o)) {
409
413
  // A29's boundary exemption (#3540): a boundary MOUNTED over a held
@@ -418,7 +422,7 @@ function createBoundary(e, n, r, t) {
418
422
  // which reveals the content — a fallback staged earlier is replaced
419
423
  // ahead of the commit and never shown.
420
424
  const e = getOwner();
421
- if (o.H.size !== 0 && e.G & STATUS_UNINITIALIZED && !(e.u & CONFIG_HELD)) return fallback(o);
425
+ if (o.V.size !== 0 && e.S & STATUS_UNINITIALIZED && !(e.i & CONFIG_HELD)) return fallback(o);
422
426
  // Readers under it still unready: the fallback, the tree untouched.
423
427
  // The seam re-derives this pass when they settle.
424
428
  if (prune(o, true) !== 0) return fallback(o);
@@ -434,7 +438,7 @@ function createBoundary(e, n, r, t) {
434
438
  }
435
439
  // Forwarded: a loading boundary showing content keeps it and
436
440
  // holds the frame like any reader (recorded for its `on`).
437
- o.H.add(i);
441
+ o.V.add(i);
438
442
  collecting.add(o);
439
443
  }
440
444
  // A pending passes an error boundary by (A6).
@@ -442,16 +446,29 @@ function createBoundary(e, n, r, t) {
442
446
  }
443
447
  // Readers under it still unready (caught before, or while the read
444
448
  // ran the content at a mount): the fallback.
445
- if (isCollecting(o) && o.H.size !== 0) return fallback(o);
446
- o.L = true;
447
- o.P = false;
449
+ if (isCollecting(o) && o.V.size !== 0) return fallback(o);
450
+ o.h = true;
451
+ o.v = false;
452
+ // The fallback dies when the content that replaces it is on screen —
453
+ // with this flush's commits when the pass is the frame's, at the
454
+ // landing when the frame parks (the run is held with it, as the old
455
+ // children of a held pass are) — not now: a held swap keeps the
456
+ // fallback on screen, live, until it commits. A pass back to the
457
+ // fallback before then finds it still built and keeps it.
458
+ const t = o.le;
459
+ if (t !== null) globalQueue.enqueue(EFFECT_RENDER, () => {
460
+ if (!o.v && o.le === t) {
461
+ dispose(t);
462
+ o.le = null;
463
+ }
464
+ });
448
465
  release(o);
449
466
  // Shown: the slot leaves its reveal order (a later re-arm gates nobody
450
467
  // else; the frontier moves on).
451
- if (o.B !== null) {
452
- const e = o.B;
453
- o.B = null;
454
- revealHooks.j(e);
468
+ if (o.H !== null) {
469
+ const e = o.H;
470
+ o.H = null;
471
+ revealHooks.P(e);
455
472
  }
456
473
  return r;
457
474
  },
@@ -459,10 +476,10 @@ function createBoundary(e, n, r, t) {
459
476
  // and legitimately swaps mid-hydration (resume), so it must never be
460
477
  // frozen by snapshot capture.
461
478
  {
462
- ie: true
479
+ ae: true
463
480
  });
464
- f.u |= CONFIG_REDERIVE;
465
- o.K = f;
481
+ f.i |= CONFIG_REDERIVE;
482
+ o.Z = f;
466
483
  return accessor(f);
467
484
  }
468
485
 
@@ -470,18 +487,35 @@ function fallback(e) {
470
487
  // The swap a display-ahead re-arm asked for: this pass is the lane's — a
471
488
  // lane read by construction (REACTIVE_LANE_READ: `recompute`'s tail keeps
472
489
  // the pass in the lane).
473
- if (e.Z !== null) {
474
- setPassLane(e.Z);
475
- e.K.R |= REACTIVE_LANE_READ;
476
- e.Z = null;
490
+ if (e.B !== null) {
491
+ setPassLane(e.B);
492
+ e.Z.R |= REACTIVE_LANE_READ;
493
+ e.B = null;
494
+ }
495
+ e.v = true;
496
+ // Built in its own scope, like `Show`'s function child, once per show
497
+ // (D-39: the fallback is the display until a new answer replaces the
498
+ // error): a re-derive of the output while it shows — a second reader
499
+ // caught, the same error propagated again through a sibling's re-run, a
500
+ // retry pending after `reset()`, a later error — keeps the built
501
+ // fallback, its DOM and focus with it (#3991). A later error reaches it
502
+ // through the error accessor; only a fallback reading the accessor at its
503
+ // top level is rebuilt by it. Created beside the output — in its owner,
504
+ // outside the boundary, as the output is: its errors and runs are the
505
+ // enclosing boundary's — with the output's id, so what it builds gets the
506
+ // ids a build under the output would; disposed once content is on screen.
507
+ if (e.le === null) {
508
+ const n = getOwner();
509
+ e.le = runWithOwner(n._parent, () => computed(() => e.te(e), {
510
+ id: n.id
511
+ }));
477
512
  }
478
- e.P = true;
479
- return e.oe(e);
513
+ return read(e.le);
480
514
  }
481
515
 
482
516
  const ERROR_SIGNAL = {
483
517
  ownedWrite: true,
484
- ie: true
518
+ ae: true
485
519
  };
486
520
 
487
521
  /**
@@ -552,19 +586,19 @@ const ERROR_SIGNAL = {
552
586
  * }
553
587
  * ```
554
588
  */ function createErrorBoundary(e, n) {
555
- return createBoundary(STATUS_ERROR, e, e => n(accessor(e.V), () => reset(e)));
589
+ return createBoundary(STATUS_ERROR, e, e => n(accessor(e.F), () => reset(e)));
556
590
  }
557
591
 
558
592
  // Installed at module evaluation — present exactly when something imports a
559
593
  // boundary. An app without one pays the three null checks and nothing else.
560
- GlobalQueue.fe = catchStatus;
594
+ GlobalQueue.ie = catchStatus;
561
595
 
562
- GlobalQueue.ce = e => e.G & STATUS_UNINITIALIZED && catchStatus(e, STATUS_PENDING);
596
+ GlobalQueue.fe = e => e.S & STATUS_UNINITIALIZED && catchStatus(e, STATUS_PENDING);
563
597
 
564
- GlobalQueue.se = hidden;
598
+ GlobalQueue.ce = hidden;
565
599
 
566
- GlobalQueue.Ee = boundarySeam;
600
+ GlobalQueue.se = boundarySeam;
567
601
 
568
- GlobalQueue.de = heldRun;
602
+ GlobalQueue.Ee = heldRun;
569
603
 
570
604
  export { REVEAL, createErrorBoundary, createLoadingBoundary, prune, ready, redraw, revealHooks, setRevealHooks };