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