@solidjs/signals 2.0.0-rc.8 → 2.0.0-rc.9
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 +1399 -285
- package/dist/dev.attribution.js +459 -307
- package/dist/dev.js +1864 -435
- package/dist/observe/affects.js +3 -1
- package/dist/observe/attribution.js +7 -1
- package/dist/observe/boundaries.js +209 -154
- package/dist/observe/core/action.js +18 -8
- package/dist/observe/core/async.js +220 -110
- package/dist/observe/core/attribution-costs.js +66 -0
- package/dist/observe/core/attribution-feedback.js +282 -0
- package/dist/observe/core/attribution-hooks.js +23 -1
- package/dist/observe/core/attribution-queries.js +28 -0
- package/dist/observe/core/attribution.js +262 -485
- package/dist/observe/core/constants.js +34 -1
- package/dist/observe/core/context.js +3 -3
- package/dist/observe/core/core.js +867 -367
- package/dist/observe/core/dev.js +78 -17
- package/dist/observe/core/effect.js +67 -51
- package/dist/observe/core/error-hooks.js +71 -0
- package/dist/observe/core/external.js +4 -4
- package/dist/observe/core/graph.js +37 -37
- package/dist/observe/core/heap.js +45 -45
- package/dist/observe/core/invariants.js +2 -0
- package/dist/observe/core/lanes.js +86 -49
- package/dist/observe/core/optimistic.js +242 -95
- package/dist/observe/core/owner.js +98 -84
- package/dist/observe/core/scheduler.js +543 -305
- package/dist/observe/core/verdict.js +247 -129
- package/dist/observe/index.js +7 -3
- package/dist/observe/map.js +126 -124
- package/dist/observe/signals.js +33 -17
- package/dist/observe/store/index.js +2 -0
- package/dist/observe/store/next/optimistic.js +141 -132
- package/dist/observe/store/next/projection.js +34 -21
- package/dist/observe/store/next/reconcile.js +58 -56
- package/dist/observe/store/next/store.js +260 -146
- package/dist/observe/store/store.js +5 -3
- package/dist/observe/store/utils.js +948 -135
- package/dist/prod/attribution.js +26 -17
- package/dist/prod/boundaries.js +128 -76
- package/dist/prod/core/action.js +16 -8
- package/dist/prod/core/async.js +251 -143
- package/dist/prod/core/constants.js +34 -1
- package/dist/prod/core/context.js +3 -3
- package/dist/prod/core/core.js +843 -347
- package/dist/prod/core/dev.js +17 -1
- package/dist/prod/core/effect.js +48 -34
- package/dist/prod/core/error-hooks.js +71 -0
- package/dist/prod/core/external.js +4 -4
- package/dist/prod/core/graph.js +37 -37
- package/dist/prod/core/heap.js +45 -45
- package/dist/prod/core/lanes.js +90 -53
- package/dist/prod/core/optimistic.js +247 -100
- package/dist/prod/core/owner.js +57 -45
- package/dist/prod/core/scheduler.js +543 -305
- package/dist/prod/core/verdict.js +245 -127
- package/dist/prod/index.js +7 -3
- package/dist/prod/map.js +112 -112
- package/dist/prod/signals.js +28 -12
- package/dist/prod/store/next/optimistic.js +135 -128
- package/dist/prod/store/next/projection.js +31 -20
- package/dist/prod/store/next/store.js +325 -252
- package/dist/prod/store/store.js +2 -2
- package/dist/prod/store/utils.js +946 -135
- package/dist/types/attribution.d.ts +7 -2
- package/dist/types/attribution.prod.d.ts +10 -1
- package/dist/types/boundaries.d.ts +10 -1
- package/dist/types/core/action.d.ts +12 -5
- 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 +28 -3
- package/dist/types/core/attribution-queries.d.ts +10 -0
- package/dist/types/core/attribution.d.ts +51 -172
- package/dist/types/core/constants.d.ts +33 -0
- package/dist/types/core/core.d.ts +186 -5
- package/dist/types/core/dev.d.ts +129 -9
- package/dist/types/core/error-hooks.d.ts +71 -0
- package/dist/types/core/index.d.ts +3 -1
- package/dist/types/core/invariants.d.ts +4 -0
- package/dist/types/core/lanes.d.ts +31 -4
- package/dist/types/core/scheduler.d.ts +95 -2
- package/dist/types/core/types.d.ts +3 -0
- package/dist/types/index.d.ts +2 -2
- package/dist/types/signals.d.ts +8 -0
- package/dist/types/store/index.d.ts +2 -1
- package/dist/types/store/next/optimistic.d.ts +1 -1
- package/dist/types/store/next/store.d.ts +6 -5
- package/dist/types/store/next/target.d.ts +1 -1
- package/dist/types/store/utils.d.ts +177 -6
- package/package.json +1 -1
package/dist/observe/affects.js
CHANGED
|
@@ -4,6 +4,8 @@ import { ext, statusNotifierOf } from "./core/core.js";
|
|
|
4
4
|
|
|
5
5
|
import { $REFRESH, STATUS_PENDING } from "./core/constants.js";
|
|
6
6
|
|
|
7
|
+
import "./core/dev.js";
|
|
8
|
+
|
|
7
9
|
import { NotReadyError } from "./core/error.js";
|
|
8
10
|
|
|
9
11
|
import { GlobalQueue, globalQueue, schedule, shiftAffectsMarks } from "./core/scheduler.js";
|
|
@@ -108,7 +110,7 @@ GlobalQueue.G = releaseAffectsMarks;
|
|
|
108
110
|
|
|
109
111
|
GlobalQueue.M = markAffects;
|
|
110
112
|
|
|
111
|
-
GlobalQueue.
|
|
113
|
+
GlobalQueue.j = releaseAffectsMark;
|
|
112
114
|
|
|
113
115
|
function affects(e, r) {
|
|
114
116
|
const t = e?.[$TARGET];
|
|
@@ -1 +1,7 @@
|
|
|
1
|
-
export { attribution } from "./core/attribution.js";
|
|
1
|
+
export { attribution, formatOrigin, formatRerun } from "./core/attribution.js";
|
|
2
|
+
|
|
3
|
+
export { costs } from "./core/attribution-costs.js";
|
|
4
|
+
|
|
5
|
+
export { feedback } from "./core/attribution-feedback.js";
|
|
6
|
+
|
|
7
|
+
export { subscriptions, why } from "./core/attribution-queries.js";
|
|
@@ -1,23 +1,27 @@
|
|
|
1
|
-
import { runWithOwner, signal, untrack, read, computed, setSignal, ext, recompute } from "./core/core.js";
|
|
1
|
+
import { runWithOwner, signal, untrack, read, computed, spectate, setSignal, ext, recompute } from "./core/core.js";
|
|
2
2
|
|
|
3
3
|
import { NotReadyError, unwrapStatusError } from "./core/error.js";
|
|
4
4
|
|
|
5
5
|
import { createOwner, cleanup } from "./core/owner.js";
|
|
6
6
|
|
|
7
|
-
import { Queue, haltReactivity, schedule } from "./core/scheduler.js";
|
|
7
|
+
import { Queue, transitions, wakeParked, haltReactivity, schedule } from "./core/scheduler.js";
|
|
8
8
|
|
|
9
9
|
import { getContext, setContext, createContext } from "./core/context.js";
|
|
10
10
|
|
|
11
|
-
import { STATUS_PENDING, STATUS_ERROR, REACTIVE_DISPOSED, CONFIG_AUTO_DISPOSE } from "./core/constants.js";
|
|
11
|
+
import { STATUS_PENDING, STATUS_ERROR, REACTIVE_ZOMBIE, REACTIVE_DISPOSED, CONFIG_AUTO_DISPOSE } from "./core/constants.js";
|
|
12
12
|
|
|
13
13
|
import { attrHooks } from "./core/attribution-hooks.js";
|
|
14
14
|
|
|
15
15
|
import "./core/invariants.js";
|
|
16
16
|
|
|
17
|
+
import "./core/dev.js";
|
|
18
|
+
|
|
17
19
|
import "./core/verdict.js";
|
|
18
20
|
|
|
19
21
|
import "./core/effect.js";
|
|
20
22
|
|
|
23
|
+
import { reportClientError } from "./core/error-hooks.js";
|
|
24
|
+
|
|
21
25
|
import { accessor } from "./signals.js";
|
|
22
26
|
|
|
23
27
|
function boundaryComputed(t, e) {
|
|
@@ -26,44 +30,44 @@ function boundaryComputed(t, e) {
|
|
|
26
30
|
});
|
|
27
31
|
ext(r).h = (t, e) => {
|
|
28
32
|
// Use passed values if provided, otherwise read from node
|
|
29
|
-
const
|
|
30
|
-
const
|
|
33
|
+
const s = t !== undefined ? t : r.S;
|
|
34
|
+
const n = e !== undefined ? e : r.o?._;
|
|
31
35
|
// Notify both status dimensions like a render effect does; the queue chain
|
|
32
36
|
// consumes this boundary's own type and forwards the remainder upward until
|
|
33
37
|
// a boundary that handles it is found.
|
|
34
38
|
r.S &= ~r.R;
|
|
35
|
-
const i = r.
|
|
39
|
+
const i = r.T.notify(r, STATUS_PENDING | STATUS_ERROR, s, n);
|
|
36
40
|
// The queue is the only propagation channel: a foreign status must not stay
|
|
37
41
|
// reader-visible on the tree, or reads re-throw it across the boundary and
|
|
38
42
|
// link unrelated ambient contexts (the #2809 nested-boundary loop). Deps are
|
|
39
43
|
// untouched, so the tree still recomputes when the foreign source settles.
|
|
40
|
-
const o =
|
|
44
|
+
const o = s & ~r.R & (STATUS_PENDING | STATUS_ERROR);
|
|
41
45
|
if (o) {
|
|
42
46
|
r.S &= ~o;
|
|
43
|
-
if (r.o?._ ===
|
|
47
|
+
if (r.o?._ === n && !(r.S & (STATUS_PENDING | STATUS_ERROR))) if (r.o !== null) r.o._ = undefined;
|
|
44
48
|
}
|
|
45
49
|
// An ERROR the chain could not deliver to any boundary is uncaught. The
|
|
46
50
|
// scrub above already removed it from reader-visible state, so without
|
|
47
51
|
// escalation here it would vanish entirely (#2884) — halt-and-throw,
|
|
48
52
|
// exactly like an unhandled effect error.
|
|
49
|
-
if (!i &&
|
|
50
|
-
haltReactivity(unwrapStatusError(
|
|
51
|
-
throw
|
|
53
|
+
if (!i && s & STATUS_ERROR) {
|
|
54
|
+
haltReactivity(unwrapStatusError(n));
|
|
55
|
+
throw n;
|
|
52
56
|
}
|
|
53
57
|
};
|
|
54
58
|
r.R = e;
|
|
55
|
-
r.
|
|
59
|
+
r.C &= ~CONFIG_AUTO_DISPOSE;
|
|
56
60
|
recompute(r, true);
|
|
57
61
|
return r;
|
|
58
62
|
}
|
|
59
63
|
|
|
60
|
-
function createBoundChildren(t, e, r,
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
cleanup(() =>
|
|
64
|
+
function createBoundChildren(t, e, r, s) {
|
|
65
|
+
const n = t.T;
|
|
66
|
+
n.addChild(t.T = r);
|
|
67
|
+
cleanup(() => n.removeChild(t.T));
|
|
64
68
|
return runWithOwner(t, () => {
|
|
65
69
|
const t = computed(e);
|
|
66
|
-
return boundaryComputed(() => flatten(read(t)),
|
|
70
|
+
return boundaryComputed(() => flatten(read(t)), s);
|
|
67
71
|
});
|
|
68
72
|
}
|
|
69
73
|
|
|
@@ -82,53 +86,53 @@ function isRevealController(t) {
|
|
|
82
86
|
}
|
|
83
87
|
|
|
84
88
|
function isSlotReady(t) {
|
|
85
|
-
return isRevealController(t) ? t.
|
|
89
|
+
return isRevealController(t) ? t.N() : t.O.size === 0 && !t.I;
|
|
86
90
|
}
|
|
87
91
|
|
|
88
92
|
function isSlotMinimallyReady(t) {
|
|
89
|
-
return isRevealController(t) ? t.
|
|
93
|
+
return isRevealController(t) ? t.U() : isSlotReady(t);
|
|
90
94
|
}
|
|
91
95
|
|
|
92
|
-
function setSlotState(t, e, r,
|
|
93
|
-
setSignal(t.
|
|
94
|
-
setSignal(t.
|
|
96
|
+
function setSlotState(t, e, r, s) {
|
|
97
|
+
setSignal(t.v, r);
|
|
98
|
+
setSignal(t.D, s);
|
|
95
99
|
if (isRevealController(t)) {
|
|
96
|
-
if (!r && t.
|
|
97
|
-
return t.B(r,
|
|
100
|
+
if (!r && t.P === e) t.P = undefined;
|
|
101
|
+
return t.B(r, s);
|
|
98
102
|
}
|
|
99
|
-
if (!r && t.W === e && t.
|
|
103
|
+
if (!r && t.W === e && t.H) t.W = undefined;
|
|
100
104
|
}
|
|
101
105
|
|
|
102
106
|
class RevealController {
|
|
107
|
+
L;
|
|
103
108
|
F;
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
D=signal(false, {
|
|
109
|
+
V=[];
|
|
110
|
+
P;
|
|
111
|
+
v=signal(false, {
|
|
108
112
|
ownedWrite: true,
|
|
109
|
-
|
|
113
|
+
q: true
|
|
110
114
|
});
|
|
111
|
-
|
|
115
|
+
D=signal(false, {
|
|
112
116
|
ownedWrite: true,
|
|
113
|
-
|
|
117
|
+
q: true
|
|
114
118
|
});
|
|
119
|
+
Z=true;
|
|
115
120
|
J=true;
|
|
116
|
-
K=
|
|
117
|
-
X=false;
|
|
121
|
+
K=false;
|
|
118
122
|
constructor(t, e) {
|
|
119
|
-
this.
|
|
120
|
-
this.
|
|
123
|
+
this.L = t;
|
|
124
|
+
this.F = e;
|
|
121
125
|
}
|
|
122
|
-
|
|
123
|
-
for (let e = 0; e < this.
|
|
124
|
-
const r = this.
|
|
125
|
-
if ((isRevealController(r) ? r.
|
|
126
|
+
X(t) {
|
|
127
|
+
for (let e = 0; e < this.V.length; e++) {
|
|
128
|
+
const r = this.V[e];
|
|
129
|
+
if ((isRevealController(r) ? r.P : r.W) !== this) continue;
|
|
126
130
|
if (t(r) === false) return false;
|
|
127
131
|
}
|
|
128
132
|
return true;
|
|
129
133
|
}
|
|
130
|
-
|
|
131
|
-
return this.
|
|
134
|
+
N() {
|
|
135
|
+
return this.X(isSlotReady);
|
|
132
136
|
}
|
|
133
137
|
/**
|
|
134
138
|
* "Minimally ready" = this group has something visible to show under its own policy.
|
|
@@ -136,13 +140,13 @@ class RevealController {
|
|
|
136
140
|
* - `together`: every direct slot is minimally ready.
|
|
137
141
|
* - `sequential`: the first owned slot is minimally ready (frontier can advance).
|
|
138
142
|
* - `natural`: any owned slot is minimally ready.
|
|
139
|
-
*/
|
|
140
|
-
const t = untrack(this.
|
|
141
|
-
if (t === "together") return this.
|
|
143
|
+
*/ U() {
|
|
144
|
+
const t = untrack(this.L);
|
|
145
|
+
if (t === "together") return this.X(isSlotMinimallyReady);
|
|
142
146
|
if (t === "natural") {
|
|
143
147
|
let t = false;
|
|
144
148
|
let e = false;
|
|
145
|
-
this.
|
|
149
|
+
this.X(r => {
|
|
146
150
|
t = true;
|
|
147
151
|
if (isSlotMinimallyReady(r)) {
|
|
148
152
|
e = true;
|
|
@@ -153,61 +157,61 @@ class RevealController {
|
|
|
153
157
|
}
|
|
154
158
|
// sequential: only the first owned slot matters.
|
|
155
159
|
let e = true;
|
|
156
|
-
this.
|
|
160
|
+
this.X(t => {
|
|
157
161
|
e = isSlotMinimallyReady(t);
|
|
158
162
|
return false;
|
|
159
163
|
});
|
|
160
164
|
return e;
|
|
161
165
|
}
|
|
162
|
-
|
|
163
|
-
if (this.
|
|
164
|
-
this.
|
|
165
|
-
const e = untrack(this.
|
|
166
|
-
setSignal(t.
|
|
166
|
+
Y(t) {
|
|
167
|
+
if (this.V.includes(t)) return;
|
|
168
|
+
this.V.push(t);
|
|
169
|
+
const e = untrack(this.L);
|
|
170
|
+
setSignal(t.v, true), setSignal(t.D, e === "sequential" ? !!untrack(this.F) : false);
|
|
167
171
|
untrack(() => this.B());
|
|
168
172
|
}
|
|
169
173
|
$(t) {
|
|
170
|
-
const e = this.
|
|
171
|
-
if (e >= 0) this.
|
|
174
|
+
const e = this.V.indexOf(t);
|
|
175
|
+
if (e >= 0) this.V.splice(e, 1);
|
|
172
176
|
untrack(() => this.B());
|
|
173
177
|
}
|
|
174
178
|
B(t, e) {
|
|
175
|
-
if (this.
|
|
176
|
-
this.
|
|
177
|
-
const r = this.
|
|
178
|
-
const
|
|
179
|
+
if (this.K) return;
|
|
180
|
+
this.K = true;
|
|
181
|
+
const r = this.Z;
|
|
182
|
+
const s = this.J;
|
|
179
183
|
try {
|
|
180
|
-
const r = t ?? read(this.
|
|
184
|
+
const r = t ?? read(this.v), s = untrack(this.L), n = s === "sequential" && !!untrack(this.F), i = e ?? n;
|
|
181
185
|
if (r) {
|
|
182
186
|
// Held by an outer group. Propagate the hold (and whatever collapsed policy
|
|
183
187
|
// the outer asked for) down the whole subtree. Inner order is ignored while
|
|
184
188
|
// held; it resumes once the outer releases us.
|
|
185
|
-
this.
|
|
186
|
-
} else if (
|
|
189
|
+
this.X(t => setSlotState(t, this, true, i));
|
|
190
|
+
} else if (s === "natural") {
|
|
187
191
|
// Each child reveals based on its own readiness. A nested controller slot
|
|
188
192
|
// is released to run its own order locally — we bypass setSlotState for it
|
|
189
193
|
// so the parent backpointer survives for upward readiness notifications.
|
|
190
|
-
this.
|
|
194
|
+
this.X(t => {
|
|
191
195
|
if (isRevealController(t)) {
|
|
192
|
-
setSignal(t.P, false);
|
|
193
196
|
setSignal(t.D, false);
|
|
197
|
+
setSignal(t.v, false);
|
|
194
198
|
t.B(false, false);
|
|
195
199
|
} else {
|
|
196
200
|
setSlotState(t, this, !isSlotReady(t), false);
|
|
197
201
|
}
|
|
198
202
|
});
|
|
199
|
-
} else if (
|
|
203
|
+
} else if (s === "together") {
|
|
200
204
|
// Release when every direct slot is minimally ready (has something to show
|
|
201
205
|
// under its own order). A fully-ready inner together is minimally ready;
|
|
202
206
|
// sequential's first slot being ready is minimally ready; natural having any
|
|
203
207
|
// ready child is minimally ready. This lets `together` guarantee a single
|
|
204
208
|
// cohesive reveal without waiting for every grandchild.
|
|
205
|
-
const t = this.
|
|
206
|
-
this.
|
|
209
|
+
const t = this.X(isSlotMinimallyReady);
|
|
210
|
+
this.X(e => setSlotState(e, this, !t, false));
|
|
207
211
|
} else {
|
|
208
212
|
let t = false;
|
|
209
|
-
this.
|
|
210
|
-
if (t) return setSlotState(e, this, true,
|
|
213
|
+
this.X(e => {
|
|
214
|
+
if (t) return setSlotState(e, this, true, n);
|
|
211
215
|
if (isSlotReady(e)) return setSlotState(e, this, false, false);
|
|
212
216
|
t = true;
|
|
213
217
|
// Frontier slot. For a leaf, holding `_disabled=true` is what keeps its
|
|
@@ -217,8 +221,8 @@ class RevealController {
|
|
|
217
221
|
// advancing past this slot, and we bypass setSlotState so the parent
|
|
218
222
|
// backpointer survives for upward readiness notifications.
|
|
219
223
|
if (isRevealController(e)) {
|
|
220
|
-
setSignal(e.P, false);
|
|
221
224
|
setSignal(e.D, false);
|
|
225
|
+
setSignal(e.v, false);
|
|
222
226
|
e.B(false, false);
|
|
223
227
|
} else {
|
|
224
228
|
setSlotState(e, this, true, false);
|
|
@@ -226,54 +230,78 @@ class RevealController {
|
|
|
226
230
|
});
|
|
227
231
|
}
|
|
228
232
|
} finally {
|
|
229
|
-
this.
|
|
230
|
-
this.
|
|
231
|
-
this.
|
|
233
|
+
this.Z = this.N();
|
|
234
|
+
this.J = this.U();
|
|
235
|
+
this.K = false;
|
|
232
236
|
}
|
|
233
|
-
if (this.
|
|
237
|
+
if (this.P && (r !== this.Z || s !== this.J)) this.P.B();
|
|
234
238
|
}
|
|
235
239
|
}
|
|
236
240
|
|
|
237
241
|
class CollectionQueue extends Queue {
|
|
238
242
|
tt;
|
|
239
|
-
|
|
243
|
+
O=new Set;
|
|
240
244
|
et;
|
|
241
|
-
|
|
242
|
-
|
|
245
|
+
I=true;
|
|
246
|
+
v=signal(false, {
|
|
243
247
|
ownedWrite: true,
|
|
244
|
-
|
|
248
|
+
q: true
|
|
245
249
|
});
|
|
246
250
|
_;
|
|
247
|
-
|
|
251
|
+
D=signal(false, {
|
|
248
252
|
ownedWrite: true,
|
|
249
|
-
|
|
253
|
+
q: true
|
|
250
254
|
});
|
|
251
255
|
W;
|
|
252
|
-
|
|
256
|
+
H=false;
|
|
253
257
|
rt;
|
|
254
|
-
|
|
258
|
+
st=ON_INIT;
|
|
259
|
+
/** The boundary's owner — where a `caught` report locates itself, set before the children are built (a creation-time throw arrives before `_tree`). */
|
|
260
|
+
nt;
|
|
255
261
|
constructor(t) {
|
|
256
262
|
super();
|
|
257
263
|
this.tt = t;
|
|
258
264
|
}
|
|
259
265
|
run(t) {
|
|
260
|
-
if (!t || read(this.
|
|
266
|
+
if (!t || read(this.v) && (!_revealUsed || read(this.D))) return;
|
|
261
267
|
return super.run(t);
|
|
262
268
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
269
|
+
/** The `on` key, or ON_INIT when it throws. Evaluated mid-propagation,
|
|
270
|
+
* inside the pending node's own pass: read as a spectator so the key's
|
|
271
|
+
* sources never become that node's dependencies (#3528). */ it() {
|
|
272
|
+
return spectate(() => {
|
|
273
|
+
try {
|
|
274
|
+
return this.rt();
|
|
275
|
+
} catch {
|
|
276
|
+
return ON_INIT;
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
notify(t, e, r, s) {
|
|
281
|
+
if (!(e & this.tt)) return super.notify(t, e, r, s);
|
|
282
|
+
if (this.H && this.rt) {
|
|
283
|
+
const t = this.it();
|
|
284
|
+
if (t !== this.st) {
|
|
285
|
+
this.st = t;
|
|
286
|
+
this.H = false;
|
|
287
|
+
this.O.clear();
|
|
288
|
+
// Readers forwarded while this boundary showed content are behind the
|
|
289
|
+
// fallback now: they stop blocking (`reporterBlocksSource`), and the
|
|
290
|
+
// transactions they were holding must be re-judged for it (A33, #3375).
|
|
291
|
+
// What those readers still wait on is this boundary's to wait on now:
|
|
292
|
+
// they never re-notify (status propagation dedupes on the reader's
|
|
293
|
+
// `_pendingSources`), so the reset collects it from their registrations
|
|
294
|
+
// — the one place a forwarded reader is recorded (INV-3) — or a sibling
|
|
295
|
+
// reader's flight that lands first reveals them stale (A33, #3459).
|
|
296
|
+
for (const t of transitions) for (const [e, r] of t.ot) for (const t of r) if (this.lt(t)) {
|
|
297
|
+
this.O.add(e);
|
|
298
|
+
t.o?.ut?.forEach(t => this.O.add(t));
|
|
271
299
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
300
|
+
if (this.O.size) {
|
|
301
|
+
setSignal(this.v, true);
|
|
302
|
+
if (attrHooks !== null && this.tt & STATUS_PENDING) attrHooks.boundaryFallback(this, this.et, true);
|
|
303
|
+
}
|
|
304
|
+
wakeParked();
|
|
277
305
|
}
|
|
278
306
|
}
|
|
279
307
|
// Routing is dimension-independent: each boundary consumes only its own
|
|
@@ -286,47 +314,73 @@ class CollectionQueue extends Queue {
|
|
|
286
314
|
// dimension, while a status already caught by an inner boundary arrives with
|
|
287
315
|
// its dimension consumed from the mask and is correctly not re-routed
|
|
288
316
|
// (the Loading > Errored > content composition escape, #2856).
|
|
289
|
-
if (this.tt & STATUS_PENDING && this.
|
|
317
|
+
if (this.tt & STATUS_PENDING && this.H) return super.notify(t, e, r, s);
|
|
290
318
|
if (r & this.tt) {
|
|
291
|
-
this.
|
|
292
|
-
const e =
|
|
319
|
+
this.I = true;
|
|
320
|
+
const e = s?.source || t.o?._?.source;
|
|
293
321
|
if (e) {
|
|
294
|
-
const
|
|
295
|
-
this.
|
|
296
|
-
|
|
297
|
-
|
|
322
|
+
const r = this.O.size === 0;
|
|
323
|
+
this.O.add(e);
|
|
324
|
+
// A collecting boundary waits on everything the effect is pending on,
|
|
325
|
+
// not only the source this notification carries. Status propagation
|
|
326
|
+
// dedupes on the effect's `_pendingSources`: a source it already
|
|
327
|
+
// carries (a flight that started before an `on` reset cleared the
|
|
328
|
+
// set) is never re-reported, and that source's later re-flight
|
|
329
|
+
// stays invisible — the boundary revealed when its one collected
|
|
330
|
+
// source settled while the effect was still pending (#3375).
|
|
331
|
+
if (this.tt & STATUS_PENDING) t.o?.ut?.forEach(t => this.O.add(t));
|
|
332
|
+
if (r) {
|
|
333
|
+
setSignal(this.v, true);
|
|
298
334
|
if (attrHooks !== null && this.tt & STATUS_PENDING) attrHooks.boundaryFallback(this, this.et, true);
|
|
299
335
|
}
|
|
300
336
|
if (this.tt & STATUS_ERROR) {
|
|
301
|
-
|
|
337
|
+
const t = unwrapStatusError(e.o?._);
|
|
338
|
+
setSignal(this._, t);
|
|
339
|
+
// The client error hook: this boundary renders its fallback for
|
|
340
|
+
// it — the one road a rendered failure took that no global handler
|
|
341
|
+
// ever saw. `source` is the computation that threw (the status
|
|
342
|
+
// wrapper's, made at the first landing and kept downstream), so the
|
|
343
|
+
// hook hears where it broke as well as where it was met. Once per
|
|
344
|
+
// error object; a `reset()` re-collecting the same failure says
|
|
345
|
+
// nothing new.
|
|
346
|
+
reportClientError(t, this.nt, e);
|
|
302
347
|
}
|
|
303
348
|
}
|
|
304
349
|
}
|
|
305
350
|
e &= ~this.tt;
|
|
306
|
-
return e ? super.notify(t, e, r,
|
|
351
|
+
return e ? super.notify(t, e, r, s) : true;
|
|
352
|
+
}
|
|
353
|
+
/** Is `reporter` live and routed to this boundary — under it, with no
|
|
354
|
+
* collecting pending-type boundary in between (`reporterBlocksSource`'s test)? */ lt(t) {
|
|
355
|
+
if (t.ft & (REACTIVE_ZOMBIE | REACTIVE_DISPOSED)) return false;
|
|
356
|
+
for (let e = t.T; e; e = e._parent) {
|
|
357
|
+
if (e === this) return true;
|
|
358
|
+
if (e.tt & STATUS_PENDING && !e.H) return false;
|
|
359
|
+
}
|
|
360
|
+
return false;
|
|
307
361
|
}
|
|
308
|
-
|
|
309
|
-
for (const t of this.
|
|
362
|
+
ct() {
|
|
363
|
+
for (const t of this.O) {
|
|
310
364
|
// A source with a live affects() mark holds display state for the
|
|
311
365
|
// mark's lifetime (the visual channel): the marked node carries no
|
|
312
366
|
// status of its own, so the count is the liveness test. The release
|
|
313
367
|
// sweep (finalizePureQueue after mark release) re-runs this check.
|
|
314
|
-
if (t.
|
|
368
|
+
if (t.ft & REACTIVE_DISPOSED || !t.o?.t && !(t.S & this.tt) && !(this.tt & STATUS_ERROR && t.S & STATUS_PENDING)) this.O.delete(t);
|
|
315
369
|
}
|
|
316
|
-
if (!this.
|
|
317
|
-
if (this.tt & STATUS_PENDING && this.
|
|
318
|
-
this.
|
|
370
|
+
if (!this.O.size) {
|
|
371
|
+
if (this.tt & STATUS_PENDING && this.I && !this.H && this.et) {
|
|
372
|
+
this.I = !!(this.et.S & this.tt);
|
|
319
373
|
} else {
|
|
320
|
-
this.
|
|
374
|
+
this.I = false;
|
|
321
375
|
}
|
|
322
|
-
if (!this.
|
|
323
|
-
setSignal(this.
|
|
376
|
+
if (!this.I) {
|
|
377
|
+
setSignal(this.v, false);
|
|
324
378
|
if (attrHooks !== null && this.tt & STATUS_PENDING) attrHooks.boundaryFallback(this, this.et, false);
|
|
325
379
|
if (this.rt) {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
380
|
+
// A throw (value not yet committed) leaves _prevOn stale; the next
|
|
381
|
+
// notify then resets.
|
|
382
|
+
const t = this.it();
|
|
383
|
+
if (t !== ON_INIT) this.st = t;
|
|
330
384
|
}
|
|
331
385
|
}
|
|
332
386
|
}
|
|
@@ -334,16 +388,17 @@ class CollectionQueue extends Queue {
|
|
|
334
388
|
}
|
|
335
389
|
}
|
|
336
390
|
|
|
337
|
-
function createCollectionBoundary(t, e, r,
|
|
338
|
-
const
|
|
339
|
-
if (_revealUsed) setContext(RevealControllerContext, null,
|
|
391
|
+
function createCollectionBoundary(t, e, r, s) {
|
|
392
|
+
const n = createOwner();
|
|
393
|
+
if (_revealUsed) setContext(RevealControllerContext, null, n);
|
|
340
394
|
const i = new CollectionQueue(t);
|
|
395
|
+
i.nt = n;
|
|
341
396
|
if (t === STATUS_ERROR) i._ = signal(undefined, {
|
|
342
397
|
ownedWrite: true,
|
|
343
|
-
|
|
398
|
+
q: true
|
|
344
399
|
});
|
|
345
|
-
if (
|
|
346
|
-
const o = i.et = createBoundChildren(
|
|
400
|
+
if (s) i.rt = s;
|
|
401
|
+
const o = i.et = createBoundChildren(n, e, i, t);
|
|
347
402
|
// Prime source tracking so reveal registration sees pending sources.
|
|
348
403
|
untrack(() => {
|
|
349
404
|
let e = false;
|
|
@@ -352,23 +407,23 @@ function createCollectionBoundary(t, e, r, n) {
|
|
|
352
407
|
} catch (t) {
|
|
353
408
|
if (t instanceof NotReadyError) e = true; else throw t;
|
|
354
409
|
}
|
|
355
|
-
i.
|
|
410
|
+
i.I = e || !!(o.S & t) || o.o?._ instanceof NotReadyError;
|
|
356
411
|
});
|
|
357
412
|
const l = _revealUsed && t === STATUS_PENDING ? getContext(RevealControllerContext) : null;
|
|
358
413
|
if (l) {
|
|
359
414
|
i.W = l;
|
|
360
|
-
l.
|
|
415
|
+
l.Y(i);
|
|
361
416
|
cleanup(() => l.$(i));
|
|
362
417
|
}
|
|
363
418
|
return accessor(computed(() => {
|
|
364
|
-
if (!read(i.
|
|
419
|
+
if (!read(i.v)) {
|
|
365
420
|
const t = read(o);
|
|
366
|
-
if (!untrack(() => read(i.
|
|
421
|
+
if (!untrack(() => read(i.v))) return i.H = true, t;
|
|
367
422
|
}
|
|
368
423
|
// Collapsed reveal slots suppress their own output entirely; the
|
|
369
424
|
// renderer treats the hole as empty, so the cast never leaks to users
|
|
370
425
|
// outside a `createRevealOrder` scope.
|
|
371
|
-
if (_revealUsed && read(i.
|
|
426
|
+
if (_revealUsed && read(i.D)) return undefined;
|
|
372
427
|
return r(i);
|
|
373
428
|
},
|
|
374
429
|
// Boundary structure, not a user source: its value is fallback-or-content and
|
|
@@ -376,7 +431,7 @@ function createCollectionBoundary(t, e, r, n) {
|
|
|
376
431
|
// by snapshot capture. The tree no longer carries foreign status flags, so
|
|
377
432
|
// capture can't rely on PENDING to skip this node the way it used to.
|
|
378
433
|
{
|
|
379
|
-
|
|
434
|
+
q: true
|
|
380
435
|
}));
|
|
381
436
|
}
|
|
382
437
|
|
|
@@ -430,11 +485,11 @@ function createCollectionBoundary(t, e, r, n) {
|
|
|
430
485
|
* ```
|
|
431
486
|
*/ function createErrorBoundary(t, e) {
|
|
432
487
|
return createCollectionBoundary(STATUS_ERROR, t, t => e(accessor(t._), () => {
|
|
433
|
-
for (const e of t.
|
|
488
|
+
for (const e of t.O) {
|
|
434
489
|
// Non-computed sources (patch-channel registrations under plain
|
|
435
490
|
// owners) are not recomputable — their reset is the record's next
|
|
436
491
|
// transition re-applying the patch (re-audit 2, P1-4).
|
|
437
|
-
if (e.
|
|
492
|
+
if (e.ht !== undefined) recompute(e);
|
|
438
493
|
}
|
|
439
494
|
schedule();
|
|
440
495
|
}));
|
|
@@ -483,21 +538,21 @@ function createCollectionBoundary(t, e, r, n) {
|
|
|
483
538
|
*/ function createRevealOrder(t, e) {
|
|
484
539
|
_revealUsed = true;
|
|
485
540
|
const r = createOwner();
|
|
486
|
-
const
|
|
487
|
-
const
|
|
488
|
-
const o = new RevealController(
|
|
541
|
+
const s = getContext(RevealControllerContext);
|
|
542
|
+
const n = e?.order || SEQUENTIAL_ACCESSOR, i = e?.collapsed || FALSE_ACCESSOR;
|
|
543
|
+
const o = new RevealController(n, i);
|
|
489
544
|
setContext(RevealControllerContext, o, r);
|
|
490
545
|
return runWithOwner(r, () => {
|
|
491
546
|
const e = t();
|
|
492
547
|
computed(() => {
|
|
493
|
-
|
|
548
|
+
n();
|
|
494
549
|
i();
|
|
495
550
|
o.B();
|
|
496
551
|
});
|
|
497
|
-
if (
|
|
498
|
-
o.
|
|
499
|
-
|
|
500
|
-
cleanup(() =>
|
|
552
|
+
if (s) {
|
|
553
|
+
o.P = s;
|
|
554
|
+
s.Y(o);
|
|
555
|
+
cleanup(() => s.$(o));
|
|
501
556
|
}
|
|
502
557
|
return e;
|
|
503
558
|
});
|
|
@@ -551,36 +606,36 @@ function createCollectionBoundary(t, e, r, n) {
|
|
|
551
606
|
}
|
|
552
607
|
|
|
553
608
|
function flattenArray(t, e = [], r) {
|
|
554
|
-
let
|
|
555
|
-
let
|
|
609
|
+
let s = null;
|
|
610
|
+
let n = false;
|
|
556
611
|
for (let i = 0; i < t.length; i++) {
|
|
557
612
|
try {
|
|
558
|
-
let
|
|
559
|
-
if (typeof
|
|
613
|
+
let s = t[i];
|
|
614
|
+
if (typeof s === "function" && !s.length) {
|
|
560
615
|
if (r?.doNotUnwrap) {
|
|
561
|
-
e.push(
|
|
562
|
-
|
|
616
|
+
e.push(s);
|
|
617
|
+
n = true;
|
|
563
618
|
continue;
|
|
564
619
|
}
|
|
565
620
|
do {
|
|
566
|
-
|
|
567
|
-
} while (typeof
|
|
621
|
+
s = s();
|
|
622
|
+
} while (typeof s === "function" && !s.length);
|
|
568
623
|
}
|
|
569
|
-
if (Array.isArray(
|
|
624
|
+
if (Array.isArray(s)) {
|
|
570
625
|
// OR, don't overwrite: an accessor already pushed under doNotUnwrap
|
|
571
626
|
// still needs the resolving wrapper even when a later sibling
|
|
572
627
|
// fragment contains no functions (#3133).
|
|
573
|
-
|
|
574
|
-
} else if (r?.skipNonRendered && (
|
|
628
|
+
n = flattenArray(s, e, r) || n;
|
|
629
|
+
} else if (r?.skipNonRendered && (s == null || s === true || s === false || s === "")) {
|
|
575
630
|
// skip
|
|
576
|
-
} else e.push(
|
|
631
|
+
} else e.push(s);
|
|
577
632
|
} catch (t) {
|
|
578
633
|
if (!(t instanceof NotReadyError)) throw t;
|
|
579
|
-
|
|
634
|
+
s = t;
|
|
580
635
|
}
|
|
581
636
|
}
|
|
582
|
-
if (
|
|
583
|
-
return
|
|
637
|
+
if (s) throw s;
|
|
638
|
+
return n;
|
|
584
639
|
}
|
|
585
640
|
|
|
586
641
|
export { CollectionQueue, RevealController, createErrorBoundary, createLoadingBoundary, createRevealOrder, flatten };
|