@solidjs/signals 2.0.0-beta.20 → 2.0.0-beta.22
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/README.md +5 -1
- package/dist/dev.js +267 -307
- package/dist/node.cjs +1125 -1168
- package/dist/prod/affects.js +71 -163
- package/dist/prod/boundaries.js +141 -137
- package/dist/prod/core/action.js +50 -12
- package/dist/prod/core/async.js +83 -99
- package/dist/prod/core/core.js +255 -298
- package/dist/prod/core/effect.js +46 -46
- package/dist/prod/core/external.js +4 -4
- package/dist/prod/core/graph.js +54 -46
- package/dist/prod/core/heap.js +58 -50
- package/dist/prod/core/lanes.js +34 -34
- package/dist/prod/core/optimistic.js +64 -106
- package/dist/prod/core/owner.js +52 -52
- package/dist/prod/core/scheduler.js +217 -211
- package/dist/prod/core/verdict.js +144 -78
- package/dist/prod/map.js +101 -101
- package/dist/prod/signals.js +23 -5
- package/dist/prod/store/optimistic.js +11 -11
- package/dist/prod/store/projection.js +2 -2
- package/dist/prod/store/store.js +14 -14
- package/dist/types/affects.d.ts +9 -9
- package/dist/types/core/action.d.ts +16 -0
- package/dist/types/core/async.d.ts +1 -1
- package/dist/types/core/error.d.ts +7 -0
- package/dist/types/core/scheduler.d.ts +1 -6
- package/dist/types/core/types.d.ts +6 -11
- package/dist/types-cjs/affects.d.cts +9 -9
- package/dist/types-cjs/core/action.d.cts +16 -0
- package/dist/types-cjs/core/async.d.cts +1 -1
- package/dist/types-cjs/core/error.d.cts +7 -0
- package/dist/types-cjs/core/scheduler.d.cts +1 -6
- package/dist/types-cjs/core/types.d.cts +6 -11
- package/package.json +1 -1
package/dist/prod/boundaries.js
CHANGED
|
@@ -22,23 +22,23 @@ function boundaryComputed(e, t) {
|
|
|
22
22
|
const r = computed(e, {
|
|
23
23
|
lazy: true
|
|
24
24
|
});
|
|
25
|
-
r.
|
|
25
|
+
r.i = (e, t) => {
|
|
26
26
|
// Use passed values if provided, otherwise read from node
|
|
27
|
-
const n = e !== undefined ? e : r.
|
|
28
|
-
const s = t !== undefined ? t : r.
|
|
27
|
+
const n = e !== undefined ? e : r.S;
|
|
28
|
+
const s = t !== undefined ? t : r._;
|
|
29
29
|
// Notify both status dimensions like a render effect does; the queue chain
|
|
30
30
|
// consumes this boundary's own type and forwards the remainder upward until
|
|
31
31
|
// a boundary that handles it is found.
|
|
32
|
-
r.
|
|
33
|
-
const i = r.
|
|
32
|
+
r.S &= ~r.R;
|
|
33
|
+
const i = r.C.notify(r, STATUS_PENDING | STATUS_ERROR, n, s);
|
|
34
34
|
// The queue is the only propagation channel: a foreign status must not stay
|
|
35
35
|
// reader-visible on the tree, or reads re-throw it across the boundary and
|
|
36
36
|
// link unrelated ambient contexts (the #2809 nested-boundary loop). Deps are
|
|
37
37
|
// untouched, so the tree still recomputes when the foreign source settles.
|
|
38
|
-
const o = n & ~r.
|
|
38
|
+
const o = n & ~r.R & (STATUS_PENDING | STATUS_ERROR);
|
|
39
39
|
if (o) {
|
|
40
|
-
r.
|
|
41
|
-
if (r.
|
|
40
|
+
r.S &= ~o;
|
|
41
|
+
if (r._ === s && !(r.S & (STATUS_PENDING | STATUS_ERROR))) r._ = undefined;
|
|
42
42
|
}
|
|
43
43
|
// An ERROR the chain could not deliver to any boundary is uncaught. The
|
|
44
44
|
// scrub above already removed it from reader-visible state, so without
|
|
@@ -49,16 +49,16 @@ function boundaryComputed(e, t) {
|
|
|
49
49
|
throw s;
|
|
50
50
|
}
|
|
51
51
|
};
|
|
52
|
-
r.
|
|
53
|
-
r.
|
|
52
|
+
r.R = t;
|
|
53
|
+
r.T &= ~CONFIG_AUTO_DISPOSE;
|
|
54
54
|
recompute(r, true);
|
|
55
55
|
return r;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
function createBoundChildren(e, t, r, n) {
|
|
59
|
-
const s = e.
|
|
60
|
-
s.addChild(e.
|
|
61
|
-
cleanup(() => s.removeChild(e.
|
|
59
|
+
const s = e.C;
|
|
60
|
+
s.addChild(e.C = r);
|
|
61
|
+
cleanup(() => s.removeChild(e.C));
|
|
62
62
|
return runWithOwner(e, () => {
|
|
63
63
|
const e = computed(t);
|
|
64
64
|
return boundaryComputed(() => flatten(read(e)), n);
|
|
@@ -80,53 +80,53 @@ function isRevealController(e) {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
function isSlotReady(e) {
|
|
83
|
-
return isRevealController(e) ? e.
|
|
83
|
+
return isRevealController(e) ? e.O() : e.v.size === 0 && !e.N;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
function isSlotMinimallyReady(e) {
|
|
87
|
-
return isRevealController(e) ? e.
|
|
87
|
+
return isRevealController(e) ? e.U() : isSlotReady(e);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
function setSlotState(e, t, r, n) {
|
|
91
|
-
setSignal(e.
|
|
92
|
-
setSignal(e.
|
|
91
|
+
setSignal(e.I, r);
|
|
92
|
+
setSignal(e.D, n);
|
|
93
93
|
if (isRevealController(e)) {
|
|
94
|
-
if (!r && e.
|
|
95
|
-
return e.
|
|
94
|
+
if (!r && e.P === t) e.P = undefined;
|
|
95
|
+
return e.j(r, n);
|
|
96
96
|
}
|
|
97
|
-
if (!r && e.
|
|
97
|
+
if (!r && e.B === t && e.W) e.B = undefined;
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
class RevealController {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
101
|
+
L;
|
|
102
|
+
F;
|
|
103
|
+
q=[];
|
|
104
|
+
P;
|
|
105
|
+
I=signal(false, {
|
|
106
106
|
ownedWrite: true,
|
|
107
|
-
|
|
107
|
+
V: true
|
|
108
108
|
});
|
|
109
|
-
|
|
109
|
+
D=signal(false, {
|
|
110
110
|
ownedWrite: true,
|
|
111
|
-
|
|
111
|
+
V: true
|
|
112
112
|
});
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
113
|
+
H=true;
|
|
114
|
+
J=true;
|
|
115
|
+
K=false;
|
|
116
116
|
constructor(e, t) {
|
|
117
|
-
this.
|
|
118
|
-
this.
|
|
117
|
+
this.L = e;
|
|
118
|
+
this.F = t;
|
|
119
119
|
}
|
|
120
|
-
|
|
121
|
-
for (let t = 0; t < this.
|
|
122
|
-
const r = this.
|
|
123
|
-
if ((isRevealController(r) ? r.
|
|
120
|
+
X(e) {
|
|
121
|
+
for (let t = 0; t < this.q.length; t++) {
|
|
122
|
+
const r = this.q[t];
|
|
123
|
+
if ((isRevealController(r) ? r.P : r.B) !== this) continue;
|
|
124
124
|
if (e(r) === false) return false;
|
|
125
125
|
}
|
|
126
126
|
return true;
|
|
127
127
|
}
|
|
128
|
-
|
|
129
|
-
return this.
|
|
128
|
+
O() {
|
|
129
|
+
return this.X(isSlotReady);
|
|
130
130
|
}
|
|
131
131
|
/**
|
|
132
132
|
* "Minimally ready" = this group has something visible to show under its own policy.
|
|
@@ -134,13 +134,13 @@ class RevealController {
|
|
|
134
134
|
* - `together`: every direct slot is minimally ready.
|
|
135
135
|
* - `sequential`: the first owned slot is minimally ready (frontier can advance).
|
|
136
136
|
* - `natural`: any owned slot is minimally ready.
|
|
137
|
-
*/
|
|
138
|
-
const e = untrack(this.
|
|
139
|
-
if (e === "together") return this.
|
|
137
|
+
*/ U() {
|
|
138
|
+
const e = untrack(this.L);
|
|
139
|
+
if (e === "together") return this.X(isSlotMinimallyReady);
|
|
140
140
|
if (e === "natural") {
|
|
141
141
|
let e = false;
|
|
142
142
|
let t = false;
|
|
143
|
-
this.
|
|
143
|
+
this.X(r => {
|
|
144
144
|
e = true;
|
|
145
145
|
if (isSlotMinimallyReady(r)) {
|
|
146
146
|
t = true;
|
|
@@ -151,45 +151,45 @@ class RevealController {
|
|
|
151
151
|
}
|
|
152
152
|
// sequential: only the first owned slot matters.
|
|
153
153
|
let t = true;
|
|
154
|
-
this.
|
|
154
|
+
this.X(e => {
|
|
155
155
|
t = isSlotMinimallyReady(e);
|
|
156
156
|
return false;
|
|
157
157
|
});
|
|
158
158
|
return t;
|
|
159
159
|
}
|
|
160
|
-
|
|
161
|
-
if (this.
|
|
162
|
-
this.
|
|
163
|
-
const t = untrack(this.
|
|
164
|
-
setSignal(e.
|
|
165
|
-
untrack(() => this.
|
|
160
|
+
Y(e) {
|
|
161
|
+
if (this.q.includes(e)) return;
|
|
162
|
+
this.q.push(e);
|
|
163
|
+
const t = untrack(this.L);
|
|
164
|
+
setSignal(e.I, true), setSignal(e.D, t === "sequential" ? !!untrack(this.F) : false);
|
|
165
|
+
untrack(() => this.j());
|
|
166
166
|
}
|
|
167
|
-
|
|
168
|
-
const t = this.
|
|
169
|
-
if (t >= 0) this.
|
|
170
|
-
untrack(() => this.
|
|
167
|
+
Z(e) {
|
|
168
|
+
const t = this.q.indexOf(e);
|
|
169
|
+
if (t >= 0) this.q.splice(t, 1);
|
|
170
|
+
untrack(() => this.j());
|
|
171
171
|
}
|
|
172
|
-
|
|
173
|
-
if (this.
|
|
174
|
-
this.
|
|
175
|
-
const r = this.
|
|
176
|
-
const n = this.
|
|
172
|
+
j(e, t) {
|
|
173
|
+
if (this.K) return;
|
|
174
|
+
this.K = true;
|
|
175
|
+
const r = this.H;
|
|
176
|
+
const n = this.J;
|
|
177
177
|
try {
|
|
178
|
-
const r = e ?? read(this.
|
|
178
|
+
const r = e ?? read(this.I), n = untrack(this.L), s = n === "sequential" && !!untrack(this.F), i = t ?? s;
|
|
179
179
|
if (r) {
|
|
180
180
|
// Held by an outer group. Propagate the hold (and whatever collapsed policy
|
|
181
181
|
// the outer asked for) down the whole subtree. Inner order is ignored while
|
|
182
182
|
// held; it resumes once the outer releases us.
|
|
183
|
-
this.
|
|
183
|
+
this.X(e => setSlotState(e, this, true, i));
|
|
184
184
|
} else if (n === "natural") {
|
|
185
185
|
// Each child reveals based on its own readiness. A nested controller slot
|
|
186
186
|
// is released to run its own order locally — we bypass setSlotState for it
|
|
187
187
|
// so the parent backpointer survives for upward readiness notifications.
|
|
188
|
-
this.
|
|
188
|
+
this.X(e => {
|
|
189
189
|
if (isRevealController(e)) {
|
|
190
|
-
setSignal(e.
|
|
191
|
-
setSignal(e.
|
|
192
|
-
e.
|
|
190
|
+
setSignal(e.D, false);
|
|
191
|
+
setSignal(e.I, false);
|
|
192
|
+
e.j(false, false);
|
|
193
193
|
} else {
|
|
194
194
|
setSlotState(e, this, !isSlotReady(e), false);
|
|
195
195
|
}
|
|
@@ -200,11 +200,11 @@ class RevealController {
|
|
|
200
200
|
// sequential's first slot being ready is minimally ready; natural having any
|
|
201
201
|
// ready child is minimally ready. This lets `together` guarantee a single
|
|
202
202
|
// cohesive reveal without waiting for every grandchild.
|
|
203
|
-
const e = this.
|
|
204
|
-
this.
|
|
203
|
+
const e = this.X(isSlotMinimallyReady);
|
|
204
|
+
this.X(t => setSlotState(t, this, !e, false));
|
|
205
205
|
} else {
|
|
206
206
|
let e = false;
|
|
207
|
-
this.
|
|
207
|
+
this.X(t => {
|
|
208
208
|
if (e) return setSlotState(t, this, true, s);
|
|
209
209
|
if (isSlotReady(t)) return setSlotState(t, this, false, false);
|
|
210
210
|
e = true;
|
|
@@ -215,63 +215,63 @@ class RevealController {
|
|
|
215
215
|
// advancing past this slot, and we bypass setSlotState so the parent
|
|
216
216
|
// backpointer survives for upward readiness notifications.
|
|
217
217
|
if (isRevealController(t)) {
|
|
218
|
-
setSignal(t.
|
|
219
|
-
setSignal(t.
|
|
220
|
-
t.
|
|
218
|
+
setSignal(t.D, false);
|
|
219
|
+
setSignal(t.I, false);
|
|
220
|
+
t.j(false, false);
|
|
221
221
|
} else {
|
|
222
222
|
setSlotState(t, this, true, false);
|
|
223
223
|
}
|
|
224
224
|
});
|
|
225
225
|
}
|
|
226
226
|
} finally {
|
|
227
|
-
this.
|
|
228
|
-
this.
|
|
229
|
-
this.
|
|
227
|
+
this.H = this.O();
|
|
228
|
+
this.J = this.U();
|
|
229
|
+
this.K = false;
|
|
230
230
|
}
|
|
231
|
-
if (this.
|
|
231
|
+
if (this.P && (r !== this.H || n !== this.J)) this.P.j();
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
234
|
|
|
235
235
|
class CollectionQueue extends Queue {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
236
|
+
$;
|
|
237
|
+
v=new Set;
|
|
238
|
+
ee;
|
|
239
|
+
N=true;
|
|
240
|
+
I=signal(false, {
|
|
241
241
|
ownedWrite: true,
|
|
242
|
-
|
|
242
|
+
V: true
|
|
243
243
|
});
|
|
244
|
-
|
|
245
|
-
|
|
244
|
+
_;
|
|
245
|
+
D=signal(false, {
|
|
246
246
|
ownedWrite: true,
|
|
247
|
-
|
|
247
|
+
V: true
|
|
248
248
|
});
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
249
|
+
B;
|
|
250
|
+
W=false;
|
|
251
|
+
te;
|
|
252
|
+
re=ON_INIT;
|
|
253
253
|
constructor(e) {
|
|
254
254
|
super();
|
|
255
|
-
this
|
|
255
|
+
this.$ = e;
|
|
256
256
|
}
|
|
257
257
|
run(e) {
|
|
258
|
-
if (!e || read(this.
|
|
258
|
+
if (!e || read(this.I) && (!_revealUsed || read(this.D))) return;
|
|
259
259
|
return super.run(e);
|
|
260
260
|
}
|
|
261
261
|
notify(e, t, r, n) {
|
|
262
|
-
if (!(t & this
|
|
263
|
-
if (this.
|
|
262
|
+
if (!(t & this.$)) return super.notify(e, t, r, n);
|
|
263
|
+
if (this.W && this.te) {
|
|
264
264
|
const e = untrack(() => {
|
|
265
265
|
try {
|
|
266
|
-
return this.
|
|
266
|
+
return this.te();
|
|
267
267
|
} catch {
|
|
268
268
|
return ON_INIT;
|
|
269
269
|
}
|
|
270
270
|
});
|
|
271
|
-
if (e !== this.
|
|
272
|
-
this.
|
|
273
|
-
this.
|
|
274
|
-
this.
|
|
271
|
+
if (e !== this.re) {
|
|
272
|
+
this.re = e;
|
|
273
|
+
this.W = false;
|
|
274
|
+
this.v.clear();
|
|
275
275
|
}
|
|
276
276
|
}
|
|
277
277
|
// Routing is dimension-independent: each boundary consumes only its own
|
|
@@ -284,43 +284,47 @@ class CollectionQueue extends Queue {
|
|
|
284
284
|
// dimension, while a status already caught by an inner boundary arrives with
|
|
285
285
|
// its dimension consumed from the mask and is correctly not re-routed
|
|
286
286
|
// (the Loading > Errored > content composition escape, #2856).
|
|
287
|
-
if (this
|
|
288
|
-
if (r & this
|
|
289
|
-
this.
|
|
290
|
-
const t = n?.source || e.
|
|
287
|
+
if (this.$ & STATUS_PENDING && this.W) return super.notify(e, t, r, n);
|
|
288
|
+
if (r & this.$) {
|
|
289
|
+
this.N = true;
|
|
290
|
+
const t = n?.source || e._?.source;
|
|
291
291
|
if (t) {
|
|
292
|
-
const e = this.
|
|
293
|
-
this.
|
|
294
|
-
if (e) setSignal(this.
|
|
295
|
-
if (this
|
|
296
|
-
setSignal(this.
|
|
292
|
+
const e = this.v.size === 0;
|
|
293
|
+
this.v.add(t);
|
|
294
|
+
if (e) setSignal(this.I, true);
|
|
295
|
+
if (this.$ & STATUS_ERROR) {
|
|
296
|
+
setSignal(this._, unwrapStatusError(t._));
|
|
297
297
|
}
|
|
298
298
|
}
|
|
299
299
|
}
|
|
300
|
-
t &= ~this
|
|
300
|
+
t &= ~this.$;
|
|
301
301
|
return t ? super.notify(e, t, r, n) : true;
|
|
302
302
|
}
|
|
303
|
-
|
|
304
|
-
for (const e of this.
|
|
305
|
-
|
|
303
|
+
ne() {
|
|
304
|
+
for (const e of this.v) {
|
|
305
|
+
// A source with a live affects() mark holds display state for the
|
|
306
|
+
// mark's lifetime (the visual channel): the marked node carries no
|
|
307
|
+
// status of its own, so the count is the liveness test. The release
|
|
308
|
+
// sweep (finalizePureQueue after mark release) re-runs this check.
|
|
309
|
+
if (e.se & REACTIVE_DISPOSED || !e.t && !(e.S & this.$) && !(this.$ & STATUS_ERROR && e.S & STATUS_PENDING)) this.v.delete(e);
|
|
306
310
|
}
|
|
307
|
-
if (!this.
|
|
308
|
-
if (this
|
|
309
|
-
this.
|
|
311
|
+
if (!this.v.size) {
|
|
312
|
+
if (this.$ & STATUS_PENDING && this.N && !this.W && this.ee) {
|
|
313
|
+
this.N = !!(this.ee.S & this.$);
|
|
310
314
|
} else {
|
|
311
|
-
this.
|
|
315
|
+
this.N = false;
|
|
312
316
|
}
|
|
313
|
-
if (!this.
|
|
314
|
-
setSignal(this.
|
|
315
|
-
if (this.
|
|
317
|
+
if (!this.N) {
|
|
318
|
+
setSignal(this.I, false);
|
|
319
|
+
if (this.te) {
|
|
316
320
|
try {
|
|
317
|
-
this.
|
|
321
|
+
this.re = untrack(() => this.te());
|
|
318
322
|
} catch {
|
|
319
323
|
/* value not yet committed — _prevOn stays stale, next notify will reset */}
|
|
320
324
|
}
|
|
321
325
|
}
|
|
322
326
|
}
|
|
323
|
-
if (_revealUsed) this.
|
|
327
|
+
if (_revealUsed) this.B?.j();
|
|
324
328
|
}
|
|
325
329
|
}
|
|
326
330
|
|
|
@@ -328,12 +332,12 @@ function createCollectionBoundary(e, t, r, n) {
|
|
|
328
332
|
const s = createOwner();
|
|
329
333
|
if (_revealUsed) setContext(RevealControllerContext, null, s);
|
|
330
334
|
const i = new CollectionQueue(e);
|
|
331
|
-
if (e === STATUS_ERROR) i.
|
|
335
|
+
if (e === STATUS_ERROR) i._ = signal(undefined, {
|
|
332
336
|
ownedWrite: true,
|
|
333
|
-
|
|
337
|
+
V: true
|
|
334
338
|
});
|
|
335
|
-
if (n) i.
|
|
336
|
-
const o = i.
|
|
339
|
+
if (n) i.te = n;
|
|
340
|
+
const o = i.ee = createBoundChildren(s, t, i, e);
|
|
337
341
|
// Prime source tracking so reveal registration sees pending sources.
|
|
338
342
|
untrack(() => {
|
|
339
343
|
let t = false;
|
|
@@ -342,23 +346,23 @@ function createCollectionBoundary(e, t, r, n) {
|
|
|
342
346
|
} catch (e) {
|
|
343
347
|
if (e instanceof NotReadyError) t = true; else throw e;
|
|
344
348
|
}
|
|
345
|
-
i.
|
|
349
|
+
i.N = t || !!(o.S & e) || o._ instanceof NotReadyError;
|
|
346
350
|
});
|
|
347
351
|
const l = _revealUsed && e === STATUS_PENDING ? getContext(RevealControllerContext) : null;
|
|
348
352
|
if (l) {
|
|
349
|
-
i.
|
|
350
|
-
l.
|
|
351
|
-
cleanup(() => l.
|
|
353
|
+
i.B = l;
|
|
354
|
+
l.Y(i);
|
|
355
|
+
cleanup(() => l.Z(i));
|
|
352
356
|
}
|
|
353
357
|
return accessor(computed(() => {
|
|
354
|
-
if (!read(i.
|
|
358
|
+
if (!read(i.I)) {
|
|
355
359
|
const e = read(o);
|
|
356
|
-
if (!untrack(() => read(i.
|
|
360
|
+
if (!untrack(() => read(i.I))) return i.W = true, e;
|
|
357
361
|
}
|
|
358
362
|
// Collapsed reveal slots suppress their own output entirely; the
|
|
359
363
|
// renderer treats the hole as empty, so the cast never leaks to users
|
|
360
364
|
// outside a `createRevealOrder` scope.
|
|
361
|
-
if (_revealUsed && read(i.
|
|
365
|
+
if (_revealUsed && read(i.D)) return undefined;
|
|
362
366
|
return r(i);
|
|
363
367
|
},
|
|
364
368
|
// Boundary structure, not a user source: its value is fallback-or-content and
|
|
@@ -366,7 +370,7 @@ function createCollectionBoundary(e, t, r, n) {
|
|
|
366
370
|
// by snapshot capture. The tree no longer carries foreign status flags, so
|
|
367
371
|
// capture can't rely on PENDING to skip this node the way it used to.
|
|
368
372
|
{
|
|
369
|
-
|
|
373
|
+
V: true
|
|
370
374
|
}));
|
|
371
375
|
}
|
|
372
376
|
|
|
@@ -419,8 +423,8 @@ function createCollectionBoundary(e, t, r, n) {
|
|
|
419
423
|
* }
|
|
420
424
|
* ```
|
|
421
425
|
*/ function createErrorBoundary(e, t) {
|
|
422
|
-
return createCollectionBoundary(STATUS_ERROR, e, e => t(accessor(e.
|
|
423
|
-
for (const t of e.
|
|
426
|
+
return createCollectionBoundary(STATUS_ERROR, e, e => t(accessor(e._), () => {
|
|
427
|
+
for (const t of e.v) recompute(t);
|
|
424
428
|
schedule();
|
|
425
429
|
}));
|
|
426
430
|
}
|
|
@@ -477,12 +481,12 @@ function createCollectionBoundary(e, t, r, n) {
|
|
|
477
481
|
computed(() => {
|
|
478
482
|
s();
|
|
479
483
|
i();
|
|
480
|
-
o.
|
|
484
|
+
o.j();
|
|
481
485
|
});
|
|
482
486
|
if (n) {
|
|
483
|
-
o.
|
|
484
|
-
n.
|
|
485
|
-
cleanup(() => n.
|
|
487
|
+
o.P = n;
|
|
488
|
+
n.Y(o);
|
|
489
|
+
cleanup(() => n.Z(o));
|
|
486
490
|
}
|
|
487
491
|
return t;
|
|
488
492
|
});
|
package/dist/prod/core/action.js
CHANGED
|
@@ -4,14 +4,30 @@ import { isThenable } from "./async.js";
|
|
|
4
4
|
|
|
5
5
|
import "./core.js";
|
|
6
6
|
|
|
7
|
-
function restoreTransition(e,
|
|
7
|
+
function restoreTransition(e, r) {
|
|
8
8
|
globalQueue.initTransition(e);
|
|
9
|
-
const t =
|
|
9
|
+
const t = r();
|
|
10
10
|
flush();
|
|
11
11
|
return t;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
+
* The primitive for mutations: imperative async workflows whose *writes span
|
|
16
|
+
* an async gap* — optimistic write, server round-trip, reconciling write —
|
|
17
|
+
* where intermediate state must not leak and failure must revert cleanly
|
|
18
|
+
* (pair with `createOptimistic` / `createOptimisticStore`).
|
|
19
|
+
*
|
|
20
|
+
* Navigation-shaped updates do not need an action. A plain setter call is
|
|
21
|
+
* enough: reads pull the async, and downstream async computeds hold their
|
|
22
|
+
* previous values per-node until the new ones are ready (`isPending` /
|
|
23
|
+
* `latest` expose the in-flight state). Reach for `action` only when writes
|
|
24
|
+
* happen *after* async work, not merely upstream of it.
|
|
25
|
+
*
|
|
26
|
+
* Framework-level actions (router form actions, server actions) are
|
|
27
|
+
* specializations of this primitive: they are actions in exactly this sense —
|
|
28
|
+
* the same transactional semantics — with form binding, serialization, and
|
|
29
|
+
* submission tracking layered on top. The shared name is deliberate.
|
|
30
|
+
*
|
|
15
31
|
* Wraps a generator function so each invocation runs as a single transaction
|
|
16
32
|
* (a "transition") that batches every signal/store write between yields. The
|
|
17
33
|
* surrounding UI sees one atomic update per yielded step; nothing is committed
|
|
@@ -58,23 +74,23 @@ function restoreTransition(e, n) {
|
|
|
58
74
|
* await addTodo("buy milk");
|
|
59
75
|
* ```
|
|
60
76
|
*/ function action(e) {
|
|
61
|
-
return (...
|
|
62
|
-
const i = e(...
|
|
77
|
+
return (...r) => new Promise((t, n) => {
|
|
78
|
+
const i = e(...r);
|
|
63
79
|
globalQueue.initTransition();
|
|
64
80
|
let o = activeTransition;
|
|
65
|
-
o.
|
|
66
|
-
const done = (e,
|
|
81
|
+
o.ie.push(i);
|
|
82
|
+
const done = (e, r, s = false) => {
|
|
67
83
|
o = currentTransition(o);
|
|
68
|
-
const u = o.
|
|
69
|
-
if (u >= 0) o.
|
|
84
|
+
const u = o.ie.indexOf(i);
|
|
85
|
+
if (u >= 0) o.ie.splice(u, 1);
|
|
70
86
|
setActiveTransition(o);
|
|
71
87
|
schedule();
|
|
72
|
-
s ? r
|
|
88
|
+
s ? n(r) : t(e);
|
|
73
89
|
};
|
|
74
|
-
const step = (e,
|
|
90
|
+
const step = (e, r) => {
|
|
75
91
|
let t;
|
|
76
92
|
try {
|
|
77
|
-
t =
|
|
93
|
+
t = r ? i.throw(e) : i.next(e);
|
|
78
94
|
} catch (e) {
|
|
79
95
|
return done(undefined, e, true);
|
|
80
96
|
}
|
|
@@ -86,7 +102,29 @@ function restoreTransition(e, n) {
|
|
|
86
102
|
};
|
|
87
103
|
const run = e => {
|
|
88
104
|
if (e.done) return done(e.value);
|
|
89
|
-
|
|
105
|
+
// Thenable assimilation can itself throw synchronously (a `then`
|
|
106
|
+
// getter, or a `then()` method that throws — #2918). Match `await`
|
|
107
|
+
// semantics: the failure is thrown back into the generator at the
|
|
108
|
+
// yield point (catchable there); if uncaught, step()'s guard settles
|
|
109
|
+
// the action so its iterator never leaks in the transition. The
|
|
110
|
+
// settled flag implements A+ 2.3.3.3.4.1: a throw after the thenable
|
|
111
|
+
// already called a callback is ignored.
|
|
112
|
+
let r = false;
|
|
113
|
+
try {
|
|
114
|
+
if (isThenable(e.value)) return void e.value.then(e => {
|
|
115
|
+
if (r) return;
|
|
116
|
+
r = true;
|
|
117
|
+
restoreTransition(o, () => step(e));
|
|
118
|
+
}, e => {
|
|
119
|
+
if (r) return;
|
|
120
|
+
r = true;
|
|
121
|
+
restoreTransition(o, () => step(e, true));
|
|
122
|
+
});
|
|
123
|
+
} catch (e) {
|
|
124
|
+
if (r) return;
|
|
125
|
+
r = true;
|
|
126
|
+
return void restoreTransition(o, () => step(e, true));
|
|
127
|
+
}
|
|
90
128
|
restoreTransition(o, () => step(e.value));
|
|
91
129
|
};
|
|
92
130
|
step();
|