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