@solidjs/signals 2.0.0-rc.14 → 2.0.0-rc.15
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 +248 -189
- package/dist/dev.attribution.js +124 -41
- package/dist/dev.js +553 -322
- package/dist/observe/affects.js +34 -31
- package/dist/observe/boundaries.js +166 -132
- package/dist/observe/core/async.js +90 -83
- package/dist/observe/core/attribution-queries.js +1 -1
- package/dist/observe/core/attribution.js +281 -197
- package/dist/observe/core/constants.js +7 -2
- package/dist/observe/core/context.js +3 -3
- package/dist/observe/core/core.js +277 -245
- package/dist/observe/core/dev.js +3 -3
- package/dist/observe/core/effect.js +21 -21
- package/dist/observe/core/external.js +2 -2
- package/dist/observe/core/graph.js +25 -25
- package/dist/observe/core/heap.js +9 -9
- package/dist/observe/core/lanes.js +206 -160
- package/dist/observe/core/owner.js +27 -27
- package/dist/observe/core/scheduler.js +92 -86
- package/dist/observe/core/verdict.js +66 -54
- package/dist/observe/map.js +35 -35
- package/dist/observe/reveal.js +24 -24
- package/dist/observe/signals.js +38 -28
- package/dist/observe/store/affects.js +75 -68
- package/dist/observe/store/hooks.js +13 -0
- package/dist/observe/store/index.js +0 -2
- package/dist/observe/store/optimistic.js +67 -34
- package/dist/observe/store/projection.js +37 -31
- package/dist/observe/store/reconcile.js +32 -30
- package/dist/observe/store/store.js +128 -85
- package/dist/prod/affects.js +34 -31
- package/dist/prod/boundaries.js +143 -109
- package/dist/prod/core/async.js +64 -67
- package/dist/prod/core/constants.js +7 -2
- package/dist/prod/core/context.js +3 -3
- package/dist/prod/core/core.js +302 -270
- package/dist/prod/core/effect.js +38 -38
- package/dist/prod/core/external.js +4 -4
- package/dist/prod/core/graph.js +27 -27
- package/dist/prod/core/heap.js +30 -30
- package/dist/prod/core/lanes.js +257 -211
- package/dist/prod/core/owner.js +39 -39
- package/dist/prod/core/scheduler.js +125 -119
- package/dist/prod/core/verdict.js +70 -58
- package/dist/prod/map.js +73 -73
- package/dist/prod/reveal.js +38 -38
- package/dist/prod/signals.js +30 -20
- package/dist/prod/store/affects.js +75 -68
- package/dist/prod/store/hooks.js +13 -0
- package/dist/prod/store/index.js +0 -2
- package/dist/prod/store/optimistic.js +114 -81
- package/dist/prod/store/projection.js +30 -24
- package/dist/prod/store/reconcile.js +32 -30
- package/dist/prod/store/store.js +136 -93
- package/dist/types/boundaries.d.ts +4 -0
- package/dist/types/core/attribution.d.ts +6 -2
- package/dist/types/core/constants.d.ts +5 -0
- package/dist/types/core/core.d.ts +33 -3
- package/dist/types/core/lanes.d.ts +10 -0
- package/dist/types/core/scheduler.d.ts +12 -6
- package/dist/types/core/types.d.ts +0 -6
- package/dist/types/store/affects.d.ts +11 -0
- package/dist/types/store/hooks.d.ts +48 -0
- package/dist/types/store/index.d.ts +0 -1
- package/dist/types/store/store.d.ts +3 -38
- package/package.json +1 -1
package/dist/observe/signals.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { NotReadyError, unwrapStatusError
|
|
1
|
+
import { TimeoutError, NotReadyError, unwrapStatusError } from "./core/error.js";
|
|
2
2
|
|
|
3
3
|
import "./core/verdict.js";
|
|
4
4
|
|
|
5
|
-
import { CONFIG_CHILDREN_FORBIDDEN, EFFECT_USER, $REFRESH, CONFIG_REDERIVE, REACTIVE_JOINED,
|
|
5
|
+
import { CONFIG_CHILDREN_FORBIDDEN, EFFECT_USER, $REFRESH, CONFIG_AUTO_DISPOSE, CONFIG_AUTHORITATIVE, CONFIG_REDERIVE, REACTIVE_JOINED, CONFIG_FRESH_READ, CONFIG_GUESS, STATUS_PENDING, STATUS_ERROR } from "./core/constants.js";
|
|
6
6
|
|
|
7
7
|
import { computed, signal, runWithOwner, setMemo, setSignal, markRefresh, read, untrack } from "./core/core.js";
|
|
8
8
|
|
|
9
9
|
import { registerGraph } from "./core/dev.js";
|
|
10
10
|
|
|
11
|
-
import { globalQueue, flushTransaction, resolveTx, passTx, staleReader,
|
|
11
|
+
import { globalQueue, flushTransaction, dirtyQueue, GlobalQueue, resolveTx, passTx, staleReader, joinFuture } from "./core/scheduler.js";
|
|
12
12
|
|
|
13
13
|
import { cleanup, createRoot, getOwner, dispose } from "./core/owner.js";
|
|
14
14
|
|
|
15
15
|
import { effect, trackedEffect } from "./core/effect.js";
|
|
16
16
|
|
|
17
|
-
import { optimisticWrite } from "./core/lanes.js";
|
|
17
|
+
import { optimisticWrite, truthRead, truthConfirmed } from "./core/lanes.js";
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Low-level reactive-cleanup primitive. Registers a callback that runs when
|
|
@@ -73,7 +73,7 @@ function accessor(e) {
|
|
|
73
73
|
function createSignal(e, t) {
|
|
74
74
|
if (typeof e === "function") {
|
|
75
75
|
const r = computed(e, t);
|
|
76
|
-
r.
|
|
76
|
+
r.i &= ~CONFIG_AUTO_DISPOSE;
|
|
77
77
|
return [ accessor(r), setMemo.bind(null, r) ];
|
|
78
78
|
}
|
|
79
79
|
const r = signal(e, t);
|
|
@@ -84,7 +84,7 @@ function createSignal(e, t) {
|
|
|
84
84
|
function createOptimistic(e, t) {
|
|
85
85
|
if (typeof e === "function") {
|
|
86
86
|
const r = computed(e, t);
|
|
87
|
-
r.
|
|
87
|
+
r.i &= ~CONFIG_AUTO_DISPOSE;
|
|
88
88
|
return [ accessor(r), optimisticWrite.bind(null, r) ];
|
|
89
89
|
}
|
|
90
90
|
const r = signal(e, t);
|
|
@@ -324,11 +324,15 @@ function createMemo(e, t) {
|
|
|
324
324
|
* - a mainline reader whose pass read a frame another transaction holds
|
|
325
325
|
* joined it (L2, born held — A29) and delivers at that commit: the
|
|
326
326
|
* committed view, never a foreign hold's unrevealed frame. */ function watch(e, t, r, n = 0, o, c = flushTransaction) {
|
|
327
|
+
if (n & CONFIG_AUTHORITATIVE) {
|
|
328
|
+
GlobalQueue.Nn = truthRead;
|
|
329
|
+
GlobalQueue.rn = truthConfirmed;
|
|
330
|
+
}
|
|
327
331
|
// `config` is on the node for its first pass too (the pass runs inside
|
|
328
332
|
// `computed()`): `until`'s predicate is authoritative from its first read,
|
|
329
333
|
// or an already-displayed guess would satisfy it in a later slice. (REDERIVE
|
|
330
334
|
// after: the first pass's own error is the promise's rejection.)
|
|
331
|
-
|
|
335
|
+
computed(() => {
|
|
332
336
|
let n;
|
|
333
337
|
try {
|
|
334
338
|
n = e();
|
|
@@ -337,18 +341,18 @@ function createMemo(e, t) {
|
|
|
337
341
|
queueMicrotask(() => r(unwrapStatusError(e)));
|
|
338
342
|
return;
|
|
339
343
|
}
|
|
340
|
-
const
|
|
344
|
+
const u = getOwner();
|
|
341
345
|
// (Outside a flush a mainline pass joins through `passTx`.)
|
|
342
|
-
const
|
|
343
|
-
if (
|
|
344
|
-
staleReader(
|
|
346
|
+
const i = flushTransaction ?? passTx;
|
|
347
|
+
if (u.R & REACTIVE_JOINED && i !== null && (c === null || resolveTx(c) !== resolveTx(i))) {
|
|
348
|
+
staleReader(u, resolveTx(i));
|
|
345
349
|
return;
|
|
346
350
|
}
|
|
347
351
|
o?.(n);
|
|
348
352
|
queueMicrotask(() => t(n));
|
|
349
353
|
}, {
|
|
350
|
-
|
|
351
|
-
}).
|
|
354
|
+
Ct: n
|
|
355
|
+
}).i |= CONFIG_REDERIVE;
|
|
352
356
|
}
|
|
353
357
|
|
|
354
358
|
/**
|
|
@@ -434,23 +438,29 @@ function createMemo(e, t) {
|
|
|
434
438
|
const r = flushTransaction;
|
|
435
439
|
const n = new Promise((n, o) => {
|
|
436
440
|
queueMicrotask(() => {
|
|
437
|
-
//
|
|
438
|
-
//
|
|
439
|
-
//
|
|
440
|
-
// capture runs inside the watcher's
|
|
441
|
-
// IS the watcher — exactly what
|
|
441
|
+
// The root is load-bearing: an ownerless computed is auto-disposing,
|
|
442
|
+
// and the settle walk releases an unobserved one instead of re-running
|
|
443
|
+
// it, so the waiter would never see the refetch land (#3888). Settle
|
|
444
|
+
// disposes the waiter directly; the capture runs inside the watcher's
|
|
445
|
+
// own pass, where the ambient owner IS the watcher — exactly what
|
|
446
|
+
// dispose() takes.
|
|
442
447
|
let c = null;
|
|
443
448
|
const make = () => watch(() => {
|
|
444
449
|
if (c === null) c = getOwner();
|
|
445
|
-
|
|
450
|
+
// Authoritative: the truth beneath a guess (`until`'s view). A
|
|
451
|
+
// guessed node's own re-ask is not answered by it (#3895): park
|
|
452
|
+
// on that flight, or reject with its error.
|
|
453
|
+
const e = read(t);
|
|
454
|
+
if (t.i & CONFIG_GUESS && t.S & (STATUS_PENDING | STATUS_ERROR)) throw t.m?.F;
|
|
455
|
+
return e;
|
|
446
456
|
}, t => {
|
|
447
457
|
n(typeof e === "function" ? t : e);
|
|
448
458
|
dispose(c);
|
|
449
459
|
}, e => {
|
|
450
460
|
o(e);
|
|
451
461
|
dispose(c);
|
|
452
|
-
}, CONFIG_FRESH_READ, undefined, r);
|
|
453
|
-
make
|
|
462
|
+
}, CONFIG_FRESH_READ | CONFIG_AUTHORITATIVE, undefined, r);
|
|
463
|
+
createRoot(make);
|
|
454
464
|
});
|
|
455
465
|
});
|
|
456
466
|
// Fire-and-forget refresh must not turn a failed refetch into an unhandled
|
|
@@ -499,14 +509,14 @@ function createMemo(e, t) {
|
|
|
499
509
|
// (`saving=true` beside the confirmed `version=1` is a tear no timeline
|
|
500
510
|
// contains). Updates that do not flip the predicate reveal freely.
|
|
501
511
|
const c = flushTransaction;
|
|
502
|
-
createRoot(
|
|
503
|
-
let
|
|
512
|
+
createRoot(u => {
|
|
513
|
+
let i;
|
|
504
514
|
let s;
|
|
505
515
|
const settle = e => {
|
|
506
|
-
if (
|
|
516
|
+
if (i !== undefined) clearTimeout(i);
|
|
507
517
|
if (s !== undefined) o.removeEventListener("abort", s);
|
|
508
518
|
e();
|
|
509
|
-
|
|
519
|
+
u();
|
|
510
520
|
};
|
|
511
521
|
// Authoritative: the predicate sees the truth, never the action's own
|
|
512
522
|
// optimistic guess — a guess cannot satisfy the wait for its own
|
|
@@ -519,9 +529,9 @@ function createMemo(e, t) {
|
|
|
519
529
|
// The flip, inside the pass of the flush carrying it: the flush
|
|
520
530
|
// joins the action's transaction, and what it staged is held and
|
|
521
531
|
// revealed with the action.
|
|
522
|
-
if (e && c !== null && globalQueue.
|
|
532
|
+
if (e && c !== null && globalQueue.X && resolveTx(c).tt !== 0) joinFuture(c);
|
|
523
533
|
});
|
|
524
|
-
if (t?.timeout !== undefined)
|
|
534
|
+
if (t?.timeout !== undefined) i = setTimeout(() => settle(() => n(new TimeoutError)), t.timeout);
|
|
525
535
|
if (o !== undefined) {
|
|
526
536
|
s = () => settle(() => n(o.reason));
|
|
527
537
|
o.addEventListener("abort", s, {
|
|
@@ -627,7 +637,7 @@ function createMemo(e, t) {
|
|
|
627
637
|
* on owner disposal
|
|
628
638
|
*/ function onSettled(e) {
|
|
629
639
|
const t = getOwner();
|
|
630
|
-
t && !(t.
|
|
640
|
+
t && !(t.i & CONFIG_CHILDREN_FORBIDDEN) ? trackedEffect(() => untrack(e), {
|
|
631
641
|
name: "onSettled"
|
|
632
642
|
}) : globalQueue.enqueue(EFFECT_USER, function fire() {
|
|
633
643
|
// Settled means derived. A settle that reverts optimism (or replays
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { GlobalQueue } from "../core/scheduler.js";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { optHooks, installAffectsHooks } from "./hooks.js";
|
|
4
4
|
|
|
5
5
|
import { lookupTarget, $OWNER } from "./target.js";
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { $TARGET } from "./types.js";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Store — the store half of `affects()` (plan sec. 29, plan sec. 37; A24 (4), #2882,
|
|
@@ -23,113 +23,120 @@ import { isWrappable, $TARGET } from "./types.js";
|
|
|
23
23
|
* the verdict. A mark declared on a draft (inside a setter) or over an
|
|
24
24
|
* optimistic family walks the view the writer sees.
|
|
25
25
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
26
|
+
* Imported by `affects()`, which installs it on its first store target
|
|
27
|
+
* (#3891, after #3912): a program with stores that never declares a mark
|
|
28
|
+
* carries none of it. It never imports store.ts — the engine's top level —
|
|
29
|
+
* so a program declaring marks without stores carries this module alone:
|
|
30
|
+
* the store's nodes and its wrappability test come through `GlobalQueue`
|
|
31
|
+
* (`_storeNode`, `_storeWrappable`), and node birth and the untracked traps
|
|
32
|
+
* reach back through `affectsHooks` (hooks.ts).
|
|
28
33
|
*/
|
|
29
34
|
/** The keyless mark's carrier key: a leaf of the record no read serves. */ const $AFFECTS = Symbol(0);
|
|
30
35
|
|
|
31
36
|
const scopes = new Map;
|
|
32
37
|
|
|
38
|
+
function scopeOf(e, o) {
|
|
39
|
+
let n = scopes.get(e);
|
|
40
|
+
if (n === undefined) scopes.set(e, n = {
|
|
41
|
+
eo: new Set,
|
|
42
|
+
oo: [],
|
|
43
|
+
bt: o
|
|
44
|
+
});
|
|
45
|
+
return n;
|
|
46
|
+
}
|
|
47
|
+
|
|
33
48
|
/** The nodes an `affects(store[, key])` declaration marks (affects.ts
|
|
34
|
-
* registers each). */ function storeMarks(e,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
});
|
|
42
|
-
const t = [ n ];
|
|
43
|
-
walk(e.px, o, t, e.fam, new Set);
|
|
44
|
-
return t;
|
|
49
|
+
* registers each). */ function storeMarks(e, o) {
|
|
50
|
+
const n = GlobalQueue.no(e, o === undefined ? $AFFECTS : o);
|
|
51
|
+
const t = scopeOf(n, o);
|
|
52
|
+
if (o === undefined) {
|
|
53
|
+
const o = [ n ];
|
|
54
|
+
walk(e.px, t, o, e.fam, new Set);
|
|
55
|
+
return o;
|
|
45
56
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
scope: new Set,
|
|
50
|
-
inherited: [],
|
|
51
|
-
key: n
|
|
52
|
-
});
|
|
53
|
-
t.scope.add(e.v);
|
|
54
|
-
if (e.pb !== null) t.scope.add(e.pb);
|
|
55
|
-
return [ o ];
|
|
57
|
+
t.eo.add(e.v);
|
|
58
|
+
if (e.pb !== null) t.eo.add(e.pb);
|
|
59
|
+
return [ n ];
|
|
56
60
|
}
|
|
57
61
|
|
|
58
62
|
/** The identities reachable from `value` into the scope (the draft's and
|
|
59
63
|
* the committed backing both, when a draft is open; an optimistic family's
|
|
60
64
|
* view), every live node under each record into `found`. Untracked by
|
|
61
|
-
* construction: raws, never traps. */ function walk(e,
|
|
62
|
-
if (!
|
|
65
|
+
* construction: raws, never traps. */ function walk(e, o, n, t, s) {
|
|
66
|
+
if (!GlobalQueue.so(e)) return;
|
|
63
67
|
const f = e[$TARGET] ?? lookupTarget(e, t);
|
|
64
|
-
let
|
|
65
|
-
if (s.has(
|
|
66
|
-
s.add(
|
|
67
|
-
|
|
68
|
+
let r = f !== undefined ? f.pb ?? f.v : e;
|
|
69
|
+
if (s.has(r)) return;
|
|
70
|
+
s.add(r);
|
|
71
|
+
o.eo.add(r);
|
|
68
72
|
if (f !== undefined) {
|
|
69
|
-
if (f.pb !== null)
|
|
73
|
+
if (f.pb !== null) o.eo.add(f.v);
|
|
70
74
|
// The writer's view: the tick's own unflushed guesses are in motion too.
|
|
71
|
-
if (f.fam?.opt === true)
|
|
75
|
+
if (f.fam?.opt === true) r = optHooks.view(f, r, true);
|
|
72
76
|
const e = f.n;
|
|
73
|
-
if (e !== null) for (const
|
|
77
|
+
if (e !== null) for (const o of Reflect.ownKeys(e))
|
|
74
78
|
// Another mark's carrier is its own channel: counting it here would
|
|
75
79
|
// extend that scope's lifetime to this declaration's.
|
|
76
|
-
if (
|
|
80
|
+
if (o !== $AFFECTS) n.push(e[o]);
|
|
77
81
|
const s = f.h;
|
|
78
|
-
if (s !== null) for (const e of Reflect.ownKeys(s))
|
|
79
|
-
if (f.k !== null)
|
|
80
|
-
if (f.dk !== null)
|
|
82
|
+
if (s !== null) for (const e of Reflect.ownKeys(s)) n.push(s[e]);
|
|
83
|
+
if (f.k !== null) n.push(f.k);
|
|
84
|
+
if (f.dk !== null) n.push(f.dk);
|
|
81
85
|
t = f.fam ?? t;
|
|
82
86
|
}
|
|
83
|
-
for (const e of Reflect.ownKeys(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (f === undefined || f.get !== undefined) continue;
|
|
87
|
-
walk(f.value, n, o, t, s);
|
|
87
|
+
for (const e of Reflect.ownKeys(r)) if (e !== $OWNER) {
|
|
88
|
+
const f = Object.getOwnPropertyDescriptor(r, e);
|
|
89
|
+
if (f.get === undefined) walk(f.value, o, n, t, s);
|
|
88
90
|
}
|
|
89
91
|
}
|
|
90
92
|
|
|
91
93
|
/** A live scope covering `t`'s identity (keyed: for `key`). Chained
|
|
92
94
|
* backings (§7b): a view's backing is another store's proxy — marks cover
|
|
93
|
-
* the BASE raw, so every identity along the chain is checked. */ function covering(e,
|
|
95
|
+
* the BASE raw, so every identity along the chain is checked. */ function covering(e, o, n) {
|
|
94
96
|
for (const [t, s] of scopes) {
|
|
95
|
-
if (t ===
|
|
96
|
-
for (let
|
|
97
|
-
if (s.
|
|
98
|
-
const e =
|
|
97
|
+
if (t === n || s.bt !== undefined && s.bt !== o) continue;
|
|
98
|
+
for (let o = e.v; ;) {
|
|
99
|
+
if (s.eo.has(o)) return t;
|
|
100
|
+
const e = o?.[$TARGET];
|
|
99
101
|
if (e === undefined) break;
|
|
100
|
-
const
|
|
101
|
-
if (
|
|
102
|
-
|
|
102
|
+
const n = e.pb ?? e.v;
|
|
103
|
+
if (n === o) break;
|
|
104
|
+
o = n;
|
|
103
105
|
}
|
|
104
106
|
}
|
|
105
107
|
return null;
|
|
106
108
|
}
|
|
107
109
|
|
|
108
|
-
|
|
110
|
+
/** The store's side: node birth and the untracked traps call it (store.ts,
|
|
111
|
+
* through `affectsHooks`) — with no live mark too: an empty scope map is
|
|
112
|
+
* no work. */ const hooks = {
|
|
109
113
|
// A node born on a record a live mark covers inherits it (released with
|
|
110
114
|
// the carrier's last registration).
|
|
111
|
-
|
|
112
|
-
if (
|
|
113
|
-
const t = covering(e,
|
|
114
|
-
if (t
|
|
115
|
-
GlobalQueue.
|
|
116
|
-
scopes.get(t).
|
|
115
|
+
fo(e, o, n) {
|
|
116
|
+
if (scopes.size === 0 || n === $AFFECTS) return;
|
|
117
|
+
const t = covering(e, n, o);
|
|
118
|
+
if (!t) return;
|
|
119
|
+
GlobalQueue.O(o);
|
|
120
|
+
scopes.get(t).oo.push(o);
|
|
117
121
|
},
|
|
118
122
|
// An untracked probe (`isPending(() => s.x)` with no observer) reading a
|
|
119
123
|
// record a live mark covers: no node carries the mark for it — the
|
|
120
124
|
// verdict is told directly.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if (o !== undefined && o.m !== null && o.m.T !== 0 || covering(e, n) !== null) GlobalQueue.Bt();
|
|
125
|
+
ro(e, o) {
|
|
126
|
+
if (e.n?.[$AFFECTS]?.m?.T || covering(e, o)) GlobalQueue.Bt();
|
|
124
127
|
}
|
|
125
|
-
}
|
|
128
|
+
};
|
|
126
129
|
|
|
127
|
-
|
|
130
|
+
/** `affects()` calls it before a store-targeted declaration (idempotent). */ function installStoreAffects() {
|
|
131
|
+
installAffectsHooks(hooks);
|
|
132
|
+
}
|
|
128
133
|
|
|
129
134
|
/** The carrier's last mark released (affects.ts): its scope dies, and the
|
|
130
|
-
* nodes that inherited the mark release theirs. */
|
|
131
|
-
const
|
|
132
|
-
if (
|
|
135
|
+
* nodes that inherited the mark release theirs. */ function releaseMarkScope(e) {
|
|
136
|
+
const o = scopes.get(e);
|
|
137
|
+
if (o === undefined) return;
|
|
133
138
|
scopes.delete(e);
|
|
134
|
-
GlobalQueue.
|
|
135
|
-
}
|
|
139
|
+
GlobalQueue.k(o.oo);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export { installStoreAffects, releaseMarkScope, storeMarks };
|
|
@@ -2,8 +2,6 @@ import { createStore as createStore$1, deep as deep$1, snapshot as snapshot$1 }
|
|
|
2
2
|
|
|
3
3
|
export { storeHasFamily, storeHasOptimisticFamily, storeIsShallow } from "./store.js";
|
|
4
4
|
|
|
5
|
-
import "./affects.js";
|
|
6
|
-
|
|
7
5
|
export { createOptimisticStore } from "./optimistic.js";
|
|
8
6
|
|
|
9
7
|
import { createStoreDerived } from "./projection.js";
|
|
@@ -10,7 +10,9 @@ import { GlobalQueue, txOf, list, insertSubs, laneDirty, notifyEpoch, schedule }
|
|
|
10
10
|
|
|
11
11
|
import { runProjectionComputed } from "./projection.js";
|
|
12
12
|
|
|
13
|
-
import { wrap, nameStore, storeSetter, LaneView, committed, getNode, unwrapValue, sameKey,
|
|
13
|
+
import { wrap, nameStore, storeSetter, LaneView, committed, getNode, unwrapValue, sameKey, resolveChainedRaw, getHasNode, targetsEqual, getContainerNode, cloneRaw } from "./store.js";
|
|
14
|
+
|
|
15
|
+
import { installOptHooks } from "./hooks.js";
|
|
14
16
|
|
|
15
17
|
import { $OWNER } from "./target.js";
|
|
16
18
|
|
|
@@ -79,7 +81,7 @@ function notifyOptimisticWrites(e, n) {
|
|
|
79
81
|
// inner store's truth for its key before guessing over it.
|
|
80
82
|
const o = e.ch ? e.v : null;
|
|
81
83
|
const guess = (e, n, i, r) => {
|
|
82
|
-
if (o !== null) e.
|
|
84
|
+
if (o !== null) e.Ae = i ? n in t : unwrapValue(t[n]);
|
|
83
85
|
// By value: a function leaf is a value, not an updater (#3017).
|
|
84
86
|
optimisticWrite(e, () => r);
|
|
85
87
|
};
|
|
@@ -157,9 +159,40 @@ function rowSame(e, n, t) {
|
|
|
157
159
|
if (t === null || !isWrappable(e) || !isWrappable(n)) return false;
|
|
158
160
|
const i = t(unwrapValue(e));
|
|
159
161
|
const r = t(unwrapValue(n));
|
|
162
|
+
// Keyless rows are identified by position — reconcile's fallback when the
|
|
163
|
+
// key is missing. The caller already paired these by index, so two keyless
|
|
164
|
+
// rows are the same row and the landing confirms the arrangement instead of
|
|
165
|
+
// dissolving it. Dissolving held an external echo under the action, and a
|
|
166
|
+
// mainline `until` then timed out on a source that already contained it
|
|
167
|
+
// (#3898). Keyed rows still match only on the key.
|
|
168
|
+
if (i === undefined && r === undefined) return true;
|
|
160
169
|
return i !== undefined && sameKey(i, r);
|
|
161
170
|
}
|
|
162
171
|
|
|
172
|
+
/** Whether reconcile would write anything into `a` from `b`: wrappable
|
|
173
|
+
* pairs descend, leaves compare as its writes do. A key mismatch is a
|
|
174
|
+
* replacement, which a content difference already says. Plain data — the
|
|
175
|
+
* server's echo of a guessed row is a new object with the same contents. */ function echoes(e, n) {
|
|
176
|
+
if (e === n) return true;
|
|
177
|
+
if (!isWrappable(e) || !isWrappable(n)) return false;
|
|
178
|
+
e = unwrapValue(e);
|
|
179
|
+
n = unwrapValue(n);
|
|
180
|
+
const t = Object.keys(e);
|
|
181
|
+
if (Array.isArray(e) !== Array.isArray(n) || t.length !== Object.keys(n).length) return false;
|
|
182
|
+
for (const i of t) if (!hasOwn.call(n, i) || !echoes(e[i], n[i])) return false;
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// A cycle (a row that references itself) is not plain data: no match, so
|
|
187
|
+
// the landing stays a correction.
|
|
188
|
+
GlobalQueue.sn = (e, n) => {
|
|
189
|
+
try {
|
|
190
|
+
return echoes(e, n);
|
|
191
|
+
} catch {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
163
196
|
function arrangementEqual(e, n, t) {
|
|
164
197
|
if (Array.isArray(e) !== Array.isArray(n)) return false;
|
|
165
198
|
if (Array.isArray(e)) {
|
|
@@ -233,10 +266,10 @@ function arrangementEqual(e, n, t) {
|
|
|
233
266
|
* node is born for it as the lane's derivation), on `length` only when the
|
|
234
267
|
* committed length differs, and as the `LaneView`'s base for structural
|
|
235
268
|
* reads. Unkeyed families compose
|
|
236
|
-
* positionally — the guess stands as written (the documented residue). */ GlobalQueue.
|
|
269
|
+
* positionally — the guess stands as written (the documented residue). */ GlobalQueue._n = (e, n) => {
|
|
237
270
|
const t = e.yt;
|
|
238
271
|
if (t === undefined || e.bt !== $TRACK) return;
|
|
239
|
-
const i = e.m.
|
|
272
|
+
const i = e.m.B;
|
|
240
273
|
if (!(i instanceof LaneView)) return;
|
|
241
274
|
if (!Array.isArray(n) || !Array.isArray(i.rows)) {
|
|
242
275
|
i.base = n;
|
|
@@ -262,15 +295,15 @@ function arrangementEqual(e, n, t) {
|
|
|
262
295
|
s.add(t);
|
|
263
296
|
} else a.push(n);
|
|
264
297
|
}
|
|
265
|
-
const
|
|
298
|
+
const u = i.removed;
|
|
266
299
|
for (let e = 0; e < n.length; e++) {
|
|
267
300
|
const t = n[e];
|
|
268
301
|
const i = isWrappable(t) ? r(t) : undefined;
|
|
269
|
-
if (i !== undefined && !s.has(i) && (
|
|
302
|
+
if (i !== undefined && !s.has(i) && (u === null || !u.has(i))) a.push(t);
|
|
270
303
|
}
|
|
271
304
|
i.rows = a;
|
|
272
305
|
i.base = a;
|
|
273
|
-
const
|
|
306
|
+
const l = txOf(e);
|
|
274
307
|
const c = committed(t);
|
|
275
308
|
const d = t.n;
|
|
276
309
|
for (let e = 0; e < a.length; e++) {
|
|
@@ -279,17 +312,17 @@ function arrangementEqual(e, n, t) {
|
|
|
279
312
|
// lane's only where the composed row is another row (by key — the same
|
|
280
313
|
// key's new object is the same row, its target adopted it).
|
|
281
314
|
if (n === undefined) {
|
|
282
|
-
if (!sameRow(c[e], a[e], r)) laneSet(getNode(t, e), a[e],
|
|
283
|
-
} else if (!sameRow(n.
|
|
315
|
+
if (!sameRow(c[e], a[e], r)) laneSet(getNode(t, e), a[e], l);
|
|
316
|
+
} else if (!sameRow(n.i & CONFIG_OVERRIDE ? n.m.B : c[e], a[e], r)) laneSet(n, a[e], l);
|
|
284
317
|
}
|
|
285
318
|
if (d !== null) {
|
|
286
319
|
for (let e = a.length; ;e++) {
|
|
287
320
|
const n = d[e];
|
|
288
321
|
if (n === undefined) break;
|
|
289
|
-
laneSet(n, undefined,
|
|
322
|
+
laneSet(n, undefined, l);
|
|
290
323
|
}
|
|
291
324
|
const e = d.length;
|
|
292
|
-
if (e !== undefined && (e.
|
|
325
|
+
if (e !== undefined && (e.i & CONFIG_OVERRIDE || c.length !== a.length)) laneSet(e, a.length, l);
|
|
293
326
|
}
|
|
294
327
|
};
|
|
295
328
|
|
|
@@ -304,11 +337,11 @@ function sameRow(e, n, t) {
|
|
|
304
337
|
* node that was not the lane's becomes its derivation (its committed value
|
|
305
338
|
* untouched beneath; the lane's end commits the latest). */ function laneSet(e, n, t) {
|
|
306
339
|
const i = ext(e);
|
|
307
|
-
if (!(e.
|
|
308
|
-
e.
|
|
340
|
+
if (!(e.i & CONFIG_OVERRIDE)) {
|
|
341
|
+
e.i |= CONFIG_OVERRIDE;
|
|
309
342
|
list(e, t);
|
|
310
|
-
} else if (i.
|
|
311
|
-
i.
|
|
343
|
+
} else if (i.B === n) return;
|
|
344
|
+
i.B = n;
|
|
312
345
|
if (e.t !== null) {
|
|
313
346
|
insertSubs(e);
|
|
314
347
|
laneDirty(e, t);
|
|
@@ -354,7 +387,7 @@ function sweepOverlaid() {
|
|
|
354
387
|
// The container shows its backing again (a shown arrangement guess
|
|
355
388
|
// that ended without a landing left its `LaneView` as the value).
|
|
356
389
|
const t = e.k;
|
|
357
|
-
if (t !== null && t.
|
|
390
|
+
if (t !== null && t.Ae instanceof LaneView) t.Ae = t.Ae.base;
|
|
358
391
|
}
|
|
359
392
|
if (n.size === 0) overlaidFamilies.delete(e);
|
|
360
393
|
}
|
|
@@ -371,8 +404,8 @@ function sweepOverlaid() {
|
|
|
371
404
|
if (i !== null) for (const e of Reflect.ownKeys(i)) {
|
|
372
405
|
const r = i[e];
|
|
373
406
|
const o = untrack(() => unwrapValue(n[e]));
|
|
374
|
-
if (r.
|
|
375
|
-
r.
|
|
407
|
+
if (r.en && r.en(r.Ae, o)) continue;
|
|
408
|
+
r.Ae = o;
|
|
376
409
|
// (The lane's end notified it this epoch when the shown value and the
|
|
377
410
|
// covered one differed — its readers re-derive already.)
|
|
378
411
|
if (r.t !== null && r.Pt !== notifyEpoch) {
|
|
@@ -384,8 +417,8 @@ function sweepOverlaid() {
|
|
|
384
417
|
if (r !== null) for (const e of Reflect.ownKeys(r)) {
|
|
385
418
|
const i = r[e];
|
|
386
419
|
const o = untrack(() => e in n);
|
|
387
|
-
if (i.
|
|
388
|
-
i.
|
|
420
|
+
if (i.Ae === o) continue;
|
|
421
|
+
i.Ae = o;
|
|
389
422
|
if (i.t !== null && i.Pt !== notifyEpoch) {
|
|
390
423
|
insertSubs(i);
|
|
391
424
|
t = true;
|
|
@@ -394,9 +427,9 @@ function sweepOverlaid() {
|
|
|
394
427
|
// The arrangement the readers saw (the shown guess) against the inner's
|
|
395
428
|
// now: a structural reader re-derives when they differ.
|
|
396
429
|
const o = e.k;
|
|
397
|
-
if (o !== null && o.t !== null && o.
|
|
430
|
+
if (o !== null && o.t !== null && o.Ae instanceof LaneView && o.Pt !== notifyEpoch) {
|
|
398
431
|
const i = Array.isArray(n) ? untrack(() => n.map(unwrapValue)) : n;
|
|
399
|
-
if (!arrangementEqual(o.
|
|
432
|
+
if (!arrangementEqual(o.Ae.rows, i, e.fam?.key ?? null)) {
|
|
400
433
|
insertSubs(o);
|
|
401
434
|
t = true;
|
|
402
435
|
}
|
|
@@ -405,12 +438,12 @@ function sweepOverlaid() {
|
|
|
405
438
|
}
|
|
406
439
|
|
|
407
440
|
function carriesLane(e) {
|
|
408
|
-
if (e.k !== null && e.k.
|
|
409
|
-
if (e.dk !== null && e.dk.
|
|
441
|
+
if (e.k !== null && e.k.i & CONFIG_OVERRIDE) return true;
|
|
442
|
+
if (e.dk !== null && e.dk.i & CONFIG_OVERRIDE) return true;
|
|
410
443
|
const n = e.n;
|
|
411
|
-
if (n !== null) for (const e of Reflect.ownKeys(n)) if (n[e].
|
|
444
|
+
if (n !== null) for (const e of Reflect.ownKeys(n)) if (n[e].i & CONFIG_OVERRIDE) return true;
|
|
412
445
|
const t = e.h;
|
|
413
|
-
if (t !== null) for (const e of Reflect.ownKeys(t)) if (t[e].
|
|
446
|
+
if (t !== null) for (const e of Reflect.ownKeys(t)) if (t[e].i & CONFIG_OVERRIDE) return true;
|
|
414
447
|
return false;
|
|
415
448
|
}
|
|
416
449
|
|
|
@@ -428,9 +461,9 @@ function overlaid(e) {
|
|
|
428
461
|
* the backing answers. */ function optimisticHas(e, n) {
|
|
429
462
|
if (!overlaid(e)) return undefined;
|
|
430
463
|
const t = e.h?.[n];
|
|
431
|
-
if (t !== undefined && t.
|
|
464
|
+
if (t !== undefined && t.i & CONFIG_OVERRIDE) return !!shown(t);
|
|
432
465
|
const i = e.n?.[n];
|
|
433
|
-
if (i !== undefined && i.
|
|
466
|
+
if (i !== undefined && i.i & CONFIG_OVERRIDE) return shown(i) !== undefined;
|
|
434
467
|
return undefined;
|
|
435
468
|
}
|
|
436
469
|
|
|
@@ -441,7 +474,7 @@ function overlaid(e) {
|
|
|
441
474
|
if (t === undefined) return undefined;
|
|
442
475
|
if (!t) return null;
|
|
443
476
|
const i = e.n?.[n];
|
|
444
|
-
const r = i !== undefined && i.
|
|
477
|
+
const r = i !== undefined && i.i & CONFIG_OVERRIDE ? shown(i) : e.v[n];
|
|
445
478
|
// An array's `length` is reported as the array has it (non-configurable).
|
|
446
479
|
if (n === "length" && Array.isArray(e.v)) return {
|
|
447
480
|
value: r,
|
|
@@ -474,7 +507,7 @@ i = false) {
|
|
|
474
507
|
for (const e of Reflect.ownKeys(o)) {
|
|
475
508
|
const r = o[e];
|
|
476
509
|
let s;
|
|
477
|
-
if (r.
|
|
510
|
+
if (r.i & CONFIG_OVERRIDE) s = t(r); else if (!i || (s = pendingGuessOf(r)) === NOT_PENDING) continue;
|
|
478
511
|
if (e === "length" && Array.isArray(n)) {
|
|
479
512
|
if (n.length !== s) ensure().length = s;
|
|
480
513
|
} else if (!isEqual(n[e], s) || !hasOwn.call(n, e)) {
|
|
@@ -487,10 +520,10 @@ i = false) {
|
|
|
487
520
|
for (const e of Reflect.ownKeys(s)) {
|
|
488
521
|
const o = s[e];
|
|
489
522
|
let a;
|
|
490
|
-
if (o.
|
|
523
|
+
if (o.i & CONFIG_OVERRIDE) a = t(o); else if (!i || (a = pendingGuessOf(o)) === NOT_PENDING) continue;
|
|
491
524
|
const f = !!a;
|
|
492
|
-
const
|
|
493
|
-
if (!f && e in
|
|
525
|
+
const u = r ?? n;
|
|
526
|
+
if (!f && e in u) delete ensure()[e];
|
|
494
527
|
}
|
|
495
528
|
}
|
|
496
529
|
return r ?? n;
|
|
@@ -529,7 +562,7 @@ function createOptimisticStore(e, n, t) {
|
|
|
529
562
|
if (!a.node) a.node = getOwner();
|
|
530
563
|
runProjectionComputed(f, n, s);
|
|
531
564
|
}, t);
|
|
532
|
-
i.
|
|
565
|
+
i.i &= ~CONFIG_AUTO_DISPOSE;
|
|
533
566
|
a.node = i;
|
|
534
567
|
}
|
|
535
568
|
return [ f, e => storeSetter(f, e) ];
|