@solidjs/signals 2.0.0-rc.19 → 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 +553 -264
- package/dist/dev.attribution.js +18 -18
- package/dist/dev.js +1030 -439
- package/dist/observe/affects.js +12 -2
- package/dist/observe/boundaries.js +168 -115
- package/dist/observe/core/async.js +57 -37
- package/dist/observe/core/attribution.js +6 -6
- package/dist/observe/core/constants.js +9 -1
- package/dist/observe/core/copies.js +10 -0
- package/dist/observe/core/core.js +261 -181
- package/dist/observe/core/effect.js +32 -27
- package/dist/observe/core/graph.js +7 -7
- package/dist/observe/core/heap.js +29 -29
- package/dist/observe/core/lanes.js +301 -101
- package/dist/observe/core/owner.js +13 -13
- package/dist/observe/core/scheduler.js +124 -69
- package/dist/observe/core/truth.js +9 -7
- package/dist/observe/core/verdict.js +160 -98
- package/dist/observe/deferred.js +1 -1
- package/dist/observe/index.js +2 -0
- package/dist/observe/map.js +120 -116
- package/dist/observe/reveal.js +48 -48
- package/dist/observe/signals.js +5 -5
- package/dist/observe/store/affects.js +2 -2
- package/dist/observe/store/optimistic.js +272 -176
- package/dist/observe/store/projection.js +21 -10
- package/dist/observe/store/reconcile.js +8 -2
- package/dist/observe/store/store.js +91 -55
- package/dist/observe/store/types.js +11 -1
- package/dist/observe/store/utils.js +1 -1
- package/dist/prod/affects.js +12 -2
- package/dist/prod/boundaries.js +164 -109
- package/dist/prod/core/async.js +201 -181
- package/dist/prod/core/constants.js +9 -1
- package/dist/prod/core/copies.js +10 -0
- package/dist/prod/core/core.js +244 -164
- package/dist/prod/core/dev.js +108 -3
- package/dist/prod/core/effect.js +28 -23
- package/dist/prod/core/graph.js +9 -9
- package/dist/prod/core/heap.js +16 -16
- package/dist/prod/core/identities.js +1 -1
- package/dist/prod/core/lanes.js +307 -95
- package/dist/prod/core/owner.js +22 -22
- package/dist/prod/core/scheduler.js +266 -211
- package/dist/prod/core/truth.js +9 -7
- package/dist/prod/core/verdict.js +161 -99
- package/dist/prod/deferred.js +1 -1
- package/dist/prod/index.js +2 -0
- package/dist/prod/map.js +78 -74
- package/dist/prod/reveal.js +34 -34
- package/dist/prod/signals.js +3 -3
- package/dist/prod/store/affects.js +15 -15
- package/dist/prod/store/optimistic.js +185 -89
- package/dist/prod/store/projection.js +49 -38
- package/dist/prod/store/reconcile.js +8 -2
- package/dist/prod/store/store.js +99 -63
- package/dist/prod/store/types.js +11 -1
- package/dist/types/affects.d.ts +3 -2
- package/dist/types/boundaries.d.ts +14 -2
- package/dist/types/core/async.d.ts +2 -1
- package/dist/types/core/constants.d.ts +8 -0
- package/dist/types/core/copies.d.ts +54 -0
- package/dist/types/core/core.d.ts +15 -0
- package/dist/types/core/dev.d.ts +1 -1
- package/dist/types/core/lanes.d.ts +8 -1
- package/dist/types/core/scheduler.d.ts +36 -4
- package/dist/types/core/truth.d.ts +3 -1
- package/dist/types/core/types.d.ts +6 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/store/hooks.d.ts +15 -1
- package/dist/types/store/store.d.ts +2 -1
- package/dist/types/store/target.d.ts +5 -0
- package/dist/types/store/types.d.ts +7 -0
- package/package.json +1 -1
|
@@ -12,6 +12,8 @@ import { getOwner, isDisposed } from "../core/owner.js";
|
|
|
12
12
|
|
|
13
13
|
import { question, setQuestion, insertSubs, schedule } from "../core/scheduler.js";
|
|
14
14
|
|
|
15
|
+
import { optHooks } from "./hooks.js";
|
|
16
|
+
|
|
15
17
|
import { reconcileState } from "./reconcile.js";
|
|
16
18
|
|
|
17
19
|
import { wrap, storeSetter, runDirect } from "./store.js";
|
|
@@ -50,22 +52,22 @@ import { $TARGET, markRawIngest, isWrappable, setWriteOverride } from "./types.j
|
|
|
50
52
|
* set/getOwnPropertyDescriptor/defineProperty. With the store proxy as
|
|
51
53
|
* target those checks re-enter the store's traps OUTSIDE the override
|
|
52
54
|
* bracket, so `Object.keys(state)` in a derive continuation fired the gate.
|
|
53
|
-
*/ function wrapDraft(e, t, r,
|
|
55
|
+
*/ function wrapDraft(e, t, r, o, n, i) {
|
|
54
56
|
const mutate = e => {
|
|
55
57
|
if (!t()) return true;
|
|
56
58
|
setWriteOverride(true);
|
|
57
59
|
// A continuation's write is the flight's answer: it asks the flight's
|
|
58
60
|
// question (A18 provenance — a landing beneath a newer guess is held,
|
|
59
61
|
// not a correction; lanes.ts `laneWrite`).
|
|
60
|
-
const
|
|
62
|
+
const o = question;
|
|
61
63
|
if (i !== undefined) setQuestion(i());
|
|
62
64
|
try {
|
|
63
65
|
r ? r(e) : e();
|
|
64
66
|
} finally {
|
|
65
67
|
setWriteOverride(false);
|
|
66
|
-
setQuestion(
|
|
68
|
+
setQuestion(o);
|
|
67
69
|
}
|
|
68
|
-
if (
|
|
70
|
+
if (n) n();
|
|
69
71
|
return true;
|
|
70
72
|
};
|
|
71
73
|
const read = e => {
|
|
@@ -79,23 +81,23 @@ import { $TARGET, markRawIngest, isWrappable, setWriteOverride } from "./types.j
|
|
|
79
81
|
const s = {
|
|
80
82
|
get(s, c) {
|
|
81
83
|
const u = read(() => e[c]);
|
|
82
|
-
return !
|
|
84
|
+
return !o && typeof u === "object" && u !== null && c !== $TARGET ? wrapDraft(u, t, r, false, n, i) : u;
|
|
83
85
|
},
|
|
84
86
|
has: (t, r) => read(() => r in e),
|
|
85
|
-
set: (t, r,
|
|
86
|
-
e[r] =
|
|
87
|
+
set: (t, r, o) => mutate(() => {
|
|
88
|
+
e[r] = o;
|
|
87
89
|
}),
|
|
88
90
|
deleteProperty: (t, r) => mutate(() => {
|
|
89
91
|
delete e[r];
|
|
90
92
|
}),
|
|
91
93
|
ownKeys: () => read(() => Reflect.ownKeys(e)),
|
|
92
94
|
getOwnPropertyDescriptor(t, r) {
|
|
93
|
-
const
|
|
94
|
-
if (
|
|
95
|
-
return
|
|
95
|
+
const o = read(() => Reflect.getOwnPropertyDescriptor(e, r));
|
|
96
|
+
if (o) o.configurable = true;
|
|
97
|
+
return o;
|
|
96
98
|
},
|
|
97
|
-
defineProperty: (t, r,
|
|
98
|
-
Reflect.defineProperty(e, r,
|
|
99
|
+
defineProperty: (t, r, o) => mutate(() => {
|
|
100
|
+
Reflect.defineProperty(e, r, o);
|
|
99
101
|
})
|
|
100
102
|
};
|
|
101
103
|
const c = new Proxy(Array.isArray(e) ? [] : {}, s);
|
|
@@ -127,6 +129,7 @@ function cloneState(e, t) {
|
|
|
127
129
|
// reader the landing leaves unchanged does not re-run to clear it (#4305).
|
|
128
130
|
const r = new NotReadyError(t);
|
|
129
131
|
e.woke = true;
|
|
132
|
+
optHooks?.ask(e);
|
|
130
133
|
forEachFamilyNode(e, e => {
|
|
131
134
|
if (e.o === null) return;
|
|
132
135
|
// The leaf carries the derive's pending as a propagated copy, as a memo
|
|
@@ -162,6 +165,10 @@ function cloneState(e, t) {
|
|
|
162
165
|
forEachFamilyNode(e, e => {
|
|
163
166
|
if (e.o !== null) settlePendingSource(e, t);
|
|
164
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);
|
|
165
172
|
}
|
|
166
173
|
|
|
167
174
|
/** A source's status reached the derive by propagation (core
|
|
@@ -174,8 +181,8 @@ function forEachFamilyNode(e, t) {
|
|
|
174
181
|
for (const r of e.live) {
|
|
175
182
|
const e = r.n;
|
|
176
183
|
if (e !== null) for (const r of Reflect.ownKeys(e)) t(e[r]);
|
|
177
|
-
const
|
|
178
|
-
if (
|
|
184
|
+
const o = r.h;
|
|
185
|
+
if (o !== null) for (const e of Reflect.ownKeys(o)) t(o[e]);
|
|
179
186
|
if (r.k !== null) t(r.k);
|
|
180
187
|
if (r.dk !== null) t(r.dk);
|
|
181
188
|
}
|
|
@@ -185,7 +192,7 @@ function forEachFamilyNode(e, t) {
|
|
|
185
192
|
* result (sync, or each async landing) through the reconcile channel.
|
|
186
193
|
* Draft validity is per run (R37): live from here until the NEXT run starts
|
|
187
194
|
* — a sync derive that subscribes to an external source and pushes into
|
|
188
|
-
* the draft from the callback (#3585) is the model use. */ function runProjectionComputed(e, t, r,
|
|
195
|
+
* the draft from the callback (#3585) is the model use. */ function runProjectionComputed(e, t, r, o, n) {
|
|
189
196
|
const i = getOwner();
|
|
190
197
|
const s = e[$TARGET];
|
|
191
198
|
const c = s.fam;
|
|
@@ -199,8 +206,8 @@ function forEachFamilyNode(e, t) {
|
|
|
199
206
|
// The question a write of this run answers: inside a slice, the slice's
|
|
200
207
|
// (the sync pass); in a continuation — the flight still registered — the
|
|
201
208
|
// flight's (`handleAsync` stamped it with the action that asked).
|
|
202
|
-
const asking = () => question || (i.T !== null && i.T.Ee !== null ? i.T.
|
|
203
|
-
const f = wrapDraft(e, () => c.run === u && !isDisposed(i),
|
|
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,
|
|
204
211
|
// A write after the run returned (a continuation, a callback) arms the
|
|
205
212
|
// drain itself when no landing will.
|
|
206
213
|
() => {
|
|
@@ -210,24 +217,24 @@ function forEachFamilyNode(e, t) {
|
|
|
210
217
|
// `_value`); every later run — a re-derive in a flush, an async landing
|
|
211
218
|
// — stages and commits with the flush like a memo's recompute.
|
|
212
219
|
const d = u === 1;
|
|
213
|
-
const pass = () => storeSetter(f,
|
|
214
|
-
l = t(a ??
|
|
215
|
-
const commit = t => {
|
|
220
|
+
const pass = () => storeSetter(f, n => {
|
|
221
|
+
l = t(a ?? n);
|
|
222
|
+
const commit = (t, u = 1) => {
|
|
216
223
|
// Shadow run: commit a detached snapshot, never the shadow itself
|
|
217
224
|
// (adoption takes the value by identity — handing it the live
|
|
218
225
|
// shadow would fuse the draft to the observable store).
|
|
219
226
|
if (a && (t === undefined || t === a)) t = cloneState(a, s.s);
|
|
220
|
-
if (t !==
|
|
227
|
+
if (t !== n && t !== undefined) {
|
|
221
228
|
const write = () => {
|
|
222
|
-
const
|
|
229
|
+
const o = question;
|
|
223
230
|
setQuestion(asking());
|
|
224
231
|
try {
|
|
225
232
|
storeSetter(e, e => reconcileState(t, e, r, true), false);
|
|
226
233
|
} finally {
|
|
227
|
-
setQuestion(
|
|
234
|
+
setQuestion(o);
|
|
228
235
|
}
|
|
229
236
|
};
|
|
230
|
-
|
|
237
|
+
o ? o(write, t) : write();
|
|
231
238
|
}
|
|
232
239
|
// A landing (not the sync pass's own commit): the readers held
|
|
233
240
|
// pending on the derive settle.
|
|
@@ -235,8 +242,11 @@ function forEachFamilyNode(e, t) {
|
|
|
235
242
|
// A commit is the derive's change for the readers that observed its
|
|
236
243
|
// flight (linked by `pullFamily`; a settled pull does not link), as
|
|
237
244
|
// a memo's landing is for its readers: one that caught the NotReady
|
|
238
|
-
// (a fallback value, not pending) re-runs too (#4070).
|
|
239
|
-
|
|
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);
|
|
240
250
|
};
|
|
241
251
|
const u = handleAsync(i, l, commit, (e, t) => {
|
|
242
252
|
// The flight rejected: the leaves' readers learn it from here —
|
|
@@ -248,7 +258,8 @@ function forEachFamilyNode(e, t) {
|
|
|
248
258
|
if (d && i.m & STATUS_UNINITIALIZED) return;
|
|
249
259
|
if (t) wakeFamily(c); else errorFamily(c);
|
|
250
260
|
});
|
|
251
|
-
|
|
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));
|
|
252
263
|
}, false);
|
|
253
264
|
try {
|
|
254
265
|
d ? runDirect(pass) : pass();
|
|
@@ -273,8 +284,8 @@ function createProjectionInternal(e, t, r) {
|
|
|
273
284
|
// Seedless (`fn` takes no draft): the root starts as an empty record. It is
|
|
274
285
|
// a draft like any seed, never observable before the first landing (A25).
|
|
275
286
|
t ||= {};
|
|
276
|
-
const
|
|
277
|
-
const
|
|
287
|
+
const o = r?.key === undefined ? "id" : r.key;
|
|
288
|
+
const n = {
|
|
278
289
|
map: new WeakMap,
|
|
279
290
|
node: null,
|
|
280
291
|
live: new Set,
|
|
@@ -282,10 +293,10 @@ function createProjectionInternal(e, t, r) {
|
|
|
282
293
|
shallow: !!r?.shallow,
|
|
283
294
|
// The row key, for a child paired after the adoption (store.ts
|
|
284
295
|
// `adoptChild`, #3712) as the adoption pairs them.
|
|
285
|
-
key: typeof
|
|
296
|
+
key: typeof o === "function" ? o : o === null ? null : e => isWrappable(e) ? e[o] : undefined
|
|
286
297
|
};
|
|
287
|
-
const i = wrap(t, null, null,
|
|
288
|
-
if (
|
|
298
|
+
const i = wrap(t, null, null, n);
|
|
299
|
+
if (n.shallow) {
|
|
289
300
|
i[$TARGET].s = true;
|
|
290
301
|
markRawIngest(t);
|
|
291
302
|
}
|
|
@@ -294,14 +305,14 @@ function createProjectionInternal(e, t, r) {
|
|
|
294
305
|
loadingValue: undefined
|
|
295
306
|
};
|
|
296
307
|
const c = computed(() => {
|
|
297
|
-
if (!
|
|
298
|
-
runProjectionComputed(i, e,
|
|
308
|
+
if (!n.node) n.node = getOwner();
|
|
309
|
+
runProjectionComputed(i, e, o);
|
|
299
310
|
}, s);
|
|
300
311
|
c.l &= ~CONFIG_AUTO_DISPOSE;
|
|
301
|
-
|
|
312
|
+
n.node = c;
|
|
302
313
|
// The derive's leaves are not its dependents: a mark's repoll
|
|
303
314
|
// (affects-marks.ts) reaches their readers through this.
|
|
304
|
-
c.fam =
|
|
315
|
+
c.fam = n;
|
|
305
316
|
return {
|
|
306
317
|
store: i,
|
|
307
318
|
node: c
|
|
@@ -313,8 +324,8 @@ function createProjection(e, t, r) {
|
|
|
313
324
|
}
|
|
314
325
|
|
|
315
326
|
/** `createStore(fn, seed, options)`: a projection with a user setter. */ function createStoreDerived(e, t, r) {
|
|
316
|
-
const {store:
|
|
317
|
-
return [
|
|
327
|
+
const {store: o} = createProjectionInternal(e, t, r);
|
|
328
|
+
return [ o, e => storeSetter(o, e) ];
|
|
318
329
|
}
|
|
319
330
|
|
|
320
331
|
export { createProjection, createStoreDerived, runProjectionComputed };
|
|
@@ -79,7 +79,10 @@ import { $TARGET, markRawIngest, isWrappable, rawValuesUsed, isRawValue } from "
|
|
|
79
79
|
if (r) markRawIngest(n);
|
|
80
80
|
// The deep witness is bumped once per adoption (none to bump: done).
|
|
81
81
|
let u = e.dk === null;
|
|
82
|
-
|
|
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]) {
|
|
83
86
|
notifyFold(e, f, n);
|
|
84
87
|
return;
|
|
85
88
|
}
|
|
@@ -186,7 +189,10 @@ import { $TARGET, markRawIngest, isWrappable, rawValuesUsed, isRawValue } from "
|
|
|
186
189
|
let a = 0;
|
|
187
190
|
for (const l in n) {
|
|
188
191
|
const c = n[l];
|
|
189
|
-
|
|
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];
|
|
190
196
|
const p = c !== null && typeof c === "object";
|
|
191
197
|
if (d === c && (!p || !isOwned(c)) && (s === null || s[l] === undefined || !hasAccessorFlag(s[l]))) {
|
|
192
198
|
if (s?.[l] !== undefined) a++;
|
package/dist/prod/store/store.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { CONFIG_MANUAL_WRITE, CONFIG_OVERRIDE, CONFIG_HELD, $REFRESH, NOT_PENDING, STATUS_PENDING, STATUS_ERROR, EFFECT_RENDER, REACTIVE_RECOMPUTING_DEPS, REACTIVE_STAGED_READ, CONFIG_VERDICT, CONFIG_CHILDREN_FORBIDDEN, CONFIG_PROMOTED, REACTIVE_DIRTY, REACTIVE_CHECK } from "../core/constants.js";
|
|
1
|
+
import { CONFIG_MANUAL_WRITE, CONFIG_OVERRIDE, CONFIG_HELD, $REFRESH, NOT_PENDING, STATUS_PENDING, STATUS_ERROR, EFFECT_RENDER, REACTIVE_RECOMPUTING_DEPS, REACTIVE_STAGED_READ, CONFIG_VERDICT, CONFIG_CHILDREN_FORBIDDEN, STATUS_UNINITIALIZED, CONFIG_PROMOTED, REACTIVE_DIRTY, REACTIVE_CHECK } from "../core/constants.js";
|
|
2
2
|
|
|
3
|
-
import { writeHook, setSignal, isEqual, read, verdict, snapshotCaptureActive, captureWriteSnapshot, context, notePromotedWrite, untrack, slotSignal, stagedScreen, stagedRead } from "../core/core.js";
|
|
3
|
+
import { writeHook, setSignal, isEqual, read, verdict, snapshotCaptureActive, captureWriteSnapshot, context, notePromotedWrite, untrack, slotSignal, stagedScreen, stagedRead, tracking } from "../core/core.js";
|
|
4
4
|
|
|
5
5
|
import { signalHook } from "../core/identities.js";
|
|
6
6
|
|
|
7
|
-
import { setSlotUnobserved } from "../core/graph.js";
|
|
7
|
+
import { link, setSlotUnobserved } from "../core/graph.js";
|
|
8
8
|
|
|
9
9
|
import { getObserver } from "../core/owner.js";
|
|
10
10
|
|
|
@@ -16,7 +16,7 @@ import { lookupTarget, setLookup, $OWNER, isOwned, markDescendants } from "./tar
|
|
|
16
16
|
|
|
17
17
|
import { optHooks, affectsHooks } from "./hooks.js";
|
|
18
18
|
|
|
19
|
-
import { $TARGET, rawValuesUsed, isRawValue, isWrappable, markRawIngest, writeOverride, markRawOne, $RECORD, $PROXY, $TRACK } from "./types.js";
|
|
19
|
+
import { $TARGET, rawValuesUsed, isRawValue, isWrappable, markRawIngest, writeOverride, markRawOne, $RECORD, $PROXY, $TRACK, installShallowView } from "./types.js";
|
|
20
20
|
|
|
21
21
|
export { markRaw } from "./types.js";
|
|
22
22
|
|
|
@@ -220,7 +220,7 @@ function sameLogicalSlot(e, n, t) {
|
|
|
220
220
|
|
|
221
221
|
function releaseSlot(e) {
|
|
222
222
|
const n = e.O;
|
|
223
|
-
const t = e.
|
|
223
|
+
const t = e.Xe;
|
|
224
224
|
if (t === $TRACK) {
|
|
225
225
|
if (n.k === e) n.k = null;
|
|
226
226
|
} else if (t === $DEEP) {
|
|
@@ -236,7 +236,7 @@ function releaseSlot(e) {
|
|
|
236
236
|
/** A node was born on `target`: a family target enters the live index. */ function noteNode(e, n, t) {
|
|
237
237
|
if (e.fam !== null) e.fam.live.add(e);
|
|
238
238
|
markDescendants(e);
|
|
239
|
-
if (affectsHooks !== null) affectsHooks.
|
|
239
|
+
if (affectsHooks !== null) affectsHooks.ro(e, n, t);
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
// Shared slot-node release handler: registered once; the core sweep
|
|
@@ -247,7 +247,7 @@ function releaseSlot(e) {
|
|
|
247
247
|
// holds: its release waits for the commit that resolves it. So does a leaf of
|
|
248
248
|
// a held container (#4146): a mainline write it carried is in the held
|
|
249
249
|
// staging too, where it would read as the hold's until the landing.
|
|
250
|
-
const slotKept = e => e.
|
|
250
|
+
const slotKept = e => e.ue !== NOT_PENDING || e.l & CONFIG_OVERRIDE || e.O.k?.l & CONFIG_HELD;
|
|
251
251
|
|
|
252
252
|
setSlotUnobserved(e => {
|
|
253
253
|
if (slotKept(e)) deferredReleases.add(e); else releaseSlot(e);
|
|
@@ -258,9 +258,9 @@ setSlotUnobserved(e => {
|
|
|
258
258
|
* pending one — and held by the container's transaction when the container
|
|
259
259
|
* is held (#3706: the unit of the hold is the key — a key the batch left
|
|
260
260
|
* unchanged stages nothing and holds no one). */ function bornStaged(e, n, t, r) {
|
|
261
|
-
if (n.
|
|
261
|
+
if (n.ye && n.ye(n.le, t)) return;
|
|
262
262
|
queuePendingNode(n);
|
|
263
|
-
n.
|
|
263
|
+
n.ue = t;
|
|
264
264
|
const o = e.k;
|
|
265
265
|
if (o === null) return;
|
|
266
266
|
// a node-less staging holds nothing (`stageOn`)
|
|
@@ -326,7 +326,7 @@ function getHasNode(e, n) {
|
|
|
326
326
|
// would have read had the node been there from the write.
|
|
327
327
|
if (e.pb !== null && !directCommit) {
|
|
328
328
|
n.le = preBatch(e);
|
|
329
|
-
n.
|
|
329
|
+
n.ue = e.pb;
|
|
330
330
|
queuePendingNode(n);
|
|
331
331
|
}
|
|
332
332
|
}
|
|
@@ -343,10 +343,10 @@ function getHasNode(e, n) {
|
|
|
343
343
|
* re-created, queued, swept and dropped ~7000 container nodes). If the
|
|
344
344
|
* flush parks, the seam materializes and holds the node then
|
|
345
345
|
* (`storeParkHook`) — the one case the staging home exists for. */ function stageOn(e, n) {
|
|
346
|
-
if (e.k === null && passLane === null && (context === null || globalQueue.
|
|
346
|
+
if (e.k === null && passLane === null && (context === null || globalQueue.Y) && !deriveHolds(e)) return;
|
|
347
347
|
const t = getContainerNode(e);
|
|
348
|
-
if (t.
|
|
349
|
-
t.
|
|
348
|
+
if (t.ue === NOT_PENDING) queuePendingNode(t);
|
|
349
|
+
t.ue = n;
|
|
350
350
|
// A28 (4): a write issued inside a pass outside a flush (a creation-time
|
|
351
351
|
// derive) is promoted at the pass's end — the batch with it.
|
|
352
352
|
if (context !== null) notePromotedWrite(t);
|
|
@@ -364,6 +364,7 @@ const storePark = e => {
|
|
|
364
364
|
const t = foldList[n];
|
|
365
365
|
if (t.k === null && t.pb !== null) holdNode(getContainerNode(t), e);
|
|
366
366
|
}
|
|
367
|
+
optHooks?.park(e);
|
|
367
368
|
};
|
|
368
369
|
|
|
369
370
|
function getDeepNode(e) {
|
|
@@ -419,16 +420,23 @@ function isOwnAccessor(e, n) {
|
|
|
419
420
|
t[$OWNER] = n;
|
|
420
421
|
return t;
|
|
421
422
|
}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
423
|
+
// A plain array with no accessors and no named keys beyond `length`
|
|
424
|
+
// (`kc` counts own keys) clones by `slice()`: the descriptor path
|
|
425
|
+
// allocates one descriptor per element, O(n) per first draft write (10k
|
|
426
|
+
// rows: ~10 ms per push).
|
|
427
|
+
let t;
|
|
428
|
+
if (!n.a && n.kc <= e.length + 1 && Object.getPrototypeOf(e) === Array.prototype) t = e.slice(); else {
|
|
429
|
+
const r = Object.getOwnPropertyDescriptors(e);
|
|
430
|
+
for (const t of Reflect.ownKeys(r)) {
|
|
431
|
+
const o = r[t];
|
|
432
|
+
if (t === "length" && Array.isArray(e)) continue;
|
|
433
|
+
o.configurable = true;
|
|
434
|
+
if (!o.get && !o.set) o.writable = true; else n.a = true;
|
|
435
|
+
}
|
|
436
|
+
t = Array.isArray(e) ? Object.defineProperties([], r) : Object.create(Object.getPrototypeOf(e), r);
|
|
428
437
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
return r;
|
|
438
|
+
t[$OWNER] = n;
|
|
439
|
+
return t;
|
|
432
440
|
}
|
|
433
441
|
|
|
434
442
|
/** Wide plain clone (many keys): null-prototype build, then reset. */ function wideClone(e, n) {
|
|
@@ -494,7 +502,7 @@ const plainProto = e => {
|
|
|
494
502
|
const t = overlayOnto(e, e.sc === 2 ? wideClone(e.v, e) : cloneRaw(e.v, e), n);
|
|
495
503
|
e.pb = t;
|
|
496
504
|
// The staging IS the pending backing: re-point the container node.
|
|
497
|
-
if (e.k?.
|
|
505
|
+
if (e.k?.ue === n) e.k.ue = t;
|
|
498
506
|
return t;
|
|
499
507
|
}
|
|
500
508
|
|
|
@@ -515,7 +523,7 @@ const plainProto = e => {
|
|
|
515
523
|
// mainline setter mid-hold publishes mainline; the held keys stay held).
|
|
516
524
|
n = e.pb = cloneRaw(n, e);
|
|
517
525
|
const t = e.k;
|
|
518
|
-
if (t !== null && !(t.l & CONFIG_HELD)) t.
|
|
526
|
+
if (t !== null && !(t.l & CONFIG_HELD)) t.ue = n;
|
|
519
527
|
} else if (n === null) {
|
|
520
528
|
const t = e.v;
|
|
521
529
|
if (!directCommit) queueFold(e);
|
|
@@ -692,7 +700,7 @@ let foldIndex = null;
|
|
|
692
700
|
if (r.pb === null) continue;
|
|
693
701
|
// folded already (a direct commit)
|
|
694
702
|
const o = r.k;
|
|
695
|
-
if (o !== null && o.
|
|
703
|
+
if (o !== null && o.ue !== NOT_PENDING) {
|
|
696
704
|
// held: commits with its hold
|
|
697
705
|
foldList.push(r);
|
|
698
706
|
foldOlds.push(n[t]);
|
|
@@ -722,10 +730,10 @@ let foldIndex = null;
|
|
|
722
730
|
/** Core asks (lanes.ts `covered`): the value a slot node's guess covered.
|
|
723
731
|
* A chained link's committed value is the inner store's at the last
|
|
724
732
|
* read-through — refreshed here, since a guess served the reads since
|
|
725
|
-
* (#3672: the revert compares against the base's live value). */ GlobalQueue.
|
|
733
|
+
* (#3672: the revert compares against the base's live value). */ GlobalQueue.an = e => {
|
|
726
734
|
const n = e.O;
|
|
727
735
|
if (n.ch) {
|
|
728
|
-
const t = e.
|
|
736
|
+
const t = e.Xe;
|
|
729
737
|
const r = n.v;
|
|
730
738
|
if (n.h?.[t] === e) e.le = untrack(() => t in r); else if (n.n?.[t] === e) e.le = untrack(() => unwrapValue(r[t]));
|
|
731
739
|
}
|
|
@@ -738,9 +746,9 @@ installStoreHooks(storePark, () => {
|
|
|
738
746
|
});
|
|
739
747
|
|
|
740
748
|
/** The store half of `affects()` (store/affects.ts) makes its marks' nodes
|
|
741
|
-
* and walks records through these: it never imports the engine. */ GlobalQueue.
|
|
749
|
+
* and walks records through these: it never imports the engine. */ GlobalQueue.so = getNode;
|
|
742
750
|
|
|
743
|
-
GlobalQueue.
|
|
751
|
+
GlobalQueue.fo = isWrappable;
|
|
744
752
|
|
|
745
753
|
/** A projection's creation run commits directly (a memo's first value is
|
|
746
754
|
* its `_value`, not a staging): the draft still writes a clone of the seed,
|
|
@@ -840,13 +848,18 @@ function heldDerivation(e, n, t) {
|
|
|
840
848
|
// Optimistic channel: a user's writes on an optimistic family become
|
|
841
849
|
// guesses on the nodes (lanes); the draft is discarded, the committed
|
|
842
850
|
// backing never touched.
|
|
843
|
-
if (optHooks &&
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
851
|
+
if (optHooks && e.fam?.opt === true) {
|
|
852
|
+
if (userWrite) {
|
|
853
|
+
// The draft is discarded first: the diff reads the target (its nodes
|
|
854
|
+
// are born from the committed frame, not the draft).
|
|
855
|
+
e.pb = null;
|
|
856
|
+
e.wk = null;
|
|
857
|
+
e.pb = optHooks.writes(e, n);
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
// The derive's draft: new rows under guessed rows are paired with them
|
|
861
|
+
// first (D-34, #4329).
|
|
862
|
+
optHooks.pair(e, n);
|
|
850
863
|
}
|
|
851
864
|
const t = e.v;
|
|
852
865
|
const r = e.n;
|
|
@@ -990,7 +1003,10 @@ function membershipChanged(e, n) {
|
|
|
990
1003
|
} else {
|
|
991
1004
|
const o = t[n];
|
|
992
1005
|
const i = r[n];
|
|
993
|
-
|
|
1006
|
+
// A live chained `old` (a store proxy, §7b) already reads the inner
|
|
1007
|
+
// store's next frame, not what the node was last told: written, the
|
|
1008
|
+
// node's own equality judging it (#4361).
|
|
1009
|
+
if (t[$TARGET] || !isEqual(o, i) && !targetsEqual(o, i)) setSignal(e, typeof i === "function" ? () => i : i);
|
|
994
1010
|
}
|
|
995
1011
|
}
|
|
996
1012
|
|
|
@@ -1016,7 +1032,8 @@ function hasAccessorFlag(e) {
|
|
|
1016
1032
|
* unrelated `a.value` stayed stale until the action settled). A real
|
|
1017
1033
|
* change still writes, and on a held node still proposes. A live chained
|
|
1018
1034
|
* `old` (a store proxy, §7b) reflects the inner store, not what the node
|
|
1019
|
-
* was last told — written unconditionally, as before
|
|
1035
|
+
* was last told — written unconditionally, as before; its container too
|
|
1036
|
+
* (#4361).
|
|
1020
1037
|
*
|
|
1021
1038
|
* The container is told of an arrangement change when it has structural
|
|
1022
1039
|
* subscribers OR carries an arrangement guess (S4): for a guessed
|
|
@@ -1027,14 +1044,14 @@ function hasAccessorFlag(e) {
|
|
|
1027
1044
|
* answers the arrangement — before, only the presence write reached the
|
|
1028
1045
|
* lane, by accident of being unconditional. */ function notifyFoldTail(e, n, t) {
|
|
1029
1046
|
const r = e.h;
|
|
1047
|
+
const o = n[$TARGET];
|
|
1030
1048
|
if (r !== null) {
|
|
1031
|
-
const e
|
|
1032
|
-
for (const o of Reflect.ownKeys(r)) if (e || o in n !== o in t) setSignal(r[o], o in t);
|
|
1049
|
+
for (const e of Reflect.ownKeys(r)) if (o || e in n !== e in t) setSignal(r[e], e in t);
|
|
1033
1050
|
}
|
|
1034
|
-
const
|
|
1035
|
-
if (
|
|
1036
|
-
const e = Array.isArray(t) && Array.isArray(n) ? arrayStructureChanged(n, t) : membershipChanged(n, t);
|
|
1037
|
-
if (e) notifyContainer(
|
|
1051
|
+
const i = e.k;
|
|
1052
|
+
if (i !== null && (i.o !== null || i.l & CONFIG_OVERRIDE)) {
|
|
1053
|
+
const e = o || (Array.isArray(t) && Array.isArray(n) ? arrayStructureChanged(n, t) : membershipChanged(n, t));
|
|
1054
|
+
if (e) notifyContainer(i, t);
|
|
1038
1055
|
}
|
|
1039
1056
|
}
|
|
1040
1057
|
|
|
@@ -1089,7 +1106,7 @@ function inDraft(e) {
|
|
|
1089
1106
|
* "adoption is eager by contract" — and the node keeps the frame committed
|
|
1090
1107
|
* readers under a hold still see), `t.v` otherwise. */ function committed(e) {
|
|
1091
1108
|
const n = e.k;
|
|
1092
|
-
if (n !== null) return n.
|
|
1109
|
+
if (n !== null) return n.ue !== NOT_PENDING ? asBacking(n.le) : e.v;
|
|
1093
1110
|
// A node-less staging (`stageOn`): the fold queue remembers the frame.
|
|
1094
1111
|
return e.pb !== null ? preBatch(e) : e.v;
|
|
1095
1112
|
}
|
|
@@ -1101,7 +1118,7 @@ function inDraft(e) {
|
|
|
1101
1118
|
* committed frame. */ function stagingReader() {
|
|
1102
1119
|
let e = context;
|
|
1103
1120
|
if (e === null) return null;
|
|
1104
|
-
if (e.Qe) e = e.
|
|
1121
|
+
if (e.Qe) e = e.Ke ?? null;
|
|
1105
1122
|
return e === null || e.l & CONFIG_CHILDREN_FORBIDDEN ? null : e;
|
|
1106
1123
|
}
|
|
1107
1124
|
|
|
@@ -1208,7 +1225,7 @@ function inDraft(e) {
|
|
|
1208
1225
|
|
|
1209
1226
|
/** Did the HELD staging (the container node's, not a mainline layer above
|
|
1210
1227
|
* it) change `key`? */ function heldKeyChanged(e, n, t = true) {
|
|
1211
|
-
return changedBetween(e, e.k.le, e.k.
|
|
1228
|
+
return changedBetween(e, e.k.le, e.k.ue, n, t) && !adoptChild(e, n);
|
|
1212
1229
|
}
|
|
1213
1230
|
|
|
1214
1231
|
/** The values of a held frame, built once per frame: neither the committed
|
|
@@ -1237,12 +1254,12 @@ function rawsOf(e) {
|
|
|
1237
1254
|
const t = e.k;
|
|
1238
1255
|
const r = asBacking(t.le);
|
|
1239
1256
|
const o = r[n];
|
|
1240
|
-
const i = t.
|
|
1257
|
+
const i = t.ue[n];
|
|
1241
1258
|
const l = Array.isArray(r) ? e.fam?.key : null;
|
|
1242
1259
|
if (e.s || l === undefined || !sameRow(o, i, l) || !isWrappable(o) || !isWrappable(i) || Array.isArray(o) !== Array.isArray(i) || lookupTarget(o, e.fam) !== undefined || lookupTarget(i, e.fam) !== undefined ||
|
|
1243
1260
|
// A raw in both frames (the same objects swapped or reordered) is its
|
|
1244
1261
|
// own row: pairing it with another would alias the two after the commit.
|
|
1245
|
-
rawsOf(r).has(i) || rawsOf(t.
|
|
1262
|
+
rawsOf(r).has(i) || rawsOf(t.ue).has(o)) return false;
|
|
1246
1263
|
// A raw-marked value wraps to itself, a proxy to its own target: neither
|
|
1247
1264
|
// is a fresh target around `ov`.
|
|
1248
1265
|
const u = wrap(o, e, n)[$TARGET];
|
|
@@ -1285,9 +1302,16 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1285
1302
|
|
|
1286
1303
|
/** Value of a key with a leaf: core's — `read()` links a tracked reader and,
|
|
1287
1304
|
* tracked or not, applies the hold and A28 rules a signal's read does. A
|
|
1288
|
-
* projection's unheld staging is ahead for a handler (`familyAhead`)
|
|
1289
|
-
|
|
1290
|
-
|
|
1305
|
+
* projection's unheld staging is ahead for a handler (`familyAhead`), and is
|
|
1306
|
+
* the only value while its derive has never committed: the committed side
|
|
1307
|
+
* is the seed, never observable (A25) — a reader core serves the screen (a
|
|
1308
|
+
* verdict reader, #4355) is served the staging, as for a memo born staged
|
|
1309
|
+
* (core `serve`). */ function nodeValue(e, n, t) {
|
|
1310
|
+
let r;
|
|
1311
|
+
if (e.ue !== NOT_PENDING && !(e.l & CONFIG_HELD) && t.fam !== null && (context === null || t.fam.node?.m & STATUS_UNINITIALIZED)) {
|
|
1312
|
+
if (context !== null && tracking) link(e, context);
|
|
1313
|
+
r = e.ue;
|
|
1314
|
+
} else r = read(e);
|
|
1291
1315
|
return r === FORCE ? n : r;
|
|
1292
1316
|
}
|
|
1293
1317
|
|
|
@@ -1365,9 +1389,9 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1365
1389
|
// (committed plus the guesses) and does not suspend on the derive; a
|
|
1366
1390
|
// verdict window still asks it (`isPending` is true while the flight is
|
|
1367
1391
|
// up). The family's, not the target's: the edit stands in for the one
|
|
1368
|
-
// flight the family has
|
|
1369
|
-
|
|
1370
|
-
|
|
1392
|
+
// flight the family has — not over a new question they do not answer
|
|
1393
|
+
// (optimistic.ts `stands`, #4336).
|
|
1394
|
+
if (verdict === null && n.m & STATUS_PENDING && optHooks?.stands(e)) return;
|
|
1371
1395
|
read(n);
|
|
1372
1396
|
} else untrack(() => read(n));
|
|
1373
1397
|
}
|
|
@@ -1380,7 +1404,7 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1380
1404
|
* classification, as core reads it — `heldNotFinal`). */ function holdWithDerive(e, n) {
|
|
1381
1405
|
const t = e.fam?.node;
|
|
1382
1406
|
if (t !== undefined && t !== null && !userWrite && // the derive's own write (its draft or landing), not a user setter's
|
|
1383
|
-
t.l & CONFIG_HELD && n.
|
|
1407
|
+
t.l & CONFIG_HELD && n.ue !== NOT_PENDING &&
|
|
1384
1408
|
// A node carrying a guess is the lane's: its staging is the truth beneath
|
|
1385
1409
|
// the guess (`setSignal` → `laneWrite`), never re-homed by the store.
|
|
1386
1410
|
!(n.l & (CONFIG_HELD | CONFIG_OVERRIDE))) {
|
|
@@ -1472,7 +1496,7 @@ const traps = {
|
|
|
1472
1496
|
}
|
|
1473
1497
|
// (The witness before the pull: a mark on an uninitialized derived
|
|
1474
1498
|
// store is witnessed, then the pull throws — loading, declared pending.)
|
|
1475
|
-
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.
|
|
1499
|
+
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.io(e, n);
|
|
1476
1500
|
if (e.fam !== null) pullFamily(e);
|
|
1477
1501
|
// Hot inline case: existing PLAIN node (non-accessor) read outside any
|
|
1478
1502
|
// draft — the dbmon/uibench effect re-read shape. Core's `read()` serves
|
|
@@ -1549,7 +1573,8 @@ const traps = {
|
|
|
1549
1573
|
// Reading a currently-absent own key subscribes to it (R12) — for any
|
|
1550
1574
|
// target OUTSIDE its own draft scope, even mid-setter (#3037, above).
|
|
1551
1575
|
if (u === undefined && !inDraft(e)) {
|
|
1552
|
-
|
|
1576
|
+
// (`nodeValue` reads — and links — the node it creates.)
|
|
1577
|
+
if (getObserver() !== null) getNode(e, n, i);
|
|
1553
1578
|
const t = e.n?.[n];
|
|
1554
1579
|
if (t) {
|
|
1555
1580
|
const r = nodeValue(t, undefined, e);
|
|
@@ -1569,7 +1594,7 @@ const traps = {
|
|
|
1569
1594
|
if (n === $OWNER || n === $RECORD) return false;
|
|
1570
1595
|
// (The witness before the pull: a mark on an uninitialized derived
|
|
1571
1596
|
// store is witnessed, then the pull throws — loading, declared pending.)
|
|
1572
|
-
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.
|
|
1597
|
+
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.io(e, n);
|
|
1573
1598
|
if (e.fam !== null) pullFamily(e);
|
|
1574
1599
|
const t = readSource(e, n);
|
|
1575
1600
|
// A tracked reader's presence node answers (born from the two frames;
|
|
@@ -1594,14 +1619,14 @@ const traps = {
|
|
|
1594
1619
|
return n in t && !(t === e.pb && e.del?.has(n));
|
|
1595
1620
|
},
|
|
1596
1621
|
ownKeys(e) {
|
|
1597
|
-
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.
|
|
1622
|
+
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.io(e, undefined);
|
|
1598
1623
|
if (e.fam !== null) pullFamily(e);
|
|
1599
1624
|
return visibleKeys(e, enumerationSource(e));
|
|
1600
1625
|
},
|
|
1601
1626
|
getOwnPropertyDescriptor(e, n) {
|
|
1602
1627
|
if (n === $OWNER || n === $RECORD) return undefined;
|
|
1603
1628
|
const t = getObserver();
|
|
1604
|
-
if (verdict !== null && affectsHooks !== null && t === null) affectsHooks.
|
|
1629
|
+
if (verdict !== null && affectsHooks !== null && t === null) affectsHooks.io(e, n);
|
|
1605
1630
|
if (e.fam !== null) pullFamily(e);
|
|
1606
1631
|
// An enumerator (spread, Object.entries) already holds the container
|
|
1607
1632
|
// node and reads its frame; a descriptor read on its own tracks
|
|
@@ -1728,8 +1753,8 @@ const traps = {
|
|
|
1728
1753
|
* to the key's presence node or a later add never re-runs it. */ function observerHoldsContainer(e, n) {
|
|
1729
1754
|
const t = e.k;
|
|
1730
1755
|
if (t === null) return false;
|
|
1731
|
-
const r = t.
|
|
1732
|
-
return r !== null && r.u === n && (!(n.R & REACTIVE_RECOMPUTING_DEPS) || r.
|
|
1756
|
+
const r = t.Ze;
|
|
1757
|
+
return r !== null && r.u === n && (!(n.R & REACTIVE_RECOMPUTING_DEPS) || r.Ne === n.de);
|
|
1733
1758
|
}
|
|
1734
1759
|
|
|
1735
1760
|
function storeSetter(e, n, t = true) {
|
|
@@ -1797,6 +1822,17 @@ function createStore(e, n) {
|
|
|
1797
1822
|
return [ r, setter ];
|
|
1798
1823
|
}
|
|
1799
1824
|
|
|
1825
|
+
/** The list driver's slot view of a shallow store (types.ts `shallowView`):
|
|
1826
|
+
* one `readSource` for the container instead of a trap per slot. A shallow
|
|
1827
|
+
* store's slots hold raw records, so the backing the container read serves
|
|
1828
|
+
* is what each slot read would — except where a slot's own leaf can answer
|
|
1829
|
+
* differently: an optimistic family's guesses, a chained backing, accessor
|
|
1830
|
+
* keys, or a read inside a draft or write scope. Those keep the trap. */ installShallowView(e => {
|
|
1831
|
+
// Any open write scope keeps the trap (it covers `inDraft`).
|
|
1832
|
+
if (e?.s && !(e.ch || e.a || writeScopes || writeOverride || e.fam?.opt)) return e.fam && pullFamily(e),
|
|
1833
|
+
readSource(e);
|
|
1834
|
+
});
|
|
1835
|
+
|
|
1800
1836
|
/** True when `proxy` is a SHALLOW store (children served verbatim, slots
|
|
1801
1837
|
* replaced by reference — #2932). The list driver uses this to choose the
|
|
1802
1838
|
* slot-patch channel (collected row bodies) over per-record registration. */ function storeIsShallow(e) {
|