@solidjs/signals 2.0.0-rc.2 → 2.0.0-rc.20
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 +6505 -0
- package/dist/dev.attribution.js +3484 -0
- package/dist/dev.js +10227 -9508
- package/dist/observe/affects-marks.js +86 -0
- package/dist/observe/affects.js +112 -0
- package/dist/observe/attribution.js +7 -0
- package/dist/observe/boundaries.js +696 -0
- package/dist/observe/core/action.js +182 -0
- package/dist/observe/core/async.js +824 -0
- package/dist/observe/core/attribution-costs.js +66 -0
- package/dist/observe/core/attribution-feedback.js +280 -0
- package/dist/observe/core/attribution-hooks.js +83 -0
- package/dist/observe/core/attribution-queries.js +43 -0
- package/dist/observe/core/attribution.js +3079 -0
- package/dist/observe/core/constants.js +269 -0
- package/dist/observe/core/context.js +63 -0
- package/dist/observe/core/copies.js +10 -0
- package/dist/observe/core/core.js +1707 -0
- package/dist/observe/core/dev.js +394 -0
- package/dist/observe/core/effect.js +193 -0
- package/dist/observe/core/error-hooks.js +71 -0
- package/dist/observe/core/error.js +82 -0
- package/dist/observe/core/external.js +97 -0
- package/dist/observe/core/graph.js +162 -0
- package/dist/observe/core/heap.js +124 -0
- package/dist/observe/core/identities.js +62 -0
- package/dist/observe/core/invariants.js +12 -0
- package/dist/observe/core/lanes.js +972 -0
- package/dist/observe/core/owner.js +371 -0
- package/dist/observe/core/scheduler.js +1157 -0
- package/dist/observe/core/truth.js +47 -0
- package/dist/observe/core/verdict.js +470 -0
- package/dist/observe/deferred.js +186 -0
- package/dist/observe/flatten.js +79 -0
- package/dist/observe/from.js +248 -0
- package/dist/observe/index.js +71 -0
- package/dist/observe/map.js +593 -0
- package/dist/observe/reveal.js +212 -0
- package/dist/observe/signals.js +717 -0
- package/dist/observe/store/affects.js +142 -0
- package/dist/observe/store/edges.js +39 -0
- package/dist/observe/store/hooks.js +16 -0
- package/dist/observe/store/index.js +47 -0
- package/dist/observe/store/optimistic.js +713 -0
- package/dist/observe/store/projection.js +345 -0
- package/dist/observe/store/reconcile.js +281 -0
- package/dist/observe/store/store.js +2127 -0
- package/dist/observe/store/storePath.js +103 -0
- package/dist/observe/store/target.js +46 -0
- package/dist/observe/store/types.js +83 -0
- package/dist/observe/store/utils.js +1063 -0
- package/dist/prod/affects-marks.js +86 -0
- package/dist/prod/affects.js +98 -112
- package/dist/prod/attribution.js +45 -0
- package/dist/prod/boundaries.js +578 -468
- package/dist/prod/core/action.js +92 -59
- package/dist/prod/core/async.js +522 -336
- package/dist/prod/core/attribution-hooks.js +3 -0
- package/dist/prod/core/constants.js +168 -44
- package/dist/prod/core/context.js +12 -16
- package/dist/prod/core/copies.js +10 -0
- package/dist/prod/core/core.js +1345 -627
- package/dist/prod/core/dev.js +123 -1
- package/dist/prod/core/effect.js +113 -80
- package/dist/prod/core/error-hooks.js +71 -0
- package/dist/prod/core/error.js +18 -4
- package/dist/prod/core/external.js +6 -7
- package/dist/prod/core/graph.js +119 -72
- package/dist/prod/core/heap.js +85 -109
- package/dist/prod/core/identities.js +62 -0
- package/dist/prod/core/invariants.js +1 -32
- package/dist/prod/core/lanes.js +931 -132
- package/dist/prod/core/owner.js +192 -129
- package/dist/prod/core/scheduler.js +948 -642
- package/dist/prod/core/truth.js +47 -0
- package/dist/prod/core/verdict.js +409 -420
- package/dist/prod/deferred.js +184 -0
- package/dist/prod/flatten.js +77 -0
- package/dist/prod/from.js +248 -0
- package/dist/prod/index.js +40 -16
- package/dist/prod/map.js +481 -211
- package/dist/prod/reveal.js +212 -0
- package/dist/prod/signals.js +401 -84
- package/dist/prod/store/affects.js +142 -0
- package/dist/prod/store/edges.js +39 -0
- package/dist/prod/store/hooks.js +16 -0
- package/dist/prod/store/index.js +27 -18
- package/dist/prod/store/optimistic.js +707 -0
- package/dist/prod/store/projection.js +331 -0
- package/dist/prod/store/reconcile.js +281 -0
- package/dist/prod/store/store.js +1952 -267
- package/dist/prod/store/storePath.js +1 -1
- package/dist/prod/store/target.js +43 -0
- package/dist/prod/store/types.js +83 -0
- package/dist/prod/store/utils.js +997 -138
- package/dist/types/affects-marks.d.ts +14 -0
- package/dist/types/affects.d.ts +12 -30
- package/dist/types/attribution.d.ts +19 -0
- package/dist/types/attribution.prod.d.ts +22 -0
- package/dist/types/boundaries.d.ts +99 -124
- package/dist/types/carved.d.ts +1 -0
- package/dist/types/core/action.d.ts +23 -9
- package/dist/types/core/async.d.ts +34 -6
- package/dist/types/core/attribution-costs.d.ts +35 -0
- package/dist/types/core/attribution-feedback.d.ts +133 -0
- package/dist/types/core/attribution-hooks.d.ts +307 -8
- package/dist/types/core/attribution-queries.d.ts +24 -0
- package/dist/types/core/attribution.d.ts +802 -72
- package/dist/types/core/constants.d.ts +195 -38
- package/dist/types/core/copies.d.ts +54 -0
- package/dist/types/core/core.d.ts +295 -65
- package/dist/types/core/dev.d.ts +509 -22
- package/dist/types/core/effect.d.ts +1 -1
- package/dist/types/core/error-hooks.d.ts +71 -0
- package/dist/types/core/error.d.ts +18 -1
- package/dist/types/core/graph.d.ts +40 -1
- package/dist/types/core/heap.d.ts +5 -5
- package/dist/types/core/identities.d.ts +17 -0
- package/dist/types/core/index.d.ts +11 -7
- package/dist/types/core/invariants.d.ts +1 -26
- package/dist/types/core/lanes.d.ts +74 -43
- package/dist/types/core/owner.d.ts +27 -9
- package/dist/types/core/scheduler.d.ts +353 -102
- package/dist/types/core/truth.d.ts +18 -0
- package/dist/types/core/types.d.ts +97 -71
- package/dist/types/core/verdict.d.ts +35 -0
- package/dist/types/deferred.d.ts +28 -0
- package/dist/types/flatten.d.ts +8 -0
- package/dist/types/from.d.ts +47 -0
- package/dist/types/index.d.ts +33 -7
- package/dist/types/map.d.ts +4 -2
- package/dist/types/reveal.d.ts +19 -0
- package/dist/types/signals.d.ts +196 -45
- package/dist/types/store/affects.d.ts +12 -0
- package/dist/types/store/edges.d.ts +9 -0
- package/dist/types/store/hooks.d.ts +62 -0
- package/dist/types/store/index.d.ts +42 -15
- package/dist/types/store/optimistic.d.ts +24 -45
- package/dist/types/store/projection.d.ts +17 -68
- package/dist/types/store/reconcile.d.ts +3 -46
- package/dist/types/store/store.d.ts +133 -136
- package/dist/types/store/storePath.d.ts +1 -1
- package/dist/types/store/target.d.ts +166 -0
- package/dist/types/store/types.d.ts +105 -0
- package/dist/types/store/utils.d.ts +188 -0
- package/package.json +31 -26
- package/dist/node.cjs +0 -8745
- package/dist/prod/core/optimistic.js +0 -265
- package/dist/prod/store/next/optimistic.js +0 -385
- package/dist/prod/store/next/projection.js +0 -172
- package/dist/prod/store/next/reconcile.js +0 -331
- package/dist/prod/store/next/store.js +0 -1499
- package/dist/prod/store/next/target.js +0 -20
- package/dist/types/core/optimistic.d.ts +0 -6
- package/dist/types/store/next/optimistic.d.ts +0 -20
- package/dist/types/store/next/projection.d.ts +0 -8
- package/dist/types/store/next/reconcile.d.ts +0 -3
- package/dist/types/store/next/store.d.ts +0 -77
- package/dist/types/store/next/target.d.ts +0 -117
- package/dist/types-cjs/affects.d.cts +0 -47
- package/dist/types-cjs/boundaries.d.cts +0 -175
- package/dist/types-cjs/core/action.d.cts +0 -64
- package/dist/types-cjs/core/async.d.cts +0 -21
- package/dist/types-cjs/core/attribution-hooks.d.cts +0 -52
- package/dist/types-cjs/core/attribution.d.cts +0 -186
- package/dist/types-cjs/core/constants.d.cts +0 -97
- package/dist/types-cjs/core/context.d.cts +0 -36
- package/dist/types-cjs/core/core.d.cts +0 -163
- package/dist/types-cjs/core/dev.d.cts +0 -81
- package/dist/types-cjs/core/effect.d.cts +0 -30
- package/dist/types-cjs/core/error.d.cts +0 -47
- package/dist/types-cjs/core/external.d.cts +0 -15
- package/dist/types-cjs/core/graph.d.cts +0 -6
- package/dist/types-cjs/core/heap.d.cts +0 -22
- package/dist/types-cjs/core/index.d.cts +0 -13
- package/dist/types-cjs/core/invariants.d.cts +0 -59
- package/dist/types-cjs/core/lanes.d.cts +0 -44
- package/dist/types-cjs/core/optimistic.d.cts +0 -6
- package/dist/types-cjs/core/owner.d.cts +0 -124
- package/dist/types-cjs/core/scheduler.d.cts +0 -180
- package/dist/types-cjs/core/types.d.cts +0 -192
- package/dist/types-cjs/core/verdict.d.cts +0 -2
- package/dist/types-cjs/index.d.cts +0 -10
- package/dist/types-cjs/map.d.cts +0 -69
- package/dist/types-cjs/package.json +0 -3
- package/dist/types-cjs/signals.d.cts +0 -624
- package/dist/types-cjs/store/index.d.cts +0 -19
- package/dist/types-cjs/store/next/optimistic.d.cts +0 -20
- package/dist/types-cjs/store/next/projection.d.cts +0 -8
- package/dist/types-cjs/store/next/reconcile.d.cts +0 -3
- package/dist/types-cjs/store/next/store.d.cts +0 -77
- package/dist/types-cjs/store/next/target.d.cts +0 -117
- package/dist/types-cjs/store/optimistic.d.cts +0 -45
- package/dist/types-cjs/store/projection.d.cts +0 -70
- package/dist/types-cjs/store/reconcile.d.cts +0 -46
- package/dist/types-cjs/store/store.d.cts +0 -138
- package/dist/types-cjs/store/storePath.d.cts +0 -58
- package/dist/types-cjs/store/utils.d.cts +0 -74
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import { CONFIG_AUTO_DISPOSE, STATUS_PENDING, STATUS_ERROR, STATUS_UNINITIALIZED } from "../core/constants.js";
|
|
2
|
+
|
|
3
|
+
import { computed } from "../core/core.js";
|
|
4
|
+
|
|
5
|
+
import { NotReadyError } from "../core/error.js";
|
|
6
|
+
|
|
7
|
+
import { handleAsync, addPendingSource, propagateStatus, forEachDependent, settlePendingSource } from "../core/async.js";
|
|
8
|
+
|
|
9
|
+
import { enqueueSub } from "../core/heap.js";
|
|
10
|
+
|
|
11
|
+
import { getOwner, isDisposed } from "../core/owner.js";
|
|
12
|
+
|
|
13
|
+
import { question, setQuestion, insertSubs, schedule } from "../core/scheduler.js";
|
|
14
|
+
|
|
15
|
+
import { optHooks } from "./hooks.js";
|
|
16
|
+
|
|
17
|
+
import { reconcileState } from "./reconcile.js";
|
|
18
|
+
|
|
19
|
+
import { wrap, storeSetter, runDirect } from "./store.js";
|
|
20
|
+
|
|
21
|
+
import { setLookup } from "./target.js";
|
|
22
|
+
|
|
23
|
+
import { $TARGET, markRawIngest, isWrappable, setWriteOverride } from "./types.js";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Store — projections (plan sec. 31, S3; INTERNALS §7/§7b).
|
|
27
|
+
*
|
|
28
|
+
* A projection is a computed store: the derive runs inside a computed — the
|
|
29
|
+
* **firewall** — whose pass writes the store through a draft; its output is
|
|
30
|
+
* adopted through the reconcile channel (replace-mode root: entity changes
|
|
31
|
+
* merge in place, the root proxy is stable for life). Children wrap into
|
|
32
|
+
* the projection's own FAMILY (writes land here, never in a source family).
|
|
33
|
+
*
|
|
34
|
+
* The firewall exists so writes from inside a computation are safe (the
|
|
35
|
+
* derive owns its leaves); the price is the ordering obligation: before any
|
|
36
|
+
* value is served through the projection, the derive is brought up to date
|
|
37
|
+
* — **without** the reader subscribing to it (that is the propagation
|
|
38
|
+
* barrier). The store's traps do that pull (`pullFamily`, store.ts): core's
|
|
39
|
+
* `read()` of the firewall with no link — freshness, the status gate
|
|
40
|
+
* (uninitialized / pending / errored) and the reader's height, by the
|
|
41
|
+
* rules a memo's read follows. No per-leaf back-pointer, nothing in core.
|
|
42
|
+
*/
|
|
43
|
+
/**
|
|
44
|
+
* Wrap a store proxy as a projection DRAFT: every operation carries the write
|
|
45
|
+
* override (the derive is the author — its ops must not hit the status gate,
|
|
46
|
+
* even in a continuation after an `await`/`yield` where the sync write scope
|
|
47
|
+
* has closed).
|
|
48
|
+
*
|
|
49
|
+
* FAKE TARGET, not the store proxy itself (#3060): after a proxy trap
|
|
50
|
+
* returns, the engine runs spec invariant validation against the proxy's
|
|
51
|
+
* TARGET — [[OwnPropertyKeys]] after ownKeys, [[GetOwnProperty]] after
|
|
52
|
+
* set/getOwnPropertyDescriptor/defineProperty. With the store proxy as
|
|
53
|
+
* target those checks re-enter the store's traps OUTSIDE the override
|
|
54
|
+
* bracket, so `Object.keys(state)` in a derive continuation fired the gate.
|
|
55
|
+
*/ function wrapDraft(e, t, r, o, n, i) {
|
|
56
|
+
const mutate = e => {
|
|
57
|
+
if (!t()) return true;
|
|
58
|
+
setWriteOverride(true);
|
|
59
|
+
// A continuation's write is the flight's answer: it asks the flight's
|
|
60
|
+
// question (A18 provenance — a landing beneath a newer guess is held,
|
|
61
|
+
// not a correction; lanes.ts `laneWrite`).
|
|
62
|
+
const o = question;
|
|
63
|
+
if (i !== undefined) setQuestion(i());
|
|
64
|
+
try {
|
|
65
|
+
r ? r(e) : e();
|
|
66
|
+
} finally {
|
|
67
|
+
setWriteOverride(false);
|
|
68
|
+
setQuestion(o);
|
|
69
|
+
}
|
|
70
|
+
if (n) n();
|
|
71
|
+
return true;
|
|
72
|
+
};
|
|
73
|
+
const read = e => {
|
|
74
|
+
setWriteOverride(true);
|
|
75
|
+
try {
|
|
76
|
+
return e();
|
|
77
|
+
} finally {
|
|
78
|
+
setWriteOverride(false);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
const s = {
|
|
82
|
+
get(s, c) {
|
|
83
|
+
const u = read(() => e[c]);
|
|
84
|
+
return !o && typeof u === "object" && u !== null && c !== $TARGET ? wrapDraft(u, t, r, false, n, i) : u;
|
|
85
|
+
},
|
|
86
|
+
has: (t, r) => read(() => r in e),
|
|
87
|
+
set: (t, r, o) => mutate(() => {
|
|
88
|
+
e[r] = o;
|
|
89
|
+
}),
|
|
90
|
+
deleteProperty: (t, r) => mutate(() => {
|
|
91
|
+
delete e[r];
|
|
92
|
+
}),
|
|
93
|
+
ownKeys: () => read(() => Reflect.ownKeys(e)),
|
|
94
|
+
getOwnPropertyDescriptor(t, r) {
|
|
95
|
+
const o = read(() => Reflect.getOwnPropertyDescriptor(e, r));
|
|
96
|
+
if (o) o.configurable = true;
|
|
97
|
+
return o;
|
|
98
|
+
},
|
|
99
|
+
defineProperty: (t, r, o) => mutate(() => {
|
|
100
|
+
Reflect.defineProperty(e, r, o);
|
|
101
|
+
})
|
|
102
|
+
};
|
|
103
|
+
const c = new Proxy(Array.isArray(e) ? [] : {}, s);
|
|
104
|
+
// The wrapper stands for its store proxy wherever the derive's result
|
|
105
|
+
// carries it (`wrap`, `unwrapValue`, reconcile's slot matching).
|
|
106
|
+
const u = e[$TARGET];
|
|
107
|
+
if (u !== undefined) setLookup(c, u);
|
|
108
|
+
return c;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function cloneState(e, t) {
|
|
112
|
+
return t ? Array.isArray(e) ? e.slice() : {
|
|
113
|
+
...e
|
|
114
|
+
} : JSON.parse(JSON.stringify(e));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** The derive's pass went pending (a new question in flight): every reader
|
|
118
|
+
* of a leaf learns it as a reader of the derive would (A9: any leaf reports
|
|
119
|
+
* the firewall's refetch) — core's own rule for a dependency going pending
|
|
120
|
+
* (`notifyStatus`): a verdict or re-derive reader re-runs, any other is
|
|
121
|
+
* pending derivatively on the derive (its readers hold; it re-runs at the
|
|
122
|
+
* landing). The leaves do not subscribe to the derive, so the family does
|
|
123
|
+
* the walk. */ function wakeFamily(e) {
|
|
124
|
+
const t = e.node;
|
|
125
|
+
// Pending on the derive itself, whatever source took it there (an upstream
|
|
126
|
+
// memo's flight reaches it as a NotReady of that memo, #4271): the leaves
|
|
127
|
+
// are not that source's dependents, so only the family's settle — which
|
|
128
|
+
// retires the derive (`settleFamily`) — reaches the readers parked here. A
|
|
129
|
+
// reader the landing leaves unchanged does not re-run to clear it (#4305).
|
|
130
|
+
const r = new NotReadyError(t);
|
|
131
|
+
e.woke = true;
|
|
132
|
+
optHooks?.ask(e);
|
|
133
|
+
forEachFamilyNode(e, e => {
|
|
134
|
+
if (e.o === null) return;
|
|
135
|
+
// The leaf carries the derive's pending as a propagated copy, as a memo
|
|
136
|
+
// between them would: a dependent recovering one branch stays pending
|
|
137
|
+
// through a leaf it still reads (core `retryReaches`), and the landing's
|
|
138
|
+
// walk retires the copy first (`settleFamily` → `settlePendingSource`).
|
|
139
|
+
addPendingSource(e, t);
|
|
140
|
+
// Its readers: core's propagation from the leaf — a verdict or re-derive
|
|
141
|
+
// reader re-runs, any other is pending derivatively on the derive and
|
|
142
|
+
// queued for the frame (held with it: the frame waits on the flight
|
|
143
|
+
// through them, `blocked`).
|
|
144
|
+
propagateStatus(e, STATUS_PENDING, r);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** The derive's flight rejected: every live reader of the family
|
|
149
|
+
* re-derives (its pull throws the derive's error). */ function errorFamily(e) {
|
|
150
|
+
forEachFamilyNode(e, e => {
|
|
151
|
+
if (e.o === null) return;
|
|
152
|
+
forEachDependent(e, e => {
|
|
153
|
+
enqueueSub(e);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
schedule();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** The flight landed: the readers the wake left pending on the derive
|
|
160
|
+
* settle — core's walk, from each leaf (the leaves are not the derive's
|
|
161
|
+
* dependents, so its own settle walk does not reach them). */ function settleFamily(e) {
|
|
162
|
+
if (!e.woke) return;
|
|
163
|
+
e.woke = false;
|
|
164
|
+
const t = e.node;
|
|
165
|
+
forEachFamilyNode(e, e => {
|
|
166
|
+
if (e.o !== null) settlePendingSource(e, t);
|
|
167
|
+
});
|
|
168
|
+
// The landing is the derive's change for a reader parked on the derive
|
|
169
|
+
// itself, whatever status the landing pass began from (an errored derive
|
|
170
|
+
// re-asked: a reader that met the error has no leaf to learn it from).
|
|
171
|
+
insertSubs(t);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** A source's status reached the derive by propagation (core
|
|
175
|
+
* `propagateStatus`), its pass not re-run: the family learns it as from the
|
|
176
|
+
* pass's own throw. */ function familyStatus(e) {
|
|
177
|
+
if (e & STATUS_PENDING) wakeFamily(this); else if (e & STATUS_ERROR) errorFamily(this);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function forEachFamilyNode(e, t) {
|
|
181
|
+
for (const r of e.live) {
|
|
182
|
+
const e = r.n;
|
|
183
|
+
if (e !== null) for (const r of Reflect.ownKeys(e)) t(e[r]);
|
|
184
|
+
const o = r.h;
|
|
185
|
+
if (o !== null) for (const e of Reflect.ownKeys(o)) t(o[e]);
|
|
186
|
+
if (r.k !== null) t(r.k);
|
|
187
|
+
if (r.dk !== null) t(r.dk);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** The derive's pass: run `fn` against a draft of the store; commit its
|
|
192
|
+
* result (sync, or each async landing) through the reconcile channel.
|
|
193
|
+
* Draft validity is per run (R37): live from here until the NEXT run starts
|
|
194
|
+
* — a sync derive that subscribes to an external source and pushes into
|
|
195
|
+
* the draft from the callback (#3585) is the model use. */ function runProjectionComputed(e, t, r, o, n) {
|
|
196
|
+
const i = getOwner();
|
|
197
|
+
const s = e[$TARGET];
|
|
198
|
+
const c = s.fam;
|
|
199
|
+
const u = c.run = (c.run || 0) + 1;
|
|
200
|
+
let l;
|
|
201
|
+
// Open loading window (seedLoadingValue): the observable store IS commit #0
|
|
202
|
+
// for the whole first flight — the derive works a detached shadow of the
|
|
203
|
+
// seed so draft writes cannot tear through to readers (#2988). Every
|
|
204
|
+
// commit point reconciles the shadow through the normal commit path.
|
|
205
|
+
const a = i.ce ? cloneState(s.v, s.s) : null;
|
|
206
|
+
// The question a write of this run answers: inside a slice, the slice's
|
|
207
|
+
// (the sync pass); in a continuation — the flight still registered — the
|
|
208
|
+
// flight's (`handleAsync` stamped it with the action that asked).
|
|
209
|
+
const asking = () => question || (i.T !== null && i.T.Ee !== null ? i.T.Ie : 0);
|
|
210
|
+
const f = wrapDraft(e, () => c.run === u && !isDisposed(i), n, s.s,
|
|
211
|
+
// A write after the run returned (a continuation, a callback) arms the
|
|
212
|
+
// drain itself when no landing will.
|
|
213
|
+
() => {
|
|
214
|
+
if (!(i.m & STATUS_PENDING) && !i.ce) schedule();
|
|
215
|
+
}, asking);
|
|
216
|
+
// The creation run commits directly (a memo's first value is its
|
|
217
|
+
// `_value`); every later run — a re-derive in a flush, an async landing
|
|
218
|
+
// — stages and commits with the flush like a memo's recompute.
|
|
219
|
+
const d = u === 1;
|
|
220
|
+
const pass = () => storeSetter(f, n => {
|
|
221
|
+
l = t(a ?? n);
|
|
222
|
+
const commit = (t, u = 1) => {
|
|
223
|
+
// Shadow run: commit a detached snapshot, never the shadow itself
|
|
224
|
+
// (adoption takes the value by identity — handing it the live
|
|
225
|
+
// shadow would fuse the draft to the observable store).
|
|
226
|
+
if (a && (t === undefined || t === a)) t = cloneState(a, s.s);
|
|
227
|
+
if (t !== n && t !== undefined) {
|
|
228
|
+
const write = () => {
|
|
229
|
+
const o = question;
|
|
230
|
+
setQuestion(asking());
|
|
231
|
+
try {
|
|
232
|
+
storeSetter(e, e => reconcileState(t, e, r, true), false);
|
|
233
|
+
} finally {
|
|
234
|
+
setQuestion(o);
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
o ? o(write, t) : write();
|
|
238
|
+
}
|
|
239
|
+
// A landing (not the sync pass's own commit): the readers held
|
|
240
|
+
// pending on the derive settle.
|
|
241
|
+
if (!d || !(i.m & STATUS_UNINITIALIZED)) settleFamily(c);
|
|
242
|
+
// A commit is the derive's change for the readers that observed its
|
|
243
|
+
// flight (linked by `pullFamily`; a settled pull does not link), as
|
|
244
|
+
// a memo's landing is for its readers: one that caught the NotReady
|
|
245
|
+
// (a fallback value, not pending) re-runs too (#4070). A pass of a
|
|
246
|
+
// settled derive has none (`wake` 0): a link left is a stale one a
|
|
247
|
+
// held frame keeps (A30), from a reader that re-ran since. Woken by
|
|
248
|
+
// every pass, a reader feeding a key the pass peeks at looped (#4342).
|
|
249
|
+
if (u) insertSubs(i);
|
|
250
|
+
};
|
|
251
|
+
const u = handleAsync(i, l, commit, (e, t) => {
|
|
252
|
+
// The flight rejected: the leaves' readers learn it from here —
|
|
253
|
+
// they do not subscribe to the derive. Pending again (a NotReady
|
|
254
|
+
// rejection): the wake, as for the pass. Errored: every live
|
|
255
|
+
// reader re-derives and meets the error at its pull (memo parity,
|
|
256
|
+
// #2897) — and the flush that runs them advances the clock, so a
|
|
257
|
+
// later tracked re-read may retry (core `read`).
|
|
258
|
+
if (d && i.m & STATUS_UNINITIALIZED) return;
|
|
259
|
+
if (t) wakeFamily(c); else errorFamily(c);
|
|
260
|
+
});
|
|
261
|
+
// (The status is the one before this pass: `recompute` sets it after.)
|
|
262
|
+
if (!i.ce) commit(u, i.m & (STATUS_PENDING | STATUS_ERROR));
|
|
263
|
+
}, false);
|
|
264
|
+
try {
|
|
265
|
+
d ? runDirect(pass) : pass();
|
|
266
|
+
} catch (e) {
|
|
267
|
+
// A flight went up (NotReady out of the pass — the derive's own, with
|
|
268
|
+
// the derive as its source): the leaves' readers learn it from here —
|
|
269
|
+
// they do not subscribe to the derive.
|
|
270
|
+
// A synchronous error the same way (#3956: a rerun that finds the
|
|
271
|
+
// entry failed throws): every live reader re-derives and meets it.
|
|
272
|
+
if (!d) e instanceof NotReadyError ? wakeFamily(c) : errorFamily(c);
|
|
273
|
+
throw e;
|
|
274
|
+
}
|
|
275
|
+
// A pass that returned with a flight up (an iterator's sync first yield):
|
|
276
|
+
// the wake, as for the throw. Not a sync pass that superseded one — the
|
|
277
|
+
// status it reads is the dead flight's, cleared when this pass settles
|
|
278
|
+
// (#3181: a reader the settle walk had just released, re-parked for good).
|
|
279
|
+
if (i.m & STATUS_PENDING && i.T.Ee !== null && !i.ce && !d) wakeFamily(c);
|
|
280
|
+
return i;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function createProjectionInternal(e, t, r) {
|
|
284
|
+
// Seedless (`fn` takes no draft): the root starts as an empty record. It is
|
|
285
|
+
// a draft like any seed, never observable before the first landing (A25).
|
|
286
|
+
t ||= {};
|
|
287
|
+
const o = r?.key === undefined ? "id" : r.key;
|
|
288
|
+
const n = {
|
|
289
|
+
map: new WeakMap,
|
|
290
|
+
node: null,
|
|
291
|
+
live: new Set,
|
|
292
|
+
st: familyStatus,
|
|
293
|
+
shallow: !!r?.shallow,
|
|
294
|
+
// The row key, for a child paired after the adoption (store.ts
|
|
295
|
+
// `adoptChild`, #3712) as the adoption pairs them.
|
|
296
|
+
key: typeof o === "function" ? o : o === null ? null : e => isWrappable(e) ? e[o] : undefined
|
|
297
|
+
};
|
|
298
|
+
const i = wrap(t, null, null, n);
|
|
299
|
+
if (n.shallow) {
|
|
300
|
+
i[$TARGET].s = true;
|
|
301
|
+
markRawIngest(t);
|
|
302
|
+
}
|
|
303
|
+
let s;
|
|
304
|
+
if (r?.seedLoadingValue) s = {
|
|
305
|
+
loadingValue: undefined
|
|
306
|
+
};
|
|
307
|
+
const c = computed(() => {
|
|
308
|
+
if (!n.node) n.node = getOwner();
|
|
309
|
+
runProjectionComputed(i, e, o);
|
|
310
|
+
}, s);
|
|
311
|
+
c.l &= ~CONFIG_AUTO_DISPOSE;
|
|
312
|
+
n.node = c;
|
|
313
|
+
// The derive's leaves are not its dependents: a mark's repoll
|
|
314
|
+
// (affects-marks.ts) reaches their readers through this.
|
|
315
|
+
c.fam = n;
|
|
316
|
+
return {
|
|
317
|
+
store: i,
|
|
318
|
+
node: c
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function createProjection(e, t, r) {
|
|
323
|
+
return createProjectionInternal(e, t, r).store;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/** `createStore(fn, seed, options)`: a projection with a user setter. */ function createStoreDerived(e, t, r) {
|
|
327
|
+
const {store: o} = createProjectionInternal(e, t, r);
|
|
328
|
+
return [ o, e => storeSetter(o, e) ];
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export { createProjection, createStoreDerived, runProjectionComputed };
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { isEqual } from "../core/core.js";
|
|
2
|
+
|
|
3
|
+
import { isOwned, $OWNER, storeLookup, lookupTarget } from "./target.js";
|
|
4
|
+
|
|
5
|
+
import { userWriting, unwrapValue, adoptPB, sameKey, materializePB, notifyFold, sameRow, targetsEqual, bumpDeep, notifyKeyValue, notifyKeyDiff, notifyFoldTail, hasAccessorFlag } from "./store.js";
|
|
6
|
+
|
|
7
|
+
import { optHooks } from "./hooks.js";
|
|
8
|
+
|
|
9
|
+
import { $TARGET, markRawIngest, isWrappable, rawValuesUsed, isRawValue } from "./types.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Store — reconcile: the immutable-diff adoption channel (INTERNALS §3).
|
|
13
|
+
*
|
|
14
|
+
* Reconcile never merge-writes into backing objects: it adopts `incoming`
|
|
15
|
+
* as the backing at every proxied level (an internal pointer swap at the
|
|
16
|
+
* fold — no clones, no user-object mutation), value-notifies only where
|
|
17
|
+
* nodes exist, and descends into a changed child pair only where a target
|
|
18
|
+
* with nodes or the sticky descendants flag exists below (§6d: diff the
|
|
19
|
+
* listened path, not every cell). Reference-equal unowned pairs skip
|
|
20
|
+
* outright (an owned backing is setter-diverged and must diff).
|
|
21
|
+
*
|
|
22
|
+
* Keyed arrays: the aligned prefix is walked positionally; from the first
|
|
23
|
+
* misalignment rows match by key (surviving rows keep their proxy
|
|
24
|
+
* identity), unkeyed items fall back to position.
|
|
25
|
+
*/ function reconcileState(e, n, t, o = false) {
|
|
26
|
+
if (n == null) throw new Error("");
|
|
27
|
+
const f = n[$TARGET];
|
|
28
|
+
if (f?.px !== n) throw new Error("");
|
|
29
|
+
const i = t === null ? null : typeof t === "string" ? e => e?.[t] : t;
|
|
30
|
+
// A user's reconcile on an optimistic family (S4): not an adoption of
|
|
31
|
+
// truth but an optimistic edit — the keyed diff is written into the draft
|
|
32
|
+
// (a matched row keeps its proxy, its changed leaves become its guesses),
|
|
33
|
+
// and the setter's exit turns the draft into guesses like any.
|
|
34
|
+
if (optHooks && userWriting() && f.fam?.opt === true) {
|
|
35
|
+
optHooks.reconcile(n, unwrapValue(e), i);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
// Replace-mode root handed another store's proxy: chain to it (§7b).
|
|
39
|
+
if (o && e !== n && e?.[$TARGET] !== undefined) {
|
|
40
|
+
const n = f.pb ?? f.v;
|
|
41
|
+
if (n === e) return;
|
|
42
|
+
// already chained to this store
|
|
43
|
+
adoptPB(f, e);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const l = unwrapValue(e);
|
|
47
|
+
if (i) {
|
|
48
|
+
const e = f.pb ?? f.v;
|
|
49
|
+
const n = i(e);
|
|
50
|
+
if (n !== undefined && !sameKey(i(l), n)) {
|
|
51
|
+
if (!o) throw new Error("");
|
|
52
|
+
// A different entity at the root: the old backing is disowned (wraps
|
|
53
|
+
// fresh if re-handed) and the new one adopted whole.
|
|
54
|
+
if (isOwned(e)) delete e[$OWNER]; else (f.fam?.map ?? storeLookup).delete(e);
|
|
55
|
+
adoptPB(f, l);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
applyAdopt(f, l, i, o);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Adopt `incoming` at `t` and notify its nodes — one leaf write per
|
|
63
|
+
* changed observed key, the structural nodes once — descending into
|
|
64
|
+
* changed child pairs that are proxied below. */ function applyAdopt(e, n, t, o = false) {
|
|
65
|
+
// The diff base is the view the nodes were last told (#3296). A nested
|
|
66
|
+
// overlay draft (`Object.create(v)`, #3044) reads a key the draft deleted
|
|
67
|
+
// as present through its prototype; materialized first — `adoptPB` does
|
|
68
|
+
// so anyway, after the base was taken — so a deletion the draft made is
|
|
69
|
+
// a deletion here (#3743: the presence and value diffs both read `old`).
|
|
70
|
+
const f = e.ovl ? materializePB(e) : e.pb ?? e.v;
|
|
71
|
+
if (n === f && !isOwned(f)) return;
|
|
72
|
+
// Q-D: a landing on an optimistic family is reconciled against the VIEW
|
|
73
|
+
// — key matching from the arrangement the guesses made (R28), so a row
|
|
74
|
+
// the guess added keeps its proxy when the server returns it.
|
|
75
|
+
const i = optHooks && e.fam?.opt === true ? optHooks.view(e, f) : f;
|
|
76
|
+
const l = Array.isArray(n);
|
|
77
|
+
const r = e.s === true;
|
|
78
|
+
adoptPB(e, n, false);
|
|
79
|
+
if (r) markRawIngest(n);
|
|
80
|
+
// The deep witness is bumped once per adoption (none to bump: done).
|
|
81
|
+
let u = e.dk === null;
|
|
82
|
+
// A live chained base (a store proxy, §7b) reads the inner store's
|
|
83
|
+
// current frame, not what the nodes were last told — nothing to diff
|
|
84
|
+
// against: the fold writes every node (#4361).
|
|
85
|
+
if (Array.isArray(i) !== l || f[$TARGET]) {
|
|
86
|
+
notifyFold(e, f, n);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (l) {
|
|
90
|
+
const l = e.n;
|
|
91
|
+
let s = 0;
|
|
92
|
+
if (t && !r) {
|
|
93
|
+
const r = i.length;
|
|
94
|
+
const a = n.length;
|
|
95
|
+
let c = 0;
|
|
96
|
+
// Aligned prefix: same row (by reference or key) at the same index.
|
|
97
|
+
for (const d = Math.min(r, a); c < d; c++) {
|
|
98
|
+
const r = n[c];
|
|
99
|
+
const a = i[c];
|
|
100
|
+
if (a !== r && !sameRow(a, r, t)) break;
|
|
101
|
+
// misaligned: fall to the keyed remainder below
|
|
102
|
+
if (r !== null && typeof r === "object" && (a !== r || isOwned(r))) descend(unwrapValue(a), r, t, o, e, c);
|
|
103
|
+
if (!u && !(r !== null && typeof r === "object" ? targetsEqual(a, r) : isEqual(a, r))) {
|
|
104
|
+
bumpDeep(e);
|
|
105
|
+
u = true;
|
|
106
|
+
}
|
|
107
|
+
if (l !== null) {
|
|
108
|
+
const e = l[c];
|
|
109
|
+
if (e !== undefined) {
|
|
110
|
+
s++;
|
|
111
|
+
notifyKeyValue(e, c, f[c], r, f, n);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (!u && c < n.length) bumpDeep(e);
|
|
116
|
+
const d = c;
|
|
117
|
+
// misalignment point (== nlen on aligned ticks)
|
|
118
|
+
let p = null;
|
|
119
|
+
for (;c < n.length; c++) {
|
|
120
|
+
const r = n[c];
|
|
121
|
+
if (r !== null && typeof r === "object") {
|
|
122
|
+
const n = t(r);
|
|
123
|
+
let f;
|
|
124
|
+
if (n !== undefined) {
|
|
125
|
+
if (p === null) {
|
|
126
|
+
p = new Map;
|
|
127
|
+
for (let e = d; e < i.length; e++) {
|
|
128
|
+
const n = unwrapValue(i[e]);
|
|
129
|
+
if (n !== null && typeof n === "object") {
|
|
130
|
+
const o = t(n);
|
|
131
|
+
if (o === undefined) continue;
|
|
132
|
+
const f = p.get(o);
|
|
133
|
+
if (f === undefined) p.set(o, e); else if (Array.isArray(f)) f.push(e); else p.set(o, [ f, e ]);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const e = p.get(n);
|
|
138
|
+
if (e === undefined) f = undefined; else if (Array.isArray(e)) {
|
|
139
|
+
f = unwrapValue(i[e.shift()]);
|
|
140
|
+
if (e.length === 1) p.set(n, e[0]);
|
|
141
|
+
} else {
|
|
142
|
+
f = unwrapValue(i[e]);
|
|
143
|
+
p.delete(n);
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
f = unwrapValue(i[c]);
|
|
147
|
+
// keyless item: positional fallback
|
|
148
|
+
}
|
|
149
|
+
descend(f, r, t, o, e, c);
|
|
150
|
+
}
|
|
151
|
+
if (l !== null) {
|
|
152
|
+
const e = l[c];
|
|
153
|
+
if (e !== undefined) {
|
|
154
|
+
s++;
|
|
155
|
+
notifyKeyDiff(e, c, f, n, false);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
} else {
|
|
160
|
+
const a = Math.min(i.length, n.length);
|
|
161
|
+
const c = n.length;
|
|
162
|
+
for (let d = 0; d < c; d++) {
|
|
163
|
+
const c = n[d];
|
|
164
|
+
if (!r && d < a && c !== null && typeof c === "object") descend(unwrapValue(i[d]), c, t, o, e, d);
|
|
165
|
+
if (!u && !(c !== null && typeof c === "object" ? targetsEqual(i[d], c) : isEqual(i[d], c))) {
|
|
166
|
+
bumpDeep(e);
|
|
167
|
+
u = true;
|
|
168
|
+
}
|
|
169
|
+
if (l !== null) {
|
|
170
|
+
const e = l[d];
|
|
171
|
+
if (e !== undefined) {
|
|
172
|
+
s++;
|
|
173
|
+
notifyKeyDiff(e, d, f, n, false);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// Indices past the new length (and `length` itself) still have nodes.
|
|
179
|
+
if (l !== null && s < e.nc) {
|
|
180
|
+
for (const e of Reflect.ownKeys(l)) {
|
|
181
|
+
const t = typeof e === "string" ? +e : NaN;
|
|
182
|
+
if (!(t >= 0 && t < n.length)) notifyKeyDiff(l[e], e, f, n, false);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
notifyFoldTail(e, f, n);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const s = e.n;
|
|
189
|
+
let a = 0;
|
|
190
|
+
for (const l in n) {
|
|
191
|
+
const c = n[l];
|
|
192
|
+
let d = f[l];
|
|
193
|
+
// A key under a guess is judged against the guess too (D-34): a truth
|
|
194
|
+
// equal to the value the guess covered still answers it.
|
|
195
|
+
if (optHooks && d === c && i !== f) d = i[l];
|
|
196
|
+
const p = c !== null && typeof c === "object";
|
|
197
|
+
if (d === c && (!p || !isOwned(c)) && (s === null || s[l] === undefined || !hasAccessorFlag(s[l]))) {
|
|
198
|
+
if (s?.[l] !== undefined) a++;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (p && !r) descend(unwrapValue(i[l]), c, t, o, e, l);
|
|
202
|
+
if (!u && !(p ? targetsEqual(d, c) : isEqual(d, c))) {
|
|
203
|
+
bumpDeep(e);
|
|
204
|
+
u = true;
|
|
205
|
+
}
|
|
206
|
+
if (s !== null) {
|
|
207
|
+
const e = s[l];
|
|
208
|
+
if (e !== undefined) {
|
|
209
|
+
a++;
|
|
210
|
+
notifyKeyValue(e, l, d, c, f, n);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
const c = Object.getOwnPropertySymbols(n);
|
|
215
|
+
for (let i = 0; i < c.length; i++) {
|
|
216
|
+
const l = c[i];
|
|
217
|
+
if (l === $OWNER) continue;
|
|
218
|
+
const u = n[l];
|
|
219
|
+
if (!r && u !== null && typeof u === "object") descend(unwrapValue(f[l]), u, t, o, e, l);
|
|
220
|
+
if (s !== null) {
|
|
221
|
+
const e = s[l];
|
|
222
|
+
if (e !== undefined) {
|
|
223
|
+
a++;
|
|
224
|
+
notifyKeyValue(e, l, f[l], u, f, n);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
// Keys the incoming object no longer has still have nodes.
|
|
229
|
+
if (s !== null && a < e.nc) {
|
|
230
|
+
for (const e of Reflect.ownKeys(s)) {
|
|
231
|
+
if (!hasOwnP.call(n, e)) notifyKeyDiff(s[e], e, f, n, false);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
notifyFoldTail(e, f, n);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const hasOwnP = Object.prototype.hasOwnProperty;
|
|
238
|
+
|
|
239
|
+
/** Descend into a changed child pair — only where something is proxied
|
|
240
|
+
* below (§6d), never into a raw-marked leaf, never across array/object or a
|
|
241
|
+
* key mismatch (that is a replacement, handled by the parent's slot).
|
|
242
|
+
*
|
|
243
|
+
* A raw in both frames (the same objects swapped, reordered or shifted,
|
|
244
|
+
* #4001) must not leave two slots on one row. A raw already a live row keeps
|
|
245
|
+
* it at the slot it moved to; a row's `pk` follows the slot it sits at (a
|
|
246
|
+
* move or a pairing writes it), so a row whose raw is at that slot of the
|
|
247
|
+
* incoming frame is spoken for and not paired again here; a raw still
|
|
248
|
+
* resolving to a row that moved on drops the lookup (it wraps fresh, or is
|
|
249
|
+
* paired here). One lookup of `nv` on the common path, no allocation. */ function descend(e, n, t, o, f, i) {
|
|
250
|
+
// `nv` is an object at every call site.
|
|
251
|
+
if (e === null || typeof e !== "object") return;
|
|
252
|
+
const l = f.fam;
|
|
253
|
+
n = unwrapValue(n);
|
|
254
|
+
let r = lookupTarget(e, l);
|
|
255
|
+
const u = lookupTarget(n, l);
|
|
256
|
+
if (r !== undefined && r.pk !== i) {
|
|
257
|
+
if (r.pk != i && f.v[r.pk] === r.v) r = undefined; else if (r.u === f) r.pk = i;
|
|
258
|
+
}
|
|
259
|
+
if (u !== undefined && u !== r) {
|
|
260
|
+
if (u.v === n) {
|
|
261
|
+
if (u.u === f) u.pk = i;
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
(l?.map ?? storeLookup).delete(n);
|
|
265
|
+
}
|
|
266
|
+
if (r === undefined) return;
|
|
267
|
+
// nothing proxied below this pair
|
|
268
|
+
if (!isWrappable(n)) return;
|
|
269
|
+
if (rawValuesUsed && isRawValue(n)) return;
|
|
270
|
+
if (Array.isArray(e) !== Array.isArray(n)) return;
|
|
271
|
+
// An array slot was paired by key already (or both rows are keyless).
|
|
272
|
+
if (typeof i !== "number" && t) {
|
|
273
|
+
const o = t(e);
|
|
274
|
+
const f = t(n);
|
|
275
|
+
if (o !== undefined && f !== undefined && !sameKey(o, f)) return;
|
|
276
|
+
}
|
|
277
|
+
if (!o && t !== null && !r.d) return;
|
|
278
|
+
applyAdopt(r, n, t, o);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export { reconcileState };
|