@solidjs/signals 2.0.0-rc.7 → 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 +6357 -0
- package/dist/dev.attribution.js +2485 -0
- package/dist/dev.js +2851 -7027
- package/dist/observe/affects.js +129 -0
- package/dist/observe/attribution.js +7 -0
- package/dist/observe/boundaries.js +641 -0
- package/dist/observe/core/action.js +177 -0
- package/dist/observe/core/async.js +846 -0
- 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 +82 -0
- package/dist/observe/core/attribution-queries.js +28 -0
- package/dist/observe/core/attribution.js +2095 -0
- package/dist/observe/core/constants.js +248 -0
- package/dist/{types-cjs/core/context.d.cts → observe/core/context.js} +37 -12
- package/dist/observe/core/core.js +1789 -0
- package/dist/observe/core/dev.js +293 -0
- package/dist/observe/core/effect.js +195 -0
- package/dist/observe/core/error-hooks.js +71 -0
- package/dist/observe/core/error.js +80 -0
- package/dist/observe/core/external.js +98 -0
- package/dist/observe/core/graph.js +155 -0
- package/dist/observe/core/heap.js +147 -0
- package/dist/observe/core/invariants.js +45 -0
- package/dist/observe/core/lanes.js +205 -0
- package/dist/observe/core/optimistic.js +585 -0
- package/dist/observe/core/owner.js +322 -0
- package/dist/observe/core/scheduler.js +1428 -0
- package/dist/observe/core/verdict.js +669 -0
- package/dist/observe/index.js +59 -0
- package/dist/observe/map.js +484 -0
- package/dist/{types-cjs/signals.d.cts → observe/signals.js} +311 -317
- package/dist/observe/store/index.js +40 -0
- package/dist/observe/store/next/optimistic.js +660 -0
- package/dist/observe/store/next/projection.js +255 -0
- package/dist/observe/store/next/reconcile.js +375 -0
- package/dist/observe/store/next/store.js +2327 -0
- package/dist/observe/store/next/target.js +51 -0
- package/dist/observe/store/store.js +325 -0
- package/dist/observe/store/storePath.js +103 -0
- package/dist/observe/store/utils.js +1023 -0
- package/dist/prod/attribution.js +41 -0
- package/dist/prod/boundaries.js +128 -76
- package/dist/prod/core/action.js +65 -37
- package/dist/prod/core/async.js +324 -172
- package/dist/prod/core/constants.js +57 -1
- package/dist/prod/core/context.js +3 -3
- package/dist/prod/core/core.js +1003 -364
- package/dist/prod/core/dev.js +17 -1
- package/dist/prod/core/effect.js +76 -42
- 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 +61 -56
- package/dist/prod/core/lanes.js +107 -63
- package/dist/prod/core/optimistic.js +374 -93
- package/dist/prod/core/owner.js +62 -46
- package/dist/prod/core/scheduler.js +653 -286
- package/dist/prod/core/verdict.js +245 -127
- package/dist/prod/index.js +15 -5
- package/dist/prod/map.js +292 -141
- package/dist/prod/signals.js +29 -16
- package/dist/prod/store/next/optimistic.js +77 -55
- package/dist/prod/store/next/projection.js +31 -20
- package/dist/prod/store/next/reconcile.js +87 -84
- package/dist/prod/store/next/store.js +718 -401
- package/dist/prod/store/next/target.js +32 -10
- package/dist/prod/store/store.js +12 -14
- package/dist/prod/store/utils.js +950 -130
- package/dist/types/attribution.d.ts +20 -0
- package/dist/types/attribution.prod.d.ts +22 -0
- package/dist/types/boundaries.d.ts +10 -1
- package/dist/types/core/action.d.ts +12 -5
- package/dist/types/core/async.d.ts +1 -1
- 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 +147 -9
- package/dist/types/core/attribution-queries.d.ts +10 -0
- package/dist/types/core/attribution.d.ts +216 -182
- package/dist/types/core/constants.d.ts +56 -0
- package/dist/types/core/core.d.ts +190 -2
- package/dist/types/core/dev.d.ts +246 -28
- package/dist/types/core/effect.d.ts +8 -1
- package/dist/types/core/error-hooks.d.ts +71 -0
- package/dist/types/core/index.d.ts +4 -1
- package/dist/types/core/invariants.d.ts +4 -0
- package/dist/types/core/lanes.d.ts +39 -5
- package/dist/types/core/scheduler.d.ts +153 -0
- package/dist/types/core/types.d.ts +32 -9
- package/dist/types/index.d.ts +10 -3
- package/dist/types/map.d.ts +2 -0
- package/dist/types/signals.d.ts +8 -0
- package/dist/types/store/index.d.ts +2 -0
- package/dist/types/store/next/optimistic.d.ts +1 -1
- package/dist/types/store/next/store.d.ts +11 -5
- package/dist/types/store/next/target.d.ts +37 -10
- package/dist/types/store/utils.d.ts +178 -0
- package/package.json +21 -17
- package/dist/node.cjs +0 -10541
- package/dist/node.dev.cjs +0 -13724
- 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 -23
- package/dist/types-cjs/core/attribution-hooks.d.cts +0 -127
- package/dist/types-cjs/core/attribution.d.cts +0 -495
- package/dist/types-cjs/core/constants.d.cts +0 -159
- package/dist/types-cjs/core/core.d.cts +0 -185
- package/dist/types-cjs/core/dev.d.cts +0 -136
- package/dist/types-cjs/core/effect.d.cts +0 -30
- package/dist/types-cjs/core/error.d.cts +0 -56
- package/dist/types-cjs/core/external.d.cts +0 -15
- package/dist/types-cjs/core/graph.d.cts +0 -28
- package/dist/types-cjs/core/heap.d.cts +0 -24
- 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 -54
- 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 -236
- package/dist/types-cjs/core/types.d.cts +0 -204
- 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/store/index.d.cts +0 -18
- package/dist/types-cjs/store/next/optimistic.d.cts +0 -25
- package/dist/types-cjs/store/next/projection.d.cts +0 -8
- package/dist/types-cjs/store/next/reconcile.d.cts +0 -10
- package/dist/types-cjs/store/next/store.d.cts +0 -121
- package/dist/types-cjs/store/next/target.d.cts +0 -166
- package/dist/types-cjs/store/store.d.cts +0 -143
- package/dist/types-cjs/store/storePath.d.cts +0 -58
- package/dist/types-cjs/store/utils.d.cts +0 -74
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const EMPTY = Object.freeze([]);
|
|
2
|
+
|
|
3
|
+
const noop = () => {};
|
|
4
|
+
|
|
5
|
+
const attribution = {
|
|
6
|
+
enable: noop,
|
|
7
|
+
disable: noop,
|
|
8
|
+
subscribe: () => noop,
|
|
9
|
+
history: () => EMPTY,
|
|
10
|
+
waterfalls: () => EMPTY,
|
|
11
|
+
holds: () => EMPTY,
|
|
12
|
+
navigations: () => EMPTY,
|
|
13
|
+
interactions: () => EMPTY,
|
|
14
|
+
markFlight: noop
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// The named surface, inert: every fold empty, every query empty, every
|
|
18
|
+
// formatter blank. Typed against the real entry's exports so the two cannot
|
|
19
|
+
// drift.
|
|
20
|
+
const costs = () => ({
|
|
21
|
+
scopes: [],
|
|
22
|
+
writes: []
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const feedback = () => ({
|
|
26
|
+
sources: [],
|
|
27
|
+
interactions: [],
|
|
28
|
+
navigations: [],
|
|
29
|
+
flights: [],
|
|
30
|
+
fallbacks: []
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const why = () => [];
|
|
34
|
+
|
|
35
|
+
const subscriptions = () => [];
|
|
36
|
+
|
|
37
|
+
const formatRerun = () => "";
|
|
38
|
+
|
|
39
|
+
const formatOrigin = () => "";
|
|
40
|
+
|
|
41
|
+
export { attribution, costs, feedback, formatOrigin, formatRerun, subscriptions, why };
|
package/dist/prod/boundaries.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
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 "./core/invariants.js";
|
|
14
14
|
|
|
@@ -16,6 +16,8 @@ import "./core/verdict.js";
|
|
|
16
16
|
|
|
17
17
|
import "./core/effect.js";
|
|
18
18
|
|
|
19
|
+
import { reportClientError } from "./core/error-hooks.js";
|
|
20
|
+
|
|
19
21
|
import { accessor } from "./signals.js";
|
|
20
22
|
|
|
21
23
|
function boundaryComputed(e, t) {
|
|
@@ -80,11 +82,11 @@ function isRevealController(e) {
|
|
|
80
82
|
}
|
|
81
83
|
|
|
82
84
|
function isSlotReady(e) {
|
|
83
|
-
return isRevealController(e) ? e.O() : e.
|
|
85
|
+
return isRevealController(e) ? e.O() : e.I.size === 0 && !e.v;
|
|
84
86
|
}
|
|
85
87
|
|
|
86
88
|
function isSlotMinimallyReady(e) {
|
|
87
|
-
return isRevealController(e) ? e.
|
|
89
|
+
return isRevealController(e) ? e.U() : isSlotReady(e);
|
|
88
90
|
}
|
|
89
91
|
|
|
90
92
|
function setSlotState(e, t, r, n) {
|
|
@@ -99,34 +101,34 @@ function setSlotState(e, t, r, n) {
|
|
|
99
101
|
|
|
100
102
|
class RevealController {
|
|
101
103
|
F;
|
|
102
|
-
|
|
103
|
-
|
|
104
|
+
V;
|
|
105
|
+
q=[];
|
|
104
106
|
j;
|
|
105
107
|
D=signal(false, {
|
|
106
108
|
ownedWrite: true,
|
|
107
|
-
|
|
109
|
+
Z: true
|
|
108
110
|
});
|
|
109
111
|
P=signal(false, {
|
|
110
112
|
ownedWrite: true,
|
|
111
|
-
|
|
113
|
+
Z: true
|
|
112
114
|
});
|
|
115
|
+
H=true;
|
|
113
116
|
J=true;
|
|
114
|
-
K=
|
|
115
|
-
X=false;
|
|
117
|
+
K=false;
|
|
116
118
|
constructor(e, t) {
|
|
117
119
|
this.F = e;
|
|
118
|
-
this.
|
|
120
|
+
this.V = t;
|
|
119
121
|
}
|
|
120
|
-
|
|
121
|
-
for (let t = 0; t < this.
|
|
122
|
-
const r = this.
|
|
122
|
+
X(e) {
|
|
123
|
+
for (let t = 0; t < this.q.length; t++) {
|
|
124
|
+
const r = this.q[t];
|
|
123
125
|
if ((isRevealController(r) ? r.j : r.W) !== this) continue;
|
|
124
126
|
if (e(r) === false) return false;
|
|
125
127
|
}
|
|
126
128
|
return true;
|
|
127
129
|
}
|
|
128
130
|
O() {
|
|
129
|
-
return this.
|
|
131
|
+
return this.X(isSlotReady);
|
|
130
132
|
}
|
|
131
133
|
/**
|
|
132
134
|
* "Minimally ready" = this group has something visible to show under its own policy.
|
|
@@ -134,13 +136,13 @@ class RevealController {
|
|
|
134
136
|
* - `together`: every direct slot is minimally ready.
|
|
135
137
|
* - `sequential`: the first owned slot is minimally ready (frontier can advance).
|
|
136
138
|
* - `natural`: any owned slot is minimally ready.
|
|
137
|
-
*/
|
|
139
|
+
*/ U() {
|
|
138
140
|
const e = untrack(this.F);
|
|
139
|
-
if (e === "together") return this.
|
|
141
|
+
if (e === "together") return this.X(isSlotMinimallyReady);
|
|
140
142
|
if (e === "natural") {
|
|
141
143
|
let e = false;
|
|
142
144
|
let t = false;
|
|
143
|
-
this.
|
|
145
|
+
this.X(r => {
|
|
144
146
|
e = true;
|
|
145
147
|
if (isSlotMinimallyReady(r)) {
|
|
146
148
|
t = true;
|
|
@@ -151,41 +153,41 @@ class RevealController {
|
|
|
151
153
|
}
|
|
152
154
|
// sequential: only the first owned slot matters.
|
|
153
155
|
let t = true;
|
|
154
|
-
this.
|
|
156
|
+
this.X(e => {
|
|
155
157
|
t = isSlotMinimallyReady(e);
|
|
156
158
|
return false;
|
|
157
159
|
});
|
|
158
160
|
return t;
|
|
159
161
|
}
|
|
160
|
-
|
|
161
|
-
if (this.
|
|
162
|
-
this.
|
|
162
|
+
Y(e) {
|
|
163
|
+
if (this.q.includes(e)) return;
|
|
164
|
+
this.q.push(e);
|
|
163
165
|
const t = untrack(this.F);
|
|
164
|
-
setSignal(e.D, true), setSignal(e.P, t === "sequential" ? !!untrack(this.
|
|
166
|
+
setSignal(e.D, true), setSignal(e.P, t === "sequential" ? !!untrack(this.V) : false);
|
|
165
167
|
untrack(() => this.B());
|
|
166
168
|
}
|
|
167
169
|
$(e) {
|
|
168
|
-
const t = this.
|
|
169
|
-
if (t >= 0) this.
|
|
170
|
+
const t = this.q.indexOf(e);
|
|
171
|
+
if (t >= 0) this.q.splice(t, 1);
|
|
170
172
|
untrack(() => this.B());
|
|
171
173
|
}
|
|
172
174
|
B(e, t) {
|
|
173
|
-
if (this.
|
|
174
|
-
this.
|
|
175
|
-
const r = this.
|
|
176
|
-
const n = this.
|
|
175
|
+
if (this.K) return;
|
|
176
|
+
this.K = true;
|
|
177
|
+
const r = this.H;
|
|
178
|
+
const n = this.J;
|
|
177
179
|
try {
|
|
178
|
-
const r = e ?? read(this.D), n = untrack(this.F), s = n === "sequential" && !!untrack(this.
|
|
180
|
+
const r = e ?? read(this.D), n = untrack(this.F), s = n === "sequential" && !!untrack(this.V), i = t ?? s;
|
|
179
181
|
if (r) {
|
|
180
182
|
// Held by an outer group. Propagate the hold (and whatever collapsed policy
|
|
181
183
|
// the outer asked for) down the whole subtree. Inner order is ignored while
|
|
182
184
|
// held; it resumes once the outer releases us.
|
|
183
|
-
this.
|
|
185
|
+
this.X(e => setSlotState(e, this, true, i));
|
|
184
186
|
} else if (n === "natural") {
|
|
185
187
|
// Each child reveals based on its own readiness. A nested controller slot
|
|
186
188
|
// is released to run its own order locally — we bypass setSlotState for it
|
|
187
189
|
// so the parent backpointer survives for upward readiness notifications.
|
|
188
|
-
this.
|
|
190
|
+
this.X(e => {
|
|
189
191
|
if (isRevealController(e)) {
|
|
190
192
|
setSignal(e.P, false);
|
|
191
193
|
setSignal(e.D, false);
|
|
@@ -200,11 +202,11 @@ class RevealController {
|
|
|
200
202
|
// sequential's first slot being ready is minimally ready; natural having any
|
|
201
203
|
// ready child is minimally ready. This lets `together` guarantee a single
|
|
202
204
|
// cohesive reveal without waiting for every grandchild.
|
|
203
|
-
const e = this.
|
|
204
|
-
this.
|
|
205
|
+
const e = this.X(isSlotMinimallyReady);
|
|
206
|
+
this.X(t => setSlotState(t, this, !e, false));
|
|
205
207
|
} else {
|
|
206
208
|
let e = false;
|
|
207
|
-
this.
|
|
209
|
+
this.X(t => {
|
|
208
210
|
if (e) return setSlotState(t, this, true, s);
|
|
209
211
|
if (isSlotReady(t)) return setSlotState(t, this, false, false);
|
|
210
212
|
e = true;
|
|
@@ -224,32 +226,34 @@ class RevealController {
|
|
|
224
226
|
});
|
|
225
227
|
}
|
|
226
228
|
} finally {
|
|
227
|
-
this.
|
|
228
|
-
this.
|
|
229
|
-
this.
|
|
229
|
+
this.H = this.O();
|
|
230
|
+
this.J = this.U();
|
|
231
|
+
this.K = false;
|
|
230
232
|
}
|
|
231
|
-
if (this.j && (r !== this.
|
|
233
|
+
if (this.j && (r !== this.H || n !== this.J)) this.j.B();
|
|
232
234
|
}
|
|
233
235
|
}
|
|
234
236
|
|
|
235
237
|
class CollectionQueue extends Queue {
|
|
236
238
|
ee;
|
|
237
|
-
|
|
239
|
+
I=new Set;
|
|
238
240
|
te;
|
|
239
|
-
|
|
241
|
+
v=true;
|
|
240
242
|
D=signal(false, {
|
|
241
243
|
ownedWrite: true,
|
|
242
|
-
|
|
244
|
+
Z: true
|
|
243
245
|
});
|
|
244
246
|
_;
|
|
245
247
|
P=signal(false, {
|
|
246
248
|
ownedWrite: true,
|
|
247
|
-
|
|
249
|
+
Z: true
|
|
248
250
|
});
|
|
249
251
|
W;
|
|
250
252
|
L=false;
|
|
251
253
|
re;
|
|
252
254
|
ne=ON_INIT;
|
|
255
|
+
/** The boundary's owner — where a `caught` report locates itself, set before the children are built (a creation-time throw arrives before `_tree`). */
|
|
256
|
+
se;
|
|
253
257
|
constructor(e) {
|
|
254
258
|
super();
|
|
255
259
|
this.ee = e;
|
|
@@ -258,20 +262,41 @@ class CollectionQueue extends Queue {
|
|
|
258
262
|
if (!e || read(this.D) && (!_revealUsed || read(this.P))) return;
|
|
259
263
|
return super.run(e);
|
|
260
264
|
}
|
|
265
|
+
/** The `on` key, or ON_INIT when it throws. Evaluated mid-propagation,
|
|
266
|
+
* inside the pending node's own pass: read as a spectator so the key's
|
|
267
|
+
* sources never become that node's dependencies (#3528). */ ie() {
|
|
268
|
+
return spectate(() => {
|
|
269
|
+
try {
|
|
270
|
+
return this.re();
|
|
271
|
+
} catch {
|
|
272
|
+
return ON_INIT;
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
}
|
|
261
276
|
notify(e, t, r, n) {
|
|
262
277
|
if (!(t & this.ee)) return super.notify(e, t, r, n);
|
|
263
278
|
if (this.L && this.re) {
|
|
264
|
-
const e =
|
|
265
|
-
try {
|
|
266
|
-
return this.re();
|
|
267
|
-
} catch {
|
|
268
|
-
return ON_INIT;
|
|
269
|
-
}
|
|
270
|
-
});
|
|
279
|
+
const e = this.ie();
|
|
271
280
|
if (e !== this.ne) {
|
|
272
281
|
this.ne = e;
|
|
273
282
|
this.L = false;
|
|
274
|
-
this.
|
|
283
|
+
this.I.clear();
|
|
284
|
+
// Readers forwarded while this boundary showed content are behind the
|
|
285
|
+
// fallback now: they stop blocking (`reporterBlocksSource`), and the
|
|
286
|
+
// transactions they were holding must be re-judged for it (A33, #3375).
|
|
287
|
+
// What those readers still wait on is this boundary's to wait on now:
|
|
288
|
+
// they never re-notify (status propagation dedupes on the reader's
|
|
289
|
+
// `_pendingSources`), so the reset collects it from their registrations
|
|
290
|
+
// — the one place a forwarded reader is recorded (INV-3) — or a sibling
|
|
291
|
+
// reader's flight that lands first reveals them stale (A33, #3459).
|
|
292
|
+
for (const e of transitions) for (const [t, r] of e.oe) for (const e of r) if (this.le(e)) {
|
|
293
|
+
this.I.add(t);
|
|
294
|
+
e.o?.ae?.forEach(e => this.I.add(e));
|
|
295
|
+
}
|
|
296
|
+
if (this.I.size) {
|
|
297
|
+
setSignal(this.D, true);
|
|
298
|
+
}
|
|
299
|
+
wakeParked();
|
|
275
300
|
}
|
|
276
301
|
}
|
|
277
302
|
// Routing is dimension-independent: each boundary consumes only its own
|
|
@@ -286,43 +311,69 @@ class CollectionQueue extends Queue {
|
|
|
286
311
|
// (the Loading > Errored > content composition escape, #2856).
|
|
287
312
|
if (this.ee & STATUS_PENDING && this.L) return super.notify(e, t, r, n);
|
|
288
313
|
if (r & this.ee) {
|
|
289
|
-
this.
|
|
314
|
+
this.v = true;
|
|
290
315
|
const t = n?.source || e.o?._?.source;
|
|
291
316
|
if (t) {
|
|
292
|
-
const
|
|
293
|
-
this.
|
|
294
|
-
|
|
317
|
+
const r = this.I.size === 0;
|
|
318
|
+
this.I.add(t);
|
|
319
|
+
// A collecting boundary waits on everything the effect is pending on,
|
|
320
|
+
// not only the source this notification carries. Status propagation
|
|
321
|
+
// dedupes on the effect's `_pendingSources`: a source it already
|
|
322
|
+
// carries (a flight that started before an `on` reset cleared the
|
|
323
|
+
// set) is never re-reported, and that source's later re-flight
|
|
324
|
+
// stays invisible — the boundary revealed when its one collected
|
|
325
|
+
// source settled while the effect was still pending (#3375).
|
|
326
|
+
if (this.ee & STATUS_PENDING) e.o?.ae?.forEach(e => this.I.add(e));
|
|
327
|
+
if (r) {
|
|
295
328
|
setSignal(this.D, true);
|
|
296
329
|
}
|
|
297
330
|
if (this.ee & STATUS_ERROR) {
|
|
298
|
-
|
|
331
|
+
const e = unwrapStatusError(t.o?._);
|
|
332
|
+
setSignal(this._, e);
|
|
333
|
+
// The client error hook: this boundary renders its fallback for
|
|
334
|
+
// it — the one road a rendered failure took that no global handler
|
|
335
|
+
// ever saw. `source` is the computation that threw (the status
|
|
336
|
+
// wrapper's, made at the first landing and kept downstream), so the
|
|
337
|
+
// hook hears where it broke as well as where it was met. Once per
|
|
338
|
+
// error object; a `reset()` re-collecting the same failure says
|
|
339
|
+
// nothing new.
|
|
340
|
+
reportClientError(e, this.se, t);
|
|
299
341
|
}
|
|
300
342
|
}
|
|
301
343
|
}
|
|
302
344
|
t &= ~this.ee;
|
|
303
345
|
return t ? super.notify(e, t, r, n) : true;
|
|
304
346
|
}
|
|
305
|
-
|
|
306
|
-
|
|
347
|
+
/** Is `reporter` live and routed to this boundary — under it, with no
|
|
348
|
+
* collecting pending-type boundary in between (`reporterBlocksSource`'s test)? */ le(e) {
|
|
349
|
+
if (e.ue & (REACTIVE_ZOMBIE | REACTIVE_DISPOSED)) return false;
|
|
350
|
+
for (let t = e.C; t; t = t._parent) {
|
|
351
|
+
if (t === this) return true;
|
|
352
|
+
if (t.ee & STATUS_PENDING && !t.L) return false;
|
|
353
|
+
}
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
fe() {
|
|
357
|
+
for (const e of this.I) {
|
|
307
358
|
// A source with a live affects() mark holds display state for the
|
|
308
359
|
// mark's lifetime (the visual channel): the marked node carries no
|
|
309
360
|
// status of its own, so the count is the liveness test. The release
|
|
310
361
|
// sweep (finalizePureQueue after mark release) re-runs this check.
|
|
311
|
-
if (e.
|
|
362
|
+
if (e.ue & REACTIVE_DISPOSED || !e.o?.t && !(e.S & this.ee) && !(this.ee & STATUS_ERROR && e.S & STATUS_PENDING)) this.I.delete(e);
|
|
312
363
|
}
|
|
313
|
-
if (!this.
|
|
314
|
-
if (this.ee & STATUS_PENDING && this.
|
|
315
|
-
this.
|
|
364
|
+
if (!this.I.size) {
|
|
365
|
+
if (this.ee & STATUS_PENDING && this.v && !this.L && this.te) {
|
|
366
|
+
this.v = !!(this.te.S & this.ee);
|
|
316
367
|
} else {
|
|
317
|
-
this.
|
|
368
|
+
this.v = false;
|
|
318
369
|
}
|
|
319
|
-
if (!this.
|
|
370
|
+
if (!this.v) {
|
|
320
371
|
setSignal(this.D, false);
|
|
321
372
|
if (this.re) {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
373
|
+
// A throw (value not yet committed) leaves _prevOn stale; the next
|
|
374
|
+
// notify then resets.
|
|
375
|
+
const e = this.ie();
|
|
376
|
+
if (e !== ON_INIT) this.ne = e;
|
|
326
377
|
}
|
|
327
378
|
}
|
|
328
379
|
}
|
|
@@ -334,9 +385,10 @@ function createCollectionBoundary(e, t, r, n) {
|
|
|
334
385
|
const s = createOwner();
|
|
335
386
|
if (_revealUsed) setContext(RevealControllerContext, null, s);
|
|
336
387
|
const i = new CollectionQueue(e);
|
|
388
|
+
i.se = s;
|
|
337
389
|
if (e === STATUS_ERROR) i._ = signal(undefined, {
|
|
338
390
|
ownedWrite: true,
|
|
339
|
-
|
|
391
|
+
Z: true
|
|
340
392
|
});
|
|
341
393
|
if (n) i.re = n;
|
|
342
394
|
const o = i.te = createBoundChildren(s, t, i, e);
|
|
@@ -348,12 +400,12 @@ function createCollectionBoundary(e, t, r, n) {
|
|
|
348
400
|
} catch (e) {
|
|
349
401
|
if (e instanceof NotReadyError) t = true; else throw e;
|
|
350
402
|
}
|
|
351
|
-
i.
|
|
403
|
+
i.v = t || !!(o.S & e) || o.o?._ instanceof NotReadyError;
|
|
352
404
|
});
|
|
353
405
|
const l = _revealUsed && e === STATUS_PENDING ? getContext(RevealControllerContext) : null;
|
|
354
406
|
if (l) {
|
|
355
407
|
i.W = l;
|
|
356
|
-
l.
|
|
408
|
+
l.Y(i);
|
|
357
409
|
cleanup(() => l.$(i));
|
|
358
410
|
}
|
|
359
411
|
return accessor(computed(() => {
|
|
@@ -372,7 +424,7 @@ function createCollectionBoundary(e, t, r, n) {
|
|
|
372
424
|
// by snapshot capture. The tree no longer carries foreign status flags, so
|
|
373
425
|
// capture can't rely on PENDING to skip this node the way it used to.
|
|
374
426
|
{
|
|
375
|
-
|
|
427
|
+
Z: true
|
|
376
428
|
}));
|
|
377
429
|
}
|
|
378
430
|
|
|
@@ -426,11 +478,11 @@ function createCollectionBoundary(e, t, r, n) {
|
|
|
426
478
|
* ```
|
|
427
479
|
*/ function createErrorBoundary(e, t) {
|
|
428
480
|
return createCollectionBoundary(STATUS_ERROR, e, e => t(accessor(e._), () => {
|
|
429
|
-
for (const t of e.
|
|
481
|
+
for (const t of e.I) {
|
|
430
482
|
// Non-computed sources (patch-channel registrations under plain
|
|
431
483
|
// owners) are not recomputable — their reset is the record's next
|
|
432
484
|
// transition re-applying the patch (re-audit 2, P1-4).
|
|
433
|
-
if (t.
|
|
485
|
+
if (t.ce !== undefined) recompute(t);
|
|
434
486
|
}
|
|
435
487
|
schedule();
|
|
436
488
|
}));
|
|
@@ -492,7 +544,7 @@ function createCollectionBoundary(e, t, r, n) {
|
|
|
492
544
|
});
|
|
493
545
|
if (n) {
|
|
494
546
|
o.j = n;
|
|
495
|
-
n.
|
|
547
|
+
n.Y(o);
|
|
496
548
|
cleanup(() => n.$(o));
|
|
497
549
|
}
|
|
498
550
|
return t;
|
package/dist/prod/core/action.js
CHANGED
|
@@ -1,14 +1,25 @@
|
|
|
1
|
-
import { globalQueue, activeTransition, currentTransition, schedule, flush } from "./scheduler.js";
|
|
1
|
+
import { setOrigin, globalQueue, activeTransition, currentTransition, schedule, actionStepDepth, flush, enterActionStep, exitActionStep } from "./scheduler.js";
|
|
2
2
|
|
|
3
3
|
import { isThenable } from "./async.js";
|
|
4
4
|
|
|
5
5
|
import "./core.js";
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
/** Invocation order across all actions — the provenance every slice of an
|
|
8
|
+
* action runs under (scheduler `origin`): the flights and overrides its
|
|
9
|
+
* ambient windows issue are stamped with it, so a later action's override
|
|
10
|
+
* can tell this action's late answer from its own (#3331). */ let actionSeq = 0;
|
|
11
|
+
|
|
12
|
+
function restoreTransition(e, t, n) {
|
|
13
|
+
const r = setOrigin(e);
|
|
14
|
+
globalQueue.initTransition(t);
|
|
15
|
+
const i = n();
|
|
16
|
+
// A nested action resuming synchronously (its body yielded a non-thenable)
|
|
17
|
+
// runs this inside the OUTER action's slice: draining here would park the
|
|
18
|
+
// shared transaction and detach the outer body's remaining writes (the
|
|
19
|
+
// flush() rule, scheduler.ts). The outer step's own return drains.
|
|
20
|
+
if (actionStepDepth === 0) flush();
|
|
21
|
+
setOrigin(r);
|
|
22
|
+
return i;
|
|
12
23
|
}
|
|
13
24
|
|
|
14
25
|
/**
|
|
@@ -36,15 +47,22 @@ function restoreTransition(e, r) {
|
|
|
36
47
|
* `yield` is the transaction-safe suspension point: the action waits for a
|
|
37
48
|
* yielded promise and re-enters the transaction before running the code after
|
|
38
49
|
* it. A plain `await` does NOT — the runtime has no hook into an async
|
|
39
|
-
* generator's internal await continuations, so
|
|
40
|
-
*
|
|
41
|
-
* immediately
|
|
42
|
-
*
|
|
50
|
+
* generator's internal await continuations, so code between an `await` and
|
|
51
|
+
* the next `yield` runs OUTSIDE the transaction: writes to fresh signals
|
|
52
|
+
* commit immediately, and anything that creates a reader there — `until()`,
|
|
53
|
+
* `latest()`, a memo or effect, a mount — is created mainline, where a read of
|
|
54
|
+
* this action's held state makes it born held (A29): staged with the
|
|
55
|
+
* transaction and replayed at its commit. For `until()` that commit is the
|
|
56
|
+
* settle its own promise holds open (#3482). `await` is still the ergonomic
|
|
57
|
+
* choice for typed results; just put a bare `yield` before any write or
|
|
58
|
+
* reader creation that follows it — including the expression of the next
|
|
59
|
+
* `yield`, which is evaluated before the step re-enters:
|
|
43
60
|
*
|
|
44
61
|
* ```ts
|
|
45
62
|
* const saved = await api.createTodo(text); // typed result
|
|
46
|
-
* yield; // re-enter the transaction before writing
|
|
63
|
+
* yield; // re-enter the transaction before writing or reading
|
|
47
64
|
* setTodos(t => { ... });
|
|
65
|
+
* yield until(() => todos.some(t => t.id === saved.id));
|
|
48
66
|
* ```
|
|
49
67
|
*
|
|
50
68
|
* (For the same reason, don't call `flush()` inside an action body — it
|
|
@@ -74,36 +92,46 @@ function restoreTransition(e, r) {
|
|
|
74
92
|
* await addTodo("buy milk");
|
|
75
93
|
* ```
|
|
76
94
|
*/ function action(e) {
|
|
77
|
-
return (...
|
|
78
|
-
const i = e(...
|
|
95
|
+
return (...t) => new Promise((n, r) => {
|
|
96
|
+
const i = e(...t);
|
|
97
|
+
const o = ++actionSeq;
|
|
98
|
+
// The first slice's window runs to the scheduled flush, which clears
|
|
99
|
+
// the provenance with the window — no restore here.
|
|
100
|
+
setOrigin(o);
|
|
79
101
|
globalQueue.initTransition();
|
|
80
|
-
let
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
102
|
+
let s = activeTransition;
|
|
103
|
+
s.pe.push(i);
|
|
104
|
+
s.he = true;
|
|
105
|
+
const done = (e, t, o = false) => {
|
|
106
|
+
s = currentTransition(s);
|
|
107
|
+
const u = s.pe.indexOf(i);
|
|
108
|
+
if (u >= 0) s.pe.splice(u, 1);
|
|
86
109
|
// Re-adopt through initTransition like every other resumption site:
|
|
87
110
|
// a bare setActiveTransition leaves globalQueue._batch as a detached
|
|
88
111
|
// ambient batch, and anything registered before the scheduled flush
|
|
89
112
|
// (held writes on a merging transition, optimistic overrides,
|
|
90
113
|
// affects() marks) lands there with nothing to ever finalize it.
|
|
91
|
-
globalQueue.initTransition(
|
|
114
|
+
globalQueue.initTransition(s);
|
|
92
115
|
schedule();
|
|
93
|
-
|
|
116
|
+
o ? r(t) : n(e);
|
|
94
117
|
};
|
|
95
|
-
const step = (e,
|
|
96
|
-
let
|
|
118
|
+
const step = (e, t) => {
|
|
119
|
+
let n;
|
|
120
|
+
// The body is on the stack between these brackets: flush() is
|
|
121
|
+
// refused inside (FLUSH_IN_ACTION, scheduler.ts).
|
|
122
|
+
enterActionStep();
|
|
97
123
|
try {
|
|
98
|
-
|
|
124
|
+
n = t ? i.throw(e) : i.next(e);
|
|
99
125
|
} catch (e) {
|
|
126
|
+
exitActionStep();
|
|
100
127
|
return done(undefined, e, true);
|
|
101
128
|
}
|
|
129
|
+
exitActionStep();
|
|
102
130
|
// A rejected iterator result (async generators) means the error already
|
|
103
131
|
// escaped the generator body — it is completed, and throwing back in
|
|
104
132
|
// would just reject again forever. Settle instead.
|
|
105
|
-
if (isThenable(
|
|
106
|
-
run(
|
|
133
|
+
if (isThenable(n)) return void n.then(run, e => done(undefined, e, true));
|
|
134
|
+
run(n);
|
|
107
135
|
};
|
|
108
136
|
const run = e => {
|
|
109
137
|
if (e.done) return done(e.value);
|
|
@@ -114,23 +142,23 @@ function restoreTransition(e, r) {
|
|
|
114
142
|
// the action so its iterator never leaks in the transition. The
|
|
115
143
|
// settled flag implements A+ 2.3.3.3.4.1: a throw after the thenable
|
|
116
144
|
// already called a callback is ignored.
|
|
117
|
-
let
|
|
145
|
+
let t = false;
|
|
118
146
|
try {
|
|
119
147
|
if (isThenable(e.value)) return void e.value.then(e => {
|
|
120
|
-
if (
|
|
121
|
-
|
|
122
|
-
restoreTransition(o, () => step(e));
|
|
148
|
+
if (t) return;
|
|
149
|
+
t = true;
|
|
150
|
+
restoreTransition(o, s, () => step(e));
|
|
123
151
|
}, e => {
|
|
124
|
-
if (
|
|
125
|
-
|
|
126
|
-
restoreTransition(o, () => step(e, true));
|
|
152
|
+
if (t) return;
|
|
153
|
+
t = true;
|
|
154
|
+
restoreTransition(o, s, () => step(e, true));
|
|
127
155
|
});
|
|
128
156
|
} catch (e) {
|
|
129
|
-
if (
|
|
130
|
-
|
|
131
|
-
return void restoreTransition(o, () => step(e, true));
|
|
157
|
+
if (t) return;
|
|
158
|
+
t = true;
|
|
159
|
+
return void restoreTransition(o, s, () => step(e, true));
|
|
132
160
|
}
|
|
133
|
-
restoreTransition(o, () => step(e.value));
|
|
161
|
+
restoreTransition(o, s, () => step(e.value));
|
|
134
162
|
};
|
|
135
163
|
step();
|
|
136
164
|
});
|