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