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