@solidjs/signals 2.0.0-rc.18 → 2.0.0-rc.19
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 +168 -88
- package/dist/dev.attribution.js +16 -13
- package/dist/dev.js +272 -129
- package/dist/observe/boundaries.js +88 -67
- package/dist/observe/core/action.js +8 -0
- package/dist/observe/core/async.js +46 -34
- package/dist/observe/core/attribution.js +30 -28
- package/dist/observe/core/constants.js +21 -1
- package/dist/observe/core/core.js +164 -136
- package/dist/observe/core/effect.js +6 -6
- package/dist/observe/core/graph.js +3 -3
- package/dist/observe/core/heap.js +5 -5
- package/dist/observe/core/lanes.js +104 -84
- package/dist/observe/core/owner.js +10 -9
- package/dist/observe/core/scheduler.js +38 -35
- package/dist/observe/core/truth.js +6 -6
- package/dist/observe/core/verdict.js +13 -13
- package/dist/observe/deferred.js +39 -40
- package/dist/observe/index.js +2 -2
- package/dist/observe/map.js +22 -22
- package/dist/observe/reveal.js +5 -5
- package/dist/observe/signals.js +57 -15
- package/dist/observe/store/affects.js +1 -1
- package/dist/observe/store/hooks.js +3 -0
- package/dist/observe/store/optimistic.js +11 -11
- package/dist/observe/store/projection.js +34 -21
- package/dist/observe/store/reconcile.js +2 -2
- package/dist/observe/store/store.js +63 -53
- package/dist/observe/store/utils.js +7 -2
- package/dist/prod/boundaries.js +153 -132
- package/dist/prod/core/action.js +8 -0
- package/dist/prod/core/async.js +55 -43
- package/dist/prod/core/constants.js +14 -1
- package/dist/prod/core/core.js +162 -134
- package/dist/prod/core/effect.js +9 -9
- package/dist/prod/core/graph.js +3 -3
- package/dist/prod/core/heap.js +12 -12
- package/dist/prod/core/lanes.js +98 -78
- package/dist/prod/core/owner.js +15 -14
- package/dist/prod/core/scheduler.js +43 -40
- package/dist/prod/core/truth.js +6 -6
- package/dist/prod/core/verdict.js +17 -17
- package/dist/prod/deferred.js +39 -40
- package/dist/prod/index.js +2 -2
- package/dist/prod/map.js +14 -14
- package/dist/prod/reveal.js +8 -8
- package/dist/prod/signals.js +46 -4
- package/dist/prod/store/affects.js +1 -1
- package/dist/prod/store/hooks.js +3 -0
- package/dist/prod/store/optimistic.js +16 -16
- package/dist/prod/store/projection.js +22 -9
- package/dist/prod/store/reconcile.js +2 -2
- package/dist/prod/store/store.js +262 -262
- package/dist/prod/store/utils.js +2 -0
- package/dist/types/core/action.d.ts +8 -0
- package/dist/types/core/constants.d.ts +20 -0
- package/dist/types/core/core.d.ts +14 -1
- package/dist/types/core/dev.d.ts +1 -1
- package/dist/types/core/index.d.ts +1 -1
- package/dist/types/core/lanes.d.ts +5 -1
- package/dist/types/core/scheduler.d.ts +2 -0
- package/dist/types/index.d.ts +3 -1
- package/dist/types/signals.d.ts +37 -0
- package/dist/types/store/target.d.ts +2 -0
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { STATUS_PENDING, STATUS_ERROR, CONFIG_REDERIVE,
|
|
1
|
+
import { STATUS_PENDING, STATUS_ERROR, CONFIG_REDERIVE, CONFIG_HELD, CONFIG_FRAME, STATUS_UNINITIALIZED, EFFECT_RENDER, CONFIG_VERDICT, REACTIVE_DISPOSED, REACTIVE_LANE_READ, CONFIG_OVERRIDE, EFFECT_USER, EFFECT_TRACKED, REACTIVE_ZOMBIE, CAUGHT_REARMED } from "./core/constants.js";
|
|
2
2
|
|
|
3
3
|
import { signal, runWithOwner, computed, read, setSignal, recompute } from "./core/core.js";
|
|
4
4
|
|
|
@@ -14,7 +14,7 @@ import { enqueueSub } from "./core/heap.js";
|
|
|
14
14
|
|
|
15
15
|
import { createOwner, getOwner, cleanup, dispose } from "./core/owner.js";
|
|
16
16
|
|
|
17
|
-
import { globalQueue, haltReactivityAt, haltReactivity, passLane, joinFuture, GlobalQueue, flushTransaction, heldUnder, setPassLane, passTx, resolveTx,
|
|
17
|
+
import { globalQueue, haltReactivityAt, haltReactivity, schedule, passLane, joinFuture, GlobalQueue, flushTransaction, heldUnder, setPassLane, passTx, resolveTx, txOf, unhold, laneStageHook, installBoundaryHooks } from "./core/scheduler.js";
|
|
18
18
|
|
|
19
19
|
import { attrHooks } from "./core/attribution-hooks.js";
|
|
20
20
|
|
|
@@ -69,6 +69,9 @@ import { accessor } from "./signals.js";
|
|
|
69
69
|
* slot of (reveal.ts); a boundary clears it for its content — only direct
|
|
70
70
|
* children are slots. */ const REVEAL = Symbol("");
|
|
71
71
|
|
|
72
|
+
// Every call site checks the hooks themselves first (not only the slot or
|
|
73
|
+
// controller, which are never set without them): a build that does not
|
|
74
|
+
// import `createRevealOrder` folds the reveal-order arms away.
|
|
72
75
|
let revealHooks = null;
|
|
73
76
|
|
|
74
77
|
function setRevealHooks(e) {
|
|
@@ -98,9 +101,9 @@ const boundaryOf = e => e.A[BOUNDARY];
|
|
|
98
101
|
// the same node.
|
|
99
102
|
n ??= r.T?.F;
|
|
100
103
|
r = n?.source ?? r;
|
|
101
|
-
const
|
|
102
|
-
setSignal(e.F,
|
|
103
|
-
reportClientError(
|
|
104
|
+
const o = unwrapStatusError(n);
|
|
105
|
+
setSignal(e.F, o);
|
|
106
|
+
reportClientError(o, e.H, r);
|
|
104
107
|
}
|
|
105
108
|
record(e, r);
|
|
106
109
|
if (e.h) flip(e);
|
|
@@ -108,7 +111,7 @@ const boundaryOf = e => e.A[BOUNDARY];
|
|
|
108
111
|
// nothing on screen. The fallback is instantiated once per show — a later
|
|
109
112
|
// error reaches it through the `error` accessor, which is why it is one.
|
|
110
113
|
if (!e.L) redraw(e);
|
|
111
|
-
if (e.v !== null) revealHooks.P(e.v);
|
|
114
|
+
if (revealHooks && e.v !== null) revealHooks.P(e.v);
|
|
112
115
|
}
|
|
113
116
|
|
|
114
117
|
/** A re-armed boundary found something pending: fresh again, and the swap
|
|
@@ -120,7 +123,7 @@ const boundaryOf = e => e.A[BOUNDARY];
|
|
|
120
123
|
e.V = e.B;
|
|
121
124
|
const r = e.j;
|
|
122
125
|
if (!e.V && r.l & CONFIG_HELD && txOf(r) !== flushTransaction) unhold(r);
|
|
123
|
-
if (e.V !== null && e.j !== null) laneStageHook(e.j, e.V, true, false);
|
|
126
|
+
if (laneStageHook && e.V !== null && e.j !== null) laneStageHook(e.j, e.V, true, false);
|
|
124
127
|
}
|
|
125
128
|
|
|
126
129
|
function redraw(e) {
|
|
@@ -143,17 +146,17 @@ function redraw(e) {
|
|
|
143
146
|
// on screen changes, the fallback stays as it is.
|
|
144
147
|
if (collecting.size !== 0) for (let r = boundaryOf(e); r; r = r._parent) if (!(e.m & unsettled(r)) && r.U.delete(e) && r.U.size === 0) {
|
|
145
148
|
if (r.L) redraw(r);
|
|
146
|
-
if (r.v !== null) revealHooks.P(r.v);
|
|
149
|
+
if (revealHooks && r.v !== null) revealHooks.P(r.v);
|
|
147
150
|
}
|
|
148
151
|
return false;
|
|
149
152
|
}
|
|
150
|
-
for (let
|
|
151
|
-
if (!(
|
|
152
|
-
if (isCollecting(
|
|
153
|
-
caught(
|
|
154
|
-
return
|
|
153
|
+
for (let o = boundaryOf(e); o; o = o._parent) {
|
|
154
|
+
if (!(o.i & r)) continue;
|
|
155
|
+
if (isCollecting(o)) {
|
|
156
|
+
caught(o, e, n);
|
|
157
|
+
return o.h ? CAUGHT_REARMED : true;
|
|
155
158
|
}
|
|
156
|
-
record(
|
|
159
|
+
record(o, e);
|
|
157
160
|
}
|
|
158
161
|
return false;
|
|
159
162
|
}
|
|
@@ -182,19 +185,19 @@ function redraw(e) {
|
|
|
182
185
|
* still showing its fallback holds it again then. */ function heldRun(e) {
|
|
183
186
|
const r = hiddenBy(e);
|
|
184
187
|
if (r === undefined) return false;
|
|
185
|
-
(r.
|
|
188
|
+
(r.M ??= new Set).add(e);
|
|
186
189
|
return true;
|
|
187
190
|
}
|
|
188
191
|
|
|
189
192
|
/** The reveal: the runs held behind the fallback are this flush's, each in
|
|
190
193
|
* its own phase — render updates with the frame that attaches the content,
|
|
191
194
|
* user effects after it. */ function release(e) {
|
|
192
|
-
if (e.
|
|
193
|
-
for (const r of e.
|
|
195
|
+
if (e.M === null) return;
|
|
196
|
+
for (const r of e.M) {
|
|
194
197
|
const e = r.i;
|
|
195
|
-
globalQueue.enqueue(e === EFFECT_TRACKED ? EFFECT_USER : e, e === EFFECT_TRACKED ? r.
|
|
198
|
+
globalQueue.enqueue(e === EFFECT_TRACKED ? EFFECT_USER : e, e === EFFECT_TRACKED ? r.Z : r.W ??= GlobalQueue.K.bind(null, r));
|
|
196
199
|
}
|
|
197
|
-
e.
|
|
200
|
+
e.M = null;
|
|
198
201
|
}
|
|
199
202
|
|
|
200
203
|
/** Drop the readers that settled or died. Settled: a loading reader no
|
|
@@ -206,9 +209,9 @@ function redraw(e) {
|
|
|
206
209
|
|
|
207
210
|
function prune(e, r) {
|
|
208
211
|
const n = unsettled(e);
|
|
209
|
-
for (const
|
|
210
|
-
if (
|
|
211
|
-
if ((
|
|
212
|
+
for (const o of e.U) {
|
|
213
|
+
if (o.R & REACTIVE_DISPOSED) e.U.delete(o); else if (!(o.m & n)) {
|
|
214
|
+
if ((o !== e.Y || e.h) && (o.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD) {
|
|
212
215
|
// Landed, held: the content is that transaction's — the output's
|
|
213
216
|
// pass enters it and the reveal lands with the reader's run. The
|
|
214
217
|
// seam, past its park decision, waits for the commit instead. (A
|
|
@@ -217,9 +220,9 @@ function prune(e, r) {
|
|
|
217
220
|
// the reader is committed (#3540: what a fresh boundary catches holds
|
|
218
221
|
// nothing outside it).
|
|
219
222
|
if (!r || !e.D) continue;
|
|
220
|
-
joinFuture(txOf(
|
|
223
|
+
joinFuture(txOf(o));
|
|
221
224
|
}
|
|
222
|
-
e.U.delete(
|
|
225
|
+
e.U.delete(o);
|
|
223
226
|
}
|
|
224
227
|
}
|
|
225
228
|
return e.U.size;
|
|
@@ -252,7 +255,7 @@ function prune(e, r) {
|
|
|
252
255
|
if (e.U.size === 0) {
|
|
253
256
|
collecting.delete(e);
|
|
254
257
|
if (e.L) redraw(e);
|
|
255
|
-
if (e.v !== null) revealHooks.P(e.v);
|
|
258
|
+
if (revealHooks && e.v !== null) revealHooks.P(e.v);
|
|
256
259
|
}
|
|
257
260
|
}
|
|
258
261
|
}
|
|
@@ -278,6 +281,8 @@ function prune(e, r) {
|
|
|
278
281
|
return;
|
|
279
282
|
}
|
|
280
283
|
e.h = true;
|
|
284
|
+
// The swap is the change's work: the output joins it (D-13, #3540).
|
|
285
|
+
e.j.l &= ~CONFIG_FRAME;
|
|
281
286
|
// A verdict read in `on` (`isPending`, or a memo of one — #3528's
|
|
282
287
|
// `createMemo(() => isPending(m2))`: the mark carries, core.ts `read`)
|
|
283
288
|
// whose frame has no transaction yet: the swap is still display-ahead — the frame gets one now (it lands
|
|
@@ -313,12 +318,12 @@ function prune(e, r) {
|
|
|
313
318
|
schedule();
|
|
314
319
|
}
|
|
315
320
|
|
|
316
|
-
function createBoundary(e, r, n,
|
|
317
|
-
const
|
|
321
|
+
function createBoundary(e, r, n, o) {
|
|
322
|
+
const t = createOwner();
|
|
318
323
|
const l = {
|
|
319
324
|
i: e,
|
|
320
|
-
_parent: boundaryOf(
|
|
321
|
-
H:
|
|
325
|
+
_parent: boundaryOf(t) ?? null,
|
|
326
|
+
H: t,
|
|
322
327
|
Y: null,
|
|
323
328
|
j: null,
|
|
324
329
|
U: new Set,
|
|
@@ -327,7 +332,7 @@ function createBoundary(e, r, n, t) {
|
|
|
327
332
|
B: null,
|
|
328
333
|
V: null,
|
|
329
334
|
L: false,
|
|
330
|
-
|
|
335
|
+
M: null,
|
|
331
336
|
v: null,
|
|
332
337
|
$: false,
|
|
333
338
|
ee: false,
|
|
@@ -335,21 +340,21 @@ function createBoundary(e, r, n, t) {
|
|
|
335
340
|
re: n,
|
|
336
341
|
ne: null
|
|
337
342
|
};
|
|
338
|
-
const u = e === STATUS_PENDING ?
|
|
343
|
+
const u = e === STATUS_PENDING ? t.A[REVEAL] : null;
|
|
339
344
|
if (getOwner()) cleanup(() => {
|
|
340
345
|
collecting.delete(l);
|
|
341
|
-
if (u) revealHooks.
|
|
346
|
+
if (revealHooks && u) revealHooks.oe(u, l);
|
|
342
347
|
});
|
|
343
348
|
// The `on` dependencies live OUTSIDE the boundary, as the condition of a
|
|
344
349
|
// `<Show>` wrapping it would: created before the owner's context names it.
|
|
345
350
|
// A source of it going pending re-runs it (CONFIG_REDERIVE) rather than
|
|
346
351
|
// marking it: the pass catches the NotReady — it is a notification, it
|
|
347
352
|
// does not suspend the parent — and re-arms.
|
|
348
|
-
if (
|
|
353
|
+
if (o) {
|
|
349
354
|
let e = false;
|
|
350
|
-
runWithOwner(
|
|
355
|
+
runWithOwner(t, () => computed(() => {
|
|
351
356
|
try {
|
|
352
|
-
|
|
357
|
+
o();
|
|
353
358
|
} catch (e) {
|
|
354
359
|
if (!(e instanceof NotReadyError)) {
|
|
355
360
|
if (!globalQueue.notify(getOwner(), STATUS_ERROR, STATUS_ERROR, e)) {
|
|
@@ -363,12 +368,12 @@ function createBoundary(e, r, n, t) {
|
|
|
363
368
|
})).l |= CONFIG_REDERIVE;
|
|
364
369
|
}
|
|
365
370
|
const a = {
|
|
366
|
-
...
|
|
371
|
+
...t.A,
|
|
367
372
|
[BOUNDARY]: l
|
|
368
373
|
};
|
|
369
374
|
if (revealHooks !== null) a[REVEAL] = null;
|
|
370
|
-
|
|
371
|
-
const
|
|
375
|
+
t.A = a;
|
|
376
|
+
const s = runWithOwner(t, () => {
|
|
372
377
|
const e = true ? computed(r, {
|
|
373
378
|
name: "children"
|
|
374
379
|
}) : computed(r);
|
|
@@ -376,23 +381,30 @@ function createBoundary(e, r, n, t) {
|
|
|
376
381
|
name: "boundary"
|
|
377
382
|
}) : computed(() => flatten(read(e)));
|
|
378
383
|
});
|
|
379
|
-
l.Y =
|
|
384
|
+
l.Y = s;
|
|
380
385
|
// A slot of the reveal order in its context (reveal.ts): gated until the
|
|
381
386
|
// controller releases it — before the output's first pass.
|
|
382
|
-
if (u) revealHooks.
|
|
383
|
-
const
|
|
387
|
+
if (revealHooks && u) revealHooks.te(u, l);
|
|
388
|
+
const i = computed(() => {
|
|
389
|
+
// Structure, not a derivation of its content (CONFIG_FRAME, #4283): it
|
|
390
|
+
// reads a hold it is not part of as the screen, as the render effect
|
|
391
|
+
// flattening the same children without a boundary would. Not while a
|
|
392
|
+
// re-arm's swap is the change's work (`arm`), until that swap commits
|
|
393
|
+
// (D-13, #3540).
|
|
394
|
+
const r = getOwner();
|
|
395
|
+
if (!l.h && !(r.l & CONFIG_HELD)) r.l |= CONFIG_FRAME;
|
|
384
396
|
// Reveal order (reveal.ts): a gated slot shows its fallback — or, in a
|
|
385
397
|
// collapsed tail, nothing — whatever its content's state; the
|
|
386
398
|
// controller is told the pass ran (its readiness may have changed) and
|
|
387
399
|
// releases it in turn.
|
|
388
|
-
|
|
400
|
+
if (l.$) {
|
|
389
401
|
// Linked without being read: the tree settling (or changing)
|
|
390
402
|
// re-derives this pass, which is how the controller hears of it.
|
|
391
403
|
// It may release this very slot (the frontier whose content just
|
|
392
404
|
// settled): the pass goes on to show it — the redraw it asks for is
|
|
393
405
|
// refused mid-pass and not needed.
|
|
394
|
-
link(
|
|
395
|
-
if (l.v !== null) revealHooks.P(l.v);
|
|
406
|
+
link(s, r);
|
|
407
|
+
if (revealHooks && l.v !== null) revealHooks.P(l.v);
|
|
396
408
|
if (l.$) {
|
|
397
409
|
if (l.ee) {
|
|
398
410
|
l.L = true;
|
|
@@ -408,21 +420,21 @@ function createBoundary(e, r, n, t) {
|
|
|
408
420
|
// display-ahead `on` causes is the lane's, and must still be the
|
|
409
421
|
// fallback. Linked, so its recovery re-derives this pass; read again
|
|
410
422
|
// once clean.
|
|
411
|
-
const
|
|
412
|
-
if (
|
|
413
|
-
link(
|
|
414
|
-
if (
|
|
423
|
+
const n = s.m & (STATUS_PENDING | STATUS_ERROR);
|
|
424
|
+
if (n !== 0) {
|
|
425
|
+
link(s, r);
|
|
426
|
+
if (n & e) {
|
|
415
427
|
if (isCollecting(l)) {
|
|
416
|
-
caught(l,
|
|
428
|
+
caught(l, s, undefined);
|
|
417
429
|
return fallback(l);
|
|
418
430
|
}
|
|
419
431
|
// Forwarded: a loading boundary showing content keeps it and
|
|
420
432
|
// holds the frame like any reader (recorded for its `on`).
|
|
421
|
-
record(l,
|
|
433
|
+
record(l, s);
|
|
422
434
|
}
|
|
423
435
|
// A pending passes an error boundary by (A6); an error, a loading
|
|
424
436
|
// one; a loading boundary showing content forwards its pending.
|
|
425
|
-
throw
|
|
437
|
+
throw s.T.F;
|
|
426
438
|
}
|
|
427
439
|
if (isCollecting(l)) {
|
|
428
440
|
// A29's boundary exemption (#3540): a boundary MOUNTED over a held
|
|
@@ -436,24 +448,23 @@ function createBoundary(e, r, n, t) {
|
|
|
436
448
|
// enters: the outside sees its committed value until the landing,
|
|
437
449
|
// which reveals the content — a fallback staged earlier is replaced
|
|
438
450
|
// ahead of the commit and never shown.
|
|
439
|
-
|
|
440
|
-
if (l.U.size !== 0 && e.m & STATUS_UNINITIALIZED && !(e.l & CONFIG_HELD)) return fallback(l);
|
|
451
|
+
if (l.U.size !== 0 && r.m & STATUS_UNINITIALIZED && !(r.l & CONFIG_HELD)) return fallback(l);
|
|
441
452
|
// Readers under it still unready: the fallback, the tree untouched.
|
|
442
453
|
// The seam re-derives this pass when they settle.
|
|
443
454
|
if (prune(l, true) !== 0) return fallback(l);
|
|
444
455
|
}
|
|
445
|
-
let
|
|
456
|
+
let o;
|
|
446
457
|
try {
|
|
447
|
-
|
|
458
|
+
o = read(s);
|
|
448
459
|
} catch (r) {
|
|
449
460
|
if (r instanceof NotReadyError ? e === STATUS_PENDING : e === STATUS_ERROR) {
|
|
450
461
|
if (isCollecting(l)) {
|
|
451
|
-
caught(l,
|
|
462
|
+
caught(l, s, r);
|
|
452
463
|
return fallback(l);
|
|
453
464
|
}
|
|
454
465
|
// Forwarded: a loading boundary showing content keeps it and
|
|
455
466
|
// holds the frame like any reader (recorded for its `on`).
|
|
456
|
-
record(l,
|
|
467
|
+
record(l, s);
|
|
457
468
|
}
|
|
458
469
|
// A pending passes an error boundary by (A6).
|
|
459
470
|
throw r;
|
|
@@ -471,34 +482,44 @@ function createBoundary(e, r, n, t) {
|
|
|
471
482
|
// landing when the frame parks (the run is held with it, as the old
|
|
472
483
|
// children of a held pass are) — not now: a held swap keeps the
|
|
473
484
|
// fallback on screen, live, until it commits. A pass back to the
|
|
474
|
-
// fallback before then finds it still built and keeps it.
|
|
485
|
+
// fallback before then finds it still built and keeps it. A fallback
|
|
486
|
+
// the screen does not show (a swap staged with a held frame, replaced
|
|
487
|
+
// before it commits) dies now: what it reads holds nothing (S-22 row
|
|
488
|
+
// 3′, #4268).
|
|
475
489
|
const t = l.ne;
|
|
476
|
-
|
|
490
|
+
const drop = () => {
|
|
477
491
|
if (!l.L && l.ne === t) {
|
|
492
|
+
// A fallback that threw (a rethrow the boundary does not
|
|
493
|
+
// handle) is what an enclosing boundary waits on: it died, and
|
|
494
|
+
// the seam of another round drops it there (#4270).
|
|
495
|
+
if (t.m & STATUS_ERROR) schedule();
|
|
478
496
|
dispose(t);
|
|
479
497
|
l.ne = null;
|
|
480
498
|
}
|
|
481
|
-
}
|
|
499
|
+
};
|
|
500
|
+
if (t !== null) l.j.le !== t.le ? drop() : globalQueue.enqueue(EFFECT_RENDER, drop);
|
|
482
501
|
release(l);
|
|
483
502
|
// Shown: the slot leaves its reveal order (a later re-arm gates nobody
|
|
484
503
|
// else; the frontier moves on).
|
|
485
|
-
if (l.v !== null) {
|
|
504
|
+
if (revealHooks && l.v !== null) {
|
|
486
505
|
const e = l.v;
|
|
487
506
|
l.v = null;
|
|
488
507
|
revealHooks.P(e);
|
|
489
508
|
}
|
|
490
|
-
return
|
|
509
|
+
return o;
|
|
491
510
|
},
|
|
492
511
|
// Boundary structure, not a user source: its value is fallback-or-content
|
|
493
512
|
// and legitimately swaps mid-hydration (resume), so it must never be
|
|
494
513
|
// frozen by snapshot capture.
|
|
495
514
|
{
|
|
496
515
|
name: "value",
|
|
497
|
-
|
|
516
|
+
ue: true
|
|
498
517
|
});
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
518
|
+
i.l |= CONFIG_REDERIVE;
|
|
519
|
+
// The tree is structure too (the output's pass marks the output).
|
|
520
|
+
s.l |= CONFIG_FRAME;
|
|
521
|
+
l.j = i;
|
|
522
|
+
return accessor(i);
|
|
502
523
|
}
|
|
503
524
|
|
|
504
525
|
function fallback(e) {
|
|
@@ -541,7 +562,7 @@ function fallback(e) {
|
|
|
541
562
|
|
|
542
563
|
const ERROR_SIGNAL = {
|
|
543
564
|
ownedWrite: true,
|
|
544
|
-
|
|
565
|
+
ue: true
|
|
545
566
|
};
|
|
546
567
|
|
|
547
568
|
/**
|
|
@@ -70,6 +70,14 @@ import { attrHooks } from "./attribution-hooks.js";
|
|
|
70
70
|
* yield until(() => todos.some(t => t.id === saved.id));
|
|
71
71
|
* ```
|
|
72
72
|
*
|
|
73
|
+
* The same holds inside an async helper the action yields
|
|
74
|
+
* (`yield (async () => { ... })()`): everything after its first `await` runs
|
|
75
|
+
* outside the transaction. An `await refresh(x)` or `await until(...)` there
|
|
76
|
+
* waits for this action's commit, which waits on the helper, so it hangs
|
|
77
|
+
* whenever `x` re-ran during the action (#4263) and works otherwise. Call
|
|
78
|
+
* `refresh(x)` in the generator once the helper returns; `yield` it only when
|
|
79
|
+
* the action needs the fresh value.
|
|
80
|
+
*
|
|
73
81
|
* (For the same reason, don't call `flush()` inside an action body — it
|
|
74
82
|
* cannot reveal the held writes and is refused.)
|
|
75
83
|
*
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { STATUS_ERROR, STATUS_PENDING, STATUS_UNINITIALIZED, CONFIG_GUESS, REACTIVE_RECOMPUTING_DEPS, CONFIG_HELD, REACTIVE_STAGED_READ, CONFIG_STAGED, CONFIG_STAGED_ERROR, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE, NewSet, REACTIVE_DIRTY, NOT_PENDING, REACTIVE_DISPOSED, CONFIG_REDERIVE, CONFIG_VERDICT, CONFIG_OVERRIDE } from "./constants.js";
|
|
1
|
+
import { STATUS_ERROR, STATUS_PENDING, STATUS_NO_VALUE, STATUS_UNINITIALIZED, CONFIG_GUESS, REACTIVE_RECOMPUTING_DEPS, CONFIG_HELD, REACTIVE_STAGED_READ, CONFIG_STAGED, CONFIG_STAGED_ERROR, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE, NewSet, REACTIVE_DIRTY, CONFIG_INPUTS_PUBLISHED, NOT_PENDING, REACTIVE_DISPOSED, CONFIG_REDERIVE, CONFIG_VERDICT, CONFIG_OVERRIDE } from "./constants.js";
|
|
2
2
|
|
|
3
3
|
import { attrHooks } from "./attribution-hooks.js";
|
|
4
4
|
|
|
@@ -156,7 +156,7 @@ function releaseSettledDependents(e) {
|
|
|
156
156
|
clearPendingSources(e);
|
|
157
157
|
if (e.T) e.T.fe = false;
|
|
158
158
|
if (!((e.m &= STATUS_ERROR | STATUS_UNINITIALIZED) & STATUS_ERROR)) setPendingError(e);
|
|
159
|
-
e.
|
|
159
|
+
e.ie = t instanceof StatusError ? t : new StatusError(e, t);
|
|
160
160
|
e.l |= CONFIG_STAGED | CONFIG_STAGED_ERROR;
|
|
161
161
|
// Readers parked on the flight re-run and derive from the staging; the
|
|
162
162
|
// verdict readers the walk wakes need a flush (#4106).
|
|
@@ -208,7 +208,7 @@ function settlePendingSource(e, t = e) {
|
|
|
208
208
|
// is its verdict changing: it runs again.
|
|
209
209
|
return s.l & CONFIG_VERDICT && enqueueSub(s);
|
|
210
210
|
o.add(s);
|
|
211
|
-
s.
|
|
211
|
+
s.ae = clock;
|
|
212
212
|
const f = s.T?.se?.values().next().value;
|
|
213
213
|
// STATUS_ERROR + pending sources only coexist via an errored loading
|
|
214
214
|
// window's park (notifyStatus(STATUS_ERROR) clears pending sources
|
|
@@ -251,9 +251,9 @@ function isThenable(e) {
|
|
|
251
251
|
}
|
|
252
252
|
|
|
253
253
|
/** Fire and clear a node's iterator-flight cancellation hook (#3122). */ function releaseFlightTeardown(e) {
|
|
254
|
-
const t = e.T?.
|
|
254
|
+
const t = e.T?.Se;
|
|
255
255
|
if (t != null) {
|
|
256
|
-
e.T.
|
|
256
|
+
e.T.Se = null;
|
|
257
257
|
t();
|
|
258
258
|
}
|
|
259
259
|
}
|
|
@@ -275,9 +275,9 @@ function isThenable(e) {
|
|
|
275
275
|
// null, which recompute would read as a flight this body registered itself.
|
|
276
276
|
// Restore undefined here, so the fix costs nothing for code that never
|
|
277
277
|
// calls onSupersede.
|
|
278
|
-
if (n) r.
|
|
279
|
-
const o = r.
|
|
280
|
-
r.
|
|
278
|
+
if (n) r.Ee = undefined;
|
|
279
|
+
const o = r.Se;
|
|
280
|
+
r.Se = o === null ? e : () => {
|
|
281
281
|
try {
|
|
282
282
|
o();
|
|
283
283
|
} finally {
|
|
@@ -301,7 +301,7 @@ r) {
|
|
|
301
301
|
});
|
|
302
302
|
}
|
|
303
303
|
if (!s && !o) {
|
|
304
|
-
if (e.T !== null) e.T.
|
|
304
|
+
if (e.T !== null) e.T.Ee = null;
|
|
305
305
|
// A sync landing is the first real answer for a loadingValue node.
|
|
306
306
|
e.ce = false;
|
|
307
307
|
return t;
|
|
@@ -311,10 +311,10 @@ r) {
|
|
|
311
311
|
// self-registration — runs during a recompute of `el`, which has already
|
|
312
312
|
// fired _flightTeardown. A future non-recompute registration path must
|
|
313
313
|
// release it here before overwriting _inFlight.
|
|
314
|
-
ext(e).
|
|
314
|
+
ext(e).Ee = t;
|
|
315
315
|
// The question this flight answers (A18 provenance, lanes.ts): the action
|
|
316
316
|
// whose slice asked it; mainline is always the current question.
|
|
317
|
-
e.T.
|
|
317
|
+
e.T.de = question || MAINLINE_QUESTION;
|
|
318
318
|
// The run that asked this flight read every input without throwing: an
|
|
319
319
|
// input still in flight was masked for it (an active override, A17), so
|
|
320
320
|
// pending state those inputs propagated onto the node earlier does not
|
|
@@ -330,7 +330,7 @@ r) {
|
|
|
330
330
|
if (attrHooks !== null) attrHooks.flightStart(e, t);
|
|
331
331
|
let f;
|
|
332
332
|
const handleError = o => {
|
|
333
|
-
if (e.T?.
|
|
333
|
+
if (e.T?.Ee !== t) return;
|
|
334
334
|
// NotReadyError from rejected promises should be treated as pending, not error
|
|
335
335
|
let s = o instanceof NotReadyError;
|
|
336
336
|
if (s && e.ce) {
|
|
@@ -338,9 +338,9 @@ r) {
|
|
|
338
338
|
// serving commit #0 — same parking as recompute's catch for sync
|
|
339
339
|
// dependency throws. The dead flight is released so the clock-gated
|
|
340
340
|
// error-retry pull (updateIfNecessary) can also re-ask.
|
|
341
|
-
if (e.T !== null) e.T.
|
|
341
|
+
if (e.T !== null) e.T.Ee = null;
|
|
342
342
|
parkLoadingWindow(e, o);
|
|
343
|
-
e.
|
|
343
|
+
e.ae = clock;
|
|
344
344
|
return;
|
|
345
345
|
}
|
|
346
346
|
// D-41: a held node's error is staged (`stageError`). A derived store's
|
|
@@ -358,7 +358,7 @@ r) {
|
|
|
358
358
|
// rejected flight will never settle its self entry, so transfer ownership
|
|
359
359
|
// after notifyStatus has propagated the replacement source.
|
|
360
360
|
if (s) settlePendingSource(e);
|
|
361
|
-
e.
|
|
361
|
+
e.ae = clock;
|
|
362
362
|
// A real error settles derivatively-pending dependents (notifyStatus
|
|
363
363
|
// cleared their pending sources), so stranded lazy ones release here —
|
|
364
364
|
// the error twin of settlePendingSource's release (#2934).
|
|
@@ -366,7 +366,7 @@ r) {
|
|
|
366
366
|
r?.(o, s);
|
|
367
367
|
};
|
|
368
368
|
const asyncWrite = (r, o) => {
|
|
369
|
-
if (e.T?.
|
|
369
|
+
if (e.T?.Ee !== t) return;
|
|
370
370
|
// If the node was dirtied by a newer write (optimistic override or regular),
|
|
371
371
|
// skip this stale async result — the upcoming flush will recompute the node
|
|
372
372
|
// with the new value, creating a fresh Promise that supersedes this one.
|
|
@@ -385,8 +385,14 @@ r) {
|
|
|
385
385
|
return;
|
|
386
386
|
}
|
|
387
387
|
// A store derive's landing commits through its setter: unheld, the
|
|
388
|
-
// shown error leaves now
|
|
389
|
-
|
|
388
|
+
// shown error leaves now, and so does a commit-beneath-a-flight's
|
|
389
|
+
// CONFIG_INPUTS_PUBLISHED (its first flight's, #4269) — left standing,
|
|
390
|
+
// a verdict reader of the next flight would suspend instead of being
|
|
391
|
+
// served the screen; held, both at the derive's commit.
|
|
392
|
+
if (!(e.l & CONFIG_HELD)) {
|
|
393
|
+
e.T.Te = undefined;
|
|
394
|
+
e.l &= ~CONFIG_INPUTS_PUBLISHED;
|
|
395
|
+
}
|
|
390
396
|
if (s) landStatus(e, true);
|
|
391
397
|
// Attribution hook: this path landed through the setter (derived
|
|
392
398
|
// stores, projections), which does not go through setSignal, so the
|
|
@@ -416,7 +422,7 @@ r) {
|
|
|
416
422
|
// (`_pendingValue` set inside setSignal) is not — commitPendingNode closes
|
|
417
423
|
// the window when the flush commits it, so no one-frame pulse can leak to
|
|
418
424
|
// observers between the landing and its commit (#2990).
|
|
419
|
-
if (e.
|
|
425
|
+
if (e.ie === NOT_PENDING) {
|
|
420
426
|
e.ce = false;
|
|
421
427
|
// The landing published: the dependency tail the flight's pass left
|
|
422
428
|
// linked goes now (A30, #3410). A staged landing has not replaced the
|
|
@@ -473,7 +479,7 @@ r) {
|
|
|
473
479
|
// `onSupersede` teardown this run registered earlier, which then falls
|
|
474
480
|
// back to the owner-cleanup channel. A returned `from()` is unaffected:
|
|
475
481
|
// `close` calls its `return()`, which runs its own teardown.
|
|
476
|
-
ext(e).
|
|
482
|
+
ext(e).Se = close;
|
|
477
483
|
// Release check before each next pull: an unobserved lazy node must tear
|
|
478
484
|
// down (its close above runs via disposal, closing the iterator) instead
|
|
479
485
|
// of pumping the stream forever with zero subscribers (#2935).
|
|
@@ -494,11 +500,11 @@ r) {
|
|
|
494
500
|
// use the bare form as a promise-free fast path when a value is already
|
|
495
501
|
// buffered (seroval's deserialized streams do), so a bare result is
|
|
496
502
|
// assimilated as an already-settled step instead of crashing on `.then`.
|
|
497
|
-
const
|
|
498
|
-
const
|
|
499
|
-
then: e => void e(
|
|
503
|
+
const E = o.next();
|
|
504
|
+
const c = isThenable(E) ? E : {
|
|
505
|
+
then: e => void e(E)
|
|
500
506
|
};
|
|
501
|
-
|
|
507
|
+
c.then(r => {
|
|
502
508
|
// The sync stash only serves the INITIAL drain (handleAsync's caller
|
|
503
509
|
// consumes syncValue / throws NotReady from it). A sync-settled step
|
|
504
510
|
// after an async gap — seroval buffering values between pulls, a
|
|
@@ -510,7 +516,7 @@ r) {
|
|
|
510
516
|
n = r;
|
|
511
517
|
l = true;
|
|
512
518
|
if (r.done) u = true;
|
|
513
|
-
} else if (e.T?.
|
|
519
|
+
} else if (e.T?.Ee !== t) {
|
|
514
520
|
return;
|
|
515
521
|
} else if (!r.done) {
|
|
516
522
|
s = true;
|
|
@@ -530,7 +536,7 @@ r) {
|
|
|
530
536
|
if (S && i) {
|
|
531
537
|
r = n;
|
|
532
538
|
a = true;
|
|
533
|
-
} else if (e.T?.
|
|
539
|
+
} else if (e.T?.Ee === t) {
|
|
534
540
|
u = true;
|
|
535
541
|
handleError(n);
|
|
536
542
|
settleAutodispose();
|
|
@@ -595,7 +601,7 @@ r) {
|
|
|
595
601
|
// compute catch above it: a protocol throw settles as this
|
|
596
602
|
// flight's error (#4112).
|
|
597
603
|
try {
|
|
598
|
-
if (e.T?.
|
|
604
|
+
if (e.T?.Ee === t && !(e.R & REACTIVE_DISPOSED) && flattenIfIterable(r, e)) return;
|
|
599
605
|
asyncWrite(r);
|
|
600
606
|
} catch (e) {
|
|
601
607
|
handleError(e);
|
|
@@ -637,7 +643,7 @@ r) {
|
|
|
637
643
|
if (u !== null) {
|
|
638
644
|
if (!u) {
|
|
639
645
|
// Loading window: serve commit #0 instead of suspending.
|
|
640
|
-
if (e.ce) return e.
|
|
646
|
+
if (e.ce) return e.le;
|
|
641
647
|
throw new NotReadyError(context);
|
|
642
648
|
}
|
|
643
649
|
// A sync first yield (or immediate empty completion) is the first real
|
|
@@ -697,15 +703,18 @@ function notifyStatus(e, t, n) {
|
|
|
697
703
|
// (CARVE 2: the lane assignment went with the engine.)
|
|
698
704
|
if (t === STATUS_PENDING && r) {
|
|
699
705
|
// D-41: a re-ask of a shown error keeps it the display (`_shownError`).
|
|
700
|
-
if (e.m & STATUS_ERROR) e.T.
|
|
706
|
+
if (e.m & STATUS_ERROR) e.T.Te = e.T.F;
|
|
701
707
|
addPendingSource(e, r);
|
|
702
|
-
|
|
708
|
+
// A re-ask of an error that answered the first read: still no value.
|
|
709
|
+
e.m = STATUS_PENDING | (e.m & STATUS_NO_VALUE ? STATUS_UNINITIALIZED : e.m & STATUS_UNINITIALIZED);
|
|
703
710
|
// Preserve the current source on this propagation so readers park on
|
|
704
711
|
// the distinct pending source.
|
|
705
712
|
setPendingError(e, r, n);
|
|
706
713
|
} else {
|
|
707
714
|
clearPendingSources(e);
|
|
708
|
-
|
|
715
|
+
// An error answering a node with no value marks it so; a re-ask of it
|
|
716
|
+
// (pending) is uninitialized again; a write (STATUS_NONE) gives it one.
|
|
717
|
+
e.m = t | (t === STATUS_ERROR ? e.m & (STATUS_UNINITIALIZED | STATUS_NO_VALUE) ? STATUS_NO_VALUE : 0 : t === STATUS_PENDING && e.m & STATUS_NO_VALUE ? STATUS_UNINITIALIZED : e.m & STATUS_UNINITIALIZED);
|
|
709
718
|
ext(e).F = n;
|
|
710
719
|
}
|
|
711
720
|
// Lanes: a written guess is an optimistic boundary (A17 — "the optimistic
|
|
@@ -733,7 +742,7 @@ function notifyStatus(e, t, n) {
|
|
|
733
742
|
* (store/projection.ts `wakeFamily`), exactly as a memo's would reach its. */ function propagateStatus(e, t, n) {
|
|
734
743
|
const r = t === STATUS_PENDING && n instanceof NotReadyError ? n.source : undefined;
|
|
735
744
|
forEachDependent(e, (o, s) => {
|
|
736
|
-
o.
|
|
745
|
+
o.ae = clock;
|
|
737
746
|
// A pending mark on a kept-tail link re-derives the subscriber instead of
|
|
738
747
|
// marking it (A30, #3494 review; fuzzer latest-1 #2141; #3519 review).
|
|
739
748
|
// Past `_depsTail` lie the committed frame's deps, kept by A30 because
|
|
@@ -753,7 +762,7 @@ function notifyStatus(e, t, n) {
|
|
|
753
762
|
// Inheriting the error would make a probe's reader errored. A boundary's
|
|
754
763
|
// output (CONFIG_REDERIVE, boundaries.ts) the same: content or fallback
|
|
755
764
|
// is its pass's call.
|
|
756
|
-
if (o.l & (CONFIG_REDERIVE | CONFIG_VERDICT) || t === STATUS_PENDING && s.
|
|
765
|
+
if (o.l & (CONFIG_REDERIVE | CONFIG_VERDICT) || t === STATUS_PENDING && s.Ie !== o.Ne) {
|
|
757
766
|
enqueueSub(o);
|
|
758
767
|
schedule();
|
|
759
768
|
return;
|
|
@@ -766,7 +775,7 @@ function notifyStatus(e, t, n) {
|
|
|
766
775
|
// shape: the unmount it stages makes the observer a zombie whose say is
|
|
767
776
|
// moot for this transaction, A15 #3463).
|
|
768
777
|
if (t === STATUS_PENDING && r && o.T?.se?.has(r)) {
|
|
769
|
-
if (passLane === null && (o.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && globalQueue.q && !freshHook?.(o, r.
|
|
778
|
+
if (passLane === null && (o.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && globalQueue.q && !freshHook?.(o, r.ae === clock)) joinFuture(txOf(o));
|
|
770
779
|
return;
|
|
771
780
|
}
|
|
772
781
|
if (t === STATUS_PENDING && r || t !== STATUS_PENDING && (o.T?.F !== n || o.T?.se)) {
|
|
@@ -785,6 +794,9 @@ function notifyStatus(e, t, n) {
|
|
|
785
794
|
if (t === STATUS_PENDING && (o.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && !o.i) joinFuture(txOf(o));
|
|
786
795
|
}
|
|
787
796
|
notifyStatus(o, t, n);
|
|
797
|
+
// A derived store's derive (store/projection.ts): marked, not re-run —
|
|
798
|
+
// its leaves' readers are not its dependents, so the family walks them.
|
|
799
|
+
o.fam?.st(t);
|
|
788
800
|
}
|
|
789
801
|
});
|
|
790
802
|
}
|