@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
package/dist/prod/map.js
CHANGED
|
@@ -21,7 +21,7 @@ function mapArray(t, e, i) {
|
|
|
21
21
|
const n = e.length > 1;
|
|
22
22
|
const r = e;
|
|
23
23
|
const o = {
|
|
24
|
-
|
|
24
|
+
H: createOwner(),
|
|
25
25
|
it: 0,
|
|
26
26
|
st: t,
|
|
27
27
|
ot: [],
|
|
@@ -32,7 +32,7 @@ function mapArray(t, e, i) {
|
|
|
32
32
|
ct: s || i?.keyed === false ? [] : undefined,
|
|
33
33
|
lt: n && i?.keyed !== false ? [] : undefined,
|
|
34
34
|
ut: i?.keyed === false,
|
|
35
|
-
|
|
35
|
+
L: i?.fallback
|
|
36
36
|
};
|
|
37
37
|
const f = computed(updateKeyedMap.bind(o), undefined);
|
|
38
38
|
f.l &= ~CONFIG_AUTO_DISPOSE;
|
|
@@ -263,11 +263,11 @@ function updateKeyedMap() {
|
|
|
263
263
|
// the flight's reader (pending tracking + post-settle retry) instead of
|
|
264
264
|
// vanishing. Set per pass: the first pass runs inside `computed()`, before
|
|
265
265
|
// the node exists to the caller.
|
|
266
|
-
this.
|
|
266
|
+
this.H.Ye = getOwner();
|
|
267
267
|
const t = this.st() || [], e = t.length;
|
|
268
268
|
t[$TRACK];
|
|
269
269
|
// top level tracking (a store array's container node)
|
|
270
|
-
runWithOwner(this.
|
|
270
|
+
runWithOwner(this.H, () => {
|
|
271
271
|
let i, s,
|
|
272
272
|
// The stage slot a mapper writes its row/index signal into: `j` on the
|
|
273
273
|
// create-all path, `j - start` on the general path, whose stage arrays
|
|
@@ -293,7 +293,7 @@ function updateKeyedMap() {
|
|
|
293
293
|
// fast path for empty arrays
|
|
294
294
|
if (e === 0) {
|
|
295
295
|
if (this.it !== 0) {
|
|
296
|
-
this.
|
|
296
|
+
this.H.dispose(false);
|
|
297
297
|
this.tn = [];
|
|
298
298
|
this.ot = [];
|
|
299
299
|
this.ht = [];
|
|
@@ -301,11 +301,11 @@ function updateKeyedMap() {
|
|
|
301
301
|
this.ct && (this.ct = []);
|
|
302
302
|
this.lt && (this.lt = []);
|
|
303
303
|
}
|
|
304
|
-
if (this.
|
|
304
|
+
if (this.L && !this.ht.length) {
|
|
305
305
|
// an aborted fallback attempt leaves an owner without a mapping;
|
|
306
306
|
// dispose it before re-creating
|
|
307
307
|
this.tn[0]?.dispose();
|
|
308
|
-
this.ht[0] = runWithOwner(this.tn[0] = createOwner(), this.
|
|
308
|
+
this.ht[0] = runWithOwner(this.tn[0] = createOwner(), this.L);
|
|
309
309
|
}
|
|
310
310
|
}
|
|
311
311
|
// fast path for new create
|
|
@@ -502,7 +502,7 @@ function updateKeyedMap() {
|
|
|
502
502
|
*/ function repeat(t, e, i) {
|
|
503
503
|
const s = e;
|
|
504
504
|
const n = {
|
|
505
|
-
|
|
505
|
+
H: createOwner(),
|
|
506
506
|
it: 0,
|
|
507
507
|
dt: 0,
|
|
508
508
|
wt: t,
|
|
@@ -510,7 +510,7 @@ function updateKeyedMap() {
|
|
|
510
510
|
tn: [],
|
|
511
511
|
ht: [],
|
|
512
512
|
Ot: i?.from,
|
|
513
|
-
|
|
513
|
+
L: i?.fallback
|
|
514
514
|
};
|
|
515
515
|
const r = computed(updateRepeat.bind(n));
|
|
516
516
|
r.l &= ~CONFIG_AUTO_DISPOSE;
|
|
@@ -526,24 +526,24 @@ function updateKeyedMap() {
|
|
|
526
526
|
// disjoint-window/front-clear/end-clear/shift special cases.
|
|
527
527
|
function updateRepeat() {
|
|
528
528
|
// As in updateKeyedMap: the node is the row callbacks' reader, per pass.
|
|
529
|
-
this.
|
|
529
|
+
this.H.Ye = getOwner();
|
|
530
530
|
const t = this.wt();
|
|
531
531
|
const e = this.Ot?.() || 0;
|
|
532
|
-
runWithOwner(this.
|
|
532
|
+
runWithOwner(this.H, () => {
|
|
533
533
|
if (t === 0) {
|
|
534
534
|
if (this.it !== 0) {
|
|
535
|
-
this.
|
|
535
|
+
this.H.dispose(false);
|
|
536
536
|
this.tn = [];
|
|
537
537
|
this.ht = [];
|
|
538
538
|
this.it = 0;
|
|
539
539
|
// Reset offset to match the cleared data (#2767, repro 2).
|
|
540
540
|
this.dt = 0;
|
|
541
541
|
}
|
|
542
|
-
if (this.
|
|
542
|
+
if (this.L && !this.ht.length) {
|
|
543
543
|
// an aborted fallback attempt leaves an owner without a mapping;
|
|
544
544
|
// dispose it before re-creating
|
|
545
545
|
this.tn[0]?.dispose();
|
|
546
|
-
this.ht[0] = runWithOwner(this.tn[0] = createOwner(), this.
|
|
546
|
+
this.ht[0] = runWithOwner(this.tn[0] = createOwner(), this.L);
|
|
547
547
|
}
|
|
548
548
|
return;
|
|
549
549
|
}
|
package/dist/prod/reveal.js
CHANGED
|
@@ -76,7 +76,7 @@ class RevealController {
|
|
|
76
76
|
/** A slot that has left this controller's ownership: a boundary that has
|
|
77
77
|
* shown content, a nested group that is ready. Shown is shown — it counts
|
|
78
78
|
* as ready whatever happens to it later (a re-arm gates nobody else). */ gt(t) {
|
|
79
|
-
return (isController(t) ? t._parent : t.
|
|
79
|
+
return (isController(t) ? t._parent : t.v) !== this;
|
|
80
80
|
}
|
|
81
81
|
/** The slots this controller still owns, in registration order — the
|
|
82
82
|
* ones the order's state is forced on. */ Lt(t) {
|
|
@@ -106,7 +106,7 @@ class RevealController {
|
|
|
106
106
|
}
|
|
107
107
|
return t !== "natural";
|
|
108
108
|
}
|
|
109
|
-
|
|
109
|
+
te(t) {
|
|
110
110
|
if (this.At.includes(t)) return;
|
|
111
111
|
this.At.push(t);
|
|
112
112
|
// Gated from the start (collapsed in a collapsed sequential tail); the
|
|
@@ -118,13 +118,13 @@ class RevealController {
|
|
|
118
118
|
t.ee = e;
|
|
119
119
|
t._t();
|
|
120
120
|
} else {
|
|
121
|
-
t.
|
|
121
|
+
t.v = this;
|
|
122
122
|
t.$ = true;
|
|
123
123
|
t.ee = e;
|
|
124
124
|
}
|
|
125
125
|
untrack(() => this._t());
|
|
126
126
|
}
|
|
127
|
-
|
|
127
|
+
oe(t) {
|
|
128
128
|
const e = this.At.indexOf(t);
|
|
129
129
|
if (e >= 0) this.At.splice(e, 1);
|
|
130
130
|
untrack(() => this._t());
|
|
@@ -194,8 +194,8 @@ class RevealController {
|
|
|
194
194
|
});
|
|
195
195
|
if (false) ;
|
|
196
196
|
if (r) {
|
|
197
|
-
r.
|
|
198
|
-
cleanup(() => r.
|
|
197
|
+
r.te(i);
|
|
198
|
+
cleanup(() => r.oe(i));
|
|
199
199
|
}
|
|
200
200
|
return e;
|
|
201
201
|
});
|
|
@@ -204,9 +204,9 @@ class RevealController {
|
|
|
204
204
|
// Installed at module evaluation — present exactly when something imports
|
|
205
205
|
// `createRevealOrder`; a Loading boundary without one pays a null check.
|
|
206
206
|
setRevealHooks({
|
|
207
|
-
oe: (t, e) => t.oe(e),
|
|
208
207
|
te: (t, e) => t.te(e),
|
|
209
|
-
|
|
208
|
+
oe: (t, e) => t.oe(e),
|
|
209
|
+
V: t => t._t()
|
|
210
210
|
});
|
|
211
211
|
|
|
212
212
|
export { createRevealOrder };
|
package/dist/prod/signals.js
CHANGED
|
@@ -6,7 +6,7 @@ import { CONFIG_CHILDREN_FORBIDDEN, EFFECT_USER, $REFRESH, CONFIG_AUTO_DISPOSE,
|
|
|
6
6
|
|
|
7
7
|
import { computed, signal, runWithOwner, setMemo, setSignal, markRefresh, read, untrack } from "./core/core.js";
|
|
8
8
|
|
|
9
|
-
import { globalQueue, flushTransaction, dirtyQueue, GlobalQueue, resolveTx, passTx, staleReader, joinFuture } from "./core/scheduler.js";
|
|
9
|
+
import { schedule, globalQueue, flushTransaction, dirtyQueue, GlobalQueue, resolveTx, passTx, staleReader, joinFuture } from "./core/scheduler.js";
|
|
10
10
|
|
|
11
11
|
import { cleanup, createRoot, getOwner, dispose } from "./core/owner.js";
|
|
12
12
|
|
|
@@ -463,7 +463,7 @@ function createMemo(e, t) {
|
|
|
463
463
|
// guessed node's own re-ask is not answered by it (#3895): park
|
|
464
464
|
// on that flight, or reject with its error.
|
|
465
465
|
const e = read(t);
|
|
466
|
-
if (t.l & CONFIG_GUESS && t.m & (STATUS_PENDING | STATUS_ERROR)) throw t.T?.
|
|
466
|
+
if (t.l & CONFIG_GUESS && t.m & (STATUS_PENDING | STATUS_ERROR)) throw t.T?.U;
|
|
467
467
|
return e;
|
|
468
468
|
}, t => {
|
|
469
469
|
n(typeof e === "function" ? t : e);
|
|
@@ -657,7 +657,7 @@ function createMemo(e, t) {
|
|
|
657
657
|
// still holding the optimistic value — reads do not pull (#3411). Fall
|
|
658
658
|
// to the next pass while the heap has work; `run` swapped the queue,
|
|
659
659
|
// so this lands there, and `enqueue` keeps the drain alive.
|
|
660
|
-
if (dirtyQueue.et >= dirtyQueue.
|
|
660
|
+
if (dirtyQueue.et >= dirtyQueue.Fe) return globalQueue.enqueue(EFFECT_USER, fire);
|
|
661
661
|
// Unowned, out-of-band fire (no owner, or a children-forbidden one this
|
|
662
662
|
// one-shot must not bind to): a returned cleanup has no lifecycle to
|
|
663
663
|
// attach to. Reject it in dev; in production the return is simply
|
|
@@ -666,4 +666,46 @@ function createMemo(e, t) {
|
|
|
666
666
|
});
|
|
667
667
|
}
|
|
668
668
|
|
|
669
|
-
|
|
669
|
+
/**
|
|
670
|
+
* Schedules `callback` to run **once**, right after the pending flush
|
|
671
|
+
* finishes, in the same microtask checkpoint (before the browser paints).
|
|
672
|
+
* Unlike `onSettled`, it never waits on async work: if that flush is held by
|
|
673
|
+
* a pending transition, the callback still runs.
|
|
674
|
+
*
|
|
675
|
+
* Its main use is keeping a write out of another update's hold. Writes made
|
|
676
|
+
* in the same synchronous frame are applied together, so when one of them
|
|
677
|
+
* starts async work (a route change, a fetch), the others are held with it
|
|
678
|
+
* until it resolves. A write made in `onFlush` happens after that flush, so
|
|
679
|
+
* it is its own update: it commits on its own unless something it changes
|
|
680
|
+
* also derives from the held work (a memo or `createEffect` reading both),
|
|
681
|
+
* in which case it joins that hold as any later write would.
|
|
682
|
+
*
|
|
683
|
+
* Call it from event handlers and effect callbacks (including
|
|
684
|
+
* `createTrackedEffect` and `onSettled` bodies). In an owned scope such as a
|
|
685
|
+
* component body or a computation it warns in dev: use `onSettled` there.
|
|
686
|
+
*
|
|
687
|
+
* - A write in the callback lands after any write made inline in the same
|
|
688
|
+
* frame. Don't write the same signal both inside and outside it; use a
|
|
689
|
+
* functional setter if the order matters.
|
|
690
|
+
* - Several `onFlush` callbacks run in call order and batch into one flush.
|
|
691
|
+
* - `queueMicrotask` is not a substitute: called before the frame's first
|
|
692
|
+
* write, it runs before that flush and its write joins it.
|
|
693
|
+
*
|
|
694
|
+
* @example
|
|
695
|
+
* ```tsx
|
|
696
|
+
* // A menu closes now even when the consumer's onSelect starts async work.
|
|
697
|
+
* const select = (id: string) => {
|
|
698
|
+
* onFlush(() => setOpen(false));
|
|
699
|
+
* props.onSelect?.(id);
|
|
700
|
+
* };
|
|
701
|
+
* ```
|
|
702
|
+
*
|
|
703
|
+
* @param callback Function to run once after the pending flush
|
|
704
|
+
*/ function onFlush(e) {
|
|
705
|
+
// The frame's flush is queued (or already draining) ahead of this
|
|
706
|
+
// microtask, so the callback's writes start a flush of their own.
|
|
707
|
+
schedule();
|
|
708
|
+
queueMicrotask(e);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
export { accessor, createEffect, createMemo, createOptimistic, createReaction, createRenderEffect, createSignal, createTrackedEffect, onCleanup, onFlush, onSettled, refresh, resolve, until };
|
|
@@ -72,7 +72,7 @@ function scopeOf(e, o) {
|
|
|
72
72
|
if (f !== undefined) {
|
|
73
73
|
if (f.pb !== null) o.eo.add(f.v);
|
|
74
74
|
// The writer's view: the tick's own unflushed guesses are in motion too.
|
|
75
|
-
if (f.fam?.opt === true) r = optHooks.view(f, r, true);
|
|
75
|
+
if (optHooks && f.fam?.opt === true) r = optHooks.view(f, r, true);
|
|
76
76
|
const e = f.n;
|
|
77
77
|
if (e !== null) for (const o of Reflect.ownKeys(e))
|
|
78
78
|
// Another mark's carrier is its own channel: counting it here would
|
package/dist/prod/store/hooks.js
CHANGED
|
@@ -83,7 +83,7 @@ function notifyOptimisticWrites(e, n) {
|
|
|
83
83
|
// inner store's truth for its key before guessing over it.
|
|
84
84
|
const o = e.ch ? e.v : null;
|
|
85
85
|
const guess = (e, n, i, r) => {
|
|
86
|
-
if (o !== null) e.
|
|
86
|
+
if (o !== null) e.le = i ? n in t : unwrapValue(t[n]);
|
|
87
87
|
// By value: a function leaf is a value, not an updater (#3017).
|
|
88
88
|
optimisticWrite(e, () => r);
|
|
89
89
|
};
|
|
@@ -274,7 +274,7 @@ function arrangementEqual(e, n, t) {
|
|
|
274
274
|
if (!t) return fold(e, n);
|
|
275
275
|
const i = e.O;
|
|
276
276
|
if (i === undefined || e.ze !== $TRACK) return;
|
|
277
|
-
const r = e.T.
|
|
277
|
+
const r = e.T.P;
|
|
278
278
|
if (!(r instanceof LaneView)) return;
|
|
279
279
|
if (!Array.isArray(n) || !Array.isArray(r.rows)) {
|
|
280
280
|
r.base = n;
|
|
@@ -318,7 +318,7 @@ function arrangementEqual(e, n, t) {
|
|
|
318
318
|
// key's new object is the same row, its target adopted it).
|
|
319
319
|
if (n === undefined) {
|
|
320
320
|
if (!sameRow(d[e], f[e], o)) laneSet(getNode(i, e), f[e], c);
|
|
321
|
-
} else if (!sameRow(n.l & CONFIG_OVERRIDE ? n.T.
|
|
321
|
+
} else if (!sameRow(n.l & CONFIG_OVERRIDE ? n.T.P : d[e], f[e], o)) laneSet(n, f[e], c);
|
|
322
322
|
}
|
|
323
323
|
if (p !== null) {
|
|
324
324
|
for (let e = f.length; ;e++) {
|
|
@@ -342,8 +342,8 @@ function arrangementEqual(e, n, t) {
|
|
|
342
342
|
const i = txOf(e);
|
|
343
343
|
// A guess the screen never showed is void at the correction (D-34); a
|
|
344
344
|
// keyed arrangement guess's view carries its removed-keys set.
|
|
345
|
-
if (!i.
|
|
346
|
-
const r = e.T.
|
|
345
|
+
if (!i.ge || !(t?.l & CONFIG_OVERRIDE) || !t.T.P?.removed) return false;
|
|
346
|
+
const r = e.T.P;
|
|
347
347
|
if ((!e.xe || !e.xe(r, n)) && !GlobalQueue.nn(r, n)) {
|
|
348
348
|
i.folded = foldedRead;
|
|
349
349
|
insertSubs(e);
|
|
@@ -365,8 +365,8 @@ function arrangementEqual(e, n, t) {
|
|
|
365
365
|
const t = txOf(n);
|
|
366
366
|
joinPassAt(e, resolveTx(t._parent ?? t));
|
|
367
367
|
}
|
|
368
|
-
if (n.
|
|
369
|
-
return n.l & CONFIG_SLOT_NODE ? GlobalQueue.fn(n) : n.
|
|
368
|
+
if (n.ie !== NOT_PENDING) return n.ie;
|
|
369
|
+
return n.l & CONFIG_SLOT_NODE ? GlobalQueue.fn(n) : n.le;
|
|
370
370
|
}
|
|
371
371
|
|
|
372
372
|
function sameRow(e, n, t) {
|
|
@@ -383,8 +383,8 @@ function sameRow(e, n, t) {
|
|
|
383
383
|
if (!(e.l & CONFIG_OVERRIDE)) {
|
|
384
384
|
e.l |= CONFIG_OVERRIDE;
|
|
385
385
|
list(e, t);
|
|
386
|
-
} else if (i.
|
|
387
|
-
i.
|
|
386
|
+
} else if (i.P === n) return;
|
|
387
|
+
i.P = n;
|
|
388
388
|
if (e.o !== null) {
|
|
389
389
|
insertSubs(e);
|
|
390
390
|
laneDirty(e, t);
|
|
@@ -430,7 +430,7 @@ function sweepOverlaid() {
|
|
|
430
430
|
// The container shows its backing again (a shown arrangement guess
|
|
431
431
|
// that ended without a landing left its `LaneView` as the value).
|
|
432
432
|
const t = e.k;
|
|
433
|
-
if (t !== null && t.
|
|
433
|
+
if (t !== null && t.le instanceof LaneView) t.le = t.le.base;
|
|
434
434
|
}
|
|
435
435
|
if (n.size === 0) overlaidFamilies.delete(e);
|
|
436
436
|
}
|
|
@@ -447,8 +447,8 @@ function sweepOverlaid() {
|
|
|
447
447
|
if (i !== null) for (const e of Reflect.ownKeys(i)) {
|
|
448
448
|
const r = i[e];
|
|
449
449
|
const o = untrack(() => unwrapValue(n[e]));
|
|
450
|
-
if (r.xe && r.xe(r.
|
|
451
|
-
r.
|
|
450
|
+
if (r.xe && r.xe(r.le, o)) continue;
|
|
451
|
+
r.le = o;
|
|
452
452
|
// (The lane's end notified it this epoch when the shown value and the
|
|
453
453
|
// covered one differed — its readers re-derive already.)
|
|
454
454
|
if (r.o !== null && r.Me !== notifyEpoch) {
|
|
@@ -460,8 +460,8 @@ function sweepOverlaid() {
|
|
|
460
460
|
if (r !== null) for (const e of Reflect.ownKeys(r)) {
|
|
461
461
|
const i = r[e];
|
|
462
462
|
const o = untrack(() => e in n);
|
|
463
|
-
if (i.
|
|
464
|
-
i.
|
|
463
|
+
if (i.le === o) continue;
|
|
464
|
+
i.le = o;
|
|
465
465
|
if (i.o !== null && i.Me !== notifyEpoch) {
|
|
466
466
|
insertSubs(i);
|
|
467
467
|
t = true;
|
|
@@ -470,9 +470,9 @@ function sweepOverlaid() {
|
|
|
470
470
|
// The arrangement the readers saw (the shown guess) against the inner's
|
|
471
471
|
// now: a structural reader re-derives when they differ.
|
|
472
472
|
const o = e.k;
|
|
473
|
-
if (o !== null && o.o !== null && o.
|
|
473
|
+
if (o !== null && o.o !== null && o.le instanceof LaneView && o.Me !== notifyEpoch) {
|
|
474
474
|
const i = Array.isArray(n) ? untrack(() => n.map(unwrapValue)) : n;
|
|
475
|
-
if (!arrangementEqual(o.
|
|
475
|
+
if (!arrangementEqual(o.le.rows, i, e.fam?.key ?? null)) {
|
|
476
476
|
insertSubs(o);
|
|
477
477
|
t = true;
|
|
478
478
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CONFIG_AUTO_DISPOSE, STATUS_PENDING, STATUS_UNINITIALIZED } from "../core/constants.js";
|
|
1
|
+
import { CONFIG_AUTO_DISPOSE, STATUS_PENDING, STATUS_ERROR, STATUS_UNINITIALIZED } from "../core/constants.js";
|
|
2
2
|
|
|
3
3
|
import { computed } from "../core/core.js";
|
|
4
4
|
|
|
@@ -118,8 +118,14 @@ function cloneState(e, t) {
|
|
|
118
118
|
* (`notifyStatus`): a verdict or re-derive reader re-runs, any other is
|
|
119
119
|
* pending derivatively on the derive (its readers hold; it re-runs at the
|
|
120
120
|
* landing). The leaves do not subscribe to the derive, so the family does
|
|
121
|
-
* the walk. */ function wakeFamily(e
|
|
122
|
-
const
|
|
121
|
+
* the walk. */ function wakeFamily(e) {
|
|
122
|
+
const t = e.node;
|
|
123
|
+
// Pending on the derive itself, whatever source took it there (an upstream
|
|
124
|
+
// memo's flight reaches it as a NotReady of that memo, #4271): the leaves
|
|
125
|
+
// are not that source's dependents, so only the family's settle — which
|
|
126
|
+
// retires the derive (`settleFamily`) — reaches the readers parked here. A
|
|
127
|
+
// reader the landing leaves unchanged does not re-run to clear it (#4305).
|
|
128
|
+
const r = new NotReadyError(t);
|
|
123
129
|
e.woke = true;
|
|
124
130
|
forEachFamilyNode(e, e => {
|
|
125
131
|
if (e.o === null) return;
|
|
@@ -127,12 +133,12 @@ function cloneState(e, t) {
|
|
|
127
133
|
// between them would: a dependent recovering one branch stays pending
|
|
128
134
|
// through a leaf it still reads (core `retryReaches`), and the landing's
|
|
129
135
|
// walk retires the copy first (`settleFamily` → `settlePendingSource`).
|
|
130
|
-
addPendingSource(e,
|
|
136
|
+
addPendingSource(e, t);
|
|
131
137
|
// Its readers: core's propagation from the leaf — a verdict or re-derive
|
|
132
138
|
// reader re-runs, any other is pending derivatively on the derive and
|
|
133
139
|
// queued for the frame (held with it: the frame waits on the flight
|
|
134
140
|
// through them, `blocked`).
|
|
135
|
-
propagateStatus(e, STATUS_PENDING,
|
|
141
|
+
propagateStatus(e, STATUS_PENDING, r);
|
|
136
142
|
});
|
|
137
143
|
}
|
|
138
144
|
|
|
@@ -158,6 +164,12 @@ function cloneState(e, t) {
|
|
|
158
164
|
});
|
|
159
165
|
}
|
|
160
166
|
|
|
167
|
+
/** A source's status reached the derive by propagation (core
|
|
168
|
+
* `propagateStatus`), its pass not re-run: the family learns it as from the
|
|
169
|
+
* pass's own throw. */ function familyStatus(e) {
|
|
170
|
+
if (e & STATUS_PENDING) wakeFamily(this); else if (e & STATUS_ERROR) errorFamily(this);
|
|
171
|
+
}
|
|
172
|
+
|
|
161
173
|
function forEachFamilyNode(e, t) {
|
|
162
174
|
for (const r of e.live) {
|
|
163
175
|
const e = r.n;
|
|
@@ -187,7 +199,7 @@ function forEachFamilyNode(e, t) {
|
|
|
187
199
|
// The question a write of this run answers: inside a slice, the slice's
|
|
188
200
|
// (the sync pass); in a continuation — the flight still registered — the
|
|
189
201
|
// flight's (`handleAsync` stamped it with the action that asked).
|
|
190
|
-
const asking = () => question || (i.T !== null && i.T.
|
|
202
|
+
const asking = () => question || (i.T !== null && i.T.Ee !== null ? i.T.Te : 0);
|
|
191
203
|
const f = wrapDraft(e, () => c.run === u && !isDisposed(i), o, s.s,
|
|
192
204
|
// A write after the run returned (a continuation, a callback) arms the
|
|
193
205
|
// drain itself when no landing will.
|
|
@@ -234,7 +246,7 @@ function forEachFamilyNode(e, t) {
|
|
|
234
246
|
// #2897) — and the flush that runs them advances the clock, so a
|
|
235
247
|
// later tracked re-read may retry (core `read`).
|
|
236
248
|
if (d && i.m & STATUS_UNINITIALIZED) return;
|
|
237
|
-
if (t) wakeFamily(c
|
|
249
|
+
if (t) wakeFamily(c); else errorFamily(c);
|
|
238
250
|
});
|
|
239
251
|
if (!i.ce) commit(u);
|
|
240
252
|
}, false);
|
|
@@ -246,14 +258,14 @@ function forEachFamilyNode(e, t) {
|
|
|
246
258
|
// they do not subscribe to the derive.
|
|
247
259
|
// A synchronous error the same way (#3956: a rerun that finds the
|
|
248
260
|
// entry failed throws): every live reader re-derives and meets it.
|
|
249
|
-
if (!d) e instanceof NotReadyError ? wakeFamily(c
|
|
261
|
+
if (!d) e instanceof NotReadyError ? wakeFamily(c) : errorFamily(c);
|
|
250
262
|
throw e;
|
|
251
263
|
}
|
|
252
264
|
// A pass that returned with a flight up (an iterator's sync first yield):
|
|
253
265
|
// the wake, as for the throw. Not a sync pass that superseded one — the
|
|
254
266
|
// status it reads is the dead flight's, cleared when this pass settles
|
|
255
267
|
// (#3181: a reader the settle walk had just released, re-parked for good).
|
|
256
|
-
if (i.m & STATUS_PENDING && i.T.
|
|
268
|
+
if (i.m & STATUS_PENDING && i.T.Ee !== null && !i.ce && !d) wakeFamily(c);
|
|
257
269
|
return i;
|
|
258
270
|
}
|
|
259
271
|
|
|
@@ -266,6 +278,7 @@ function createProjectionInternal(e, t, r) {
|
|
|
266
278
|
map: new WeakMap,
|
|
267
279
|
node: null,
|
|
268
280
|
live: new Set,
|
|
281
|
+
st: familyStatus,
|
|
269
282
|
shallow: !!r?.shallow,
|
|
270
283
|
// The row key, for a child paired after the adoption (store.ts
|
|
271
284
|
// `adoptChild`, #3712) as the adoption pairs them.
|
|
@@ -31,7 +31,7 @@ import { $TARGET, markRawIngest, isWrappable, rawValuesUsed, isRawValue } from "
|
|
|
31
31
|
// truth but an optimistic edit — the keyed diff is written into the draft
|
|
32
32
|
// (a matched row keeps its proxy, its changed leaves become its guesses),
|
|
33
33
|
// and the setter's exit turns the draft into guesses like any.
|
|
34
|
-
if (userWriting() && f.fam?.opt === true) {
|
|
34
|
+
if (optHooks && userWriting() && f.fam?.opt === true) {
|
|
35
35
|
optHooks.reconcile(n, unwrapValue(e), i);
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
@@ -72,7 +72,7 @@ import { $TARGET, markRawIngest, isWrappable, rawValuesUsed, isRawValue } from "
|
|
|
72
72
|
// Q-D: a landing on an optimistic family is reconciled against the VIEW
|
|
73
73
|
// — key matching from the arrangement the guesses made (R28), so a row
|
|
74
74
|
// the guess added keeps its proxy when the server returns it.
|
|
75
|
-
const i = e.fam?.opt === true ? optHooks.view(e, f) : f;
|
|
75
|
+
const i = optHooks && e.fam?.opt === true ? optHooks.view(e, f) : f;
|
|
76
76
|
const l = Array.isArray(n);
|
|
77
77
|
const r = e.s === true;
|
|
78
78
|
adoptPB(e, n, false);
|