@solidjs/signals 2.0.0-beta.24 → 2.0.0-beta.26
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.js +577 -87
- package/dist/node.cjs +1120 -683
- package/dist/prod/core/async.js +180 -107
- package/dist/prod/core/core.js +214 -180
- package/dist/prod/core/effect.js +19 -19
- package/dist/prod/core/external.js +4 -4
- package/dist/prod/core/graph.js +25 -25
- package/dist/prod/core/heap.js +23 -23
- package/dist/prod/core/lanes.js +15 -15
- package/dist/prod/core/optimistic.js +42 -40
- package/dist/prod/core/owner.js +41 -41
- package/dist/prod/core/scheduler.js +90 -90
- package/dist/prod/core/verdict.js +54 -54
- package/dist/prod/map.js +60 -60
- package/dist/prod/signals.js +1 -1
- package/dist/prod/store/optimistic.js +18 -11
- package/dist/prod/store/projection.js +25 -18
- package/dist/prod/store/reconcile.js +405 -211
- package/dist/prod/store/store.js +225 -105
- package/dist/prod/store/utils.js +22 -22
- package/dist/types/core/async.d.ts +1 -0
- package/dist/types/core/core.d.ts +15 -0
- package/dist/types/store/optimistic.d.ts +1 -1
- package/dist/types/store/store.d.ts +20 -2
- package/dist/types-cjs/core/async.d.cts +1 -0
- package/dist/types-cjs/core/core.d.cts +15 -0
- package/dist/types-cjs/store/optimistic.d.cts +1 -1
- package/dist/types-cjs/store/store.d.cts +20 -2
- package/package.json +1 -1
package/dist/prod/store/store.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { $REFRESH, STORE_SNAPSHOT_PROPS, NOT_PENDING, unwrapOverride, STATUS_UNINITIALIZED, STATUS_PENDING, NO_SNAPSHOT } from "../core/constants.js";
|
|
2
2
|
|
|
3
|
-
import { suppressComputedRecompute, isEqual, signal, pendingCheckActive, untrack, setSignal, read, snapshotCaptureActive, snapshotSources } from "../core/core.js";
|
|
3
|
+
import { suppressComputedRecompute, isEqual, signal, pendingCheckActive, untrack, setSignal, read, readNodeFast, READ_SLOW, snapshotCaptureActive, snapshotSources } from "../core/core.js";
|
|
4
4
|
|
|
5
5
|
import { DEV } from "../core/dev.js";
|
|
6
6
|
|
|
@@ -28,7 +28,7 @@ import { createProjectionInternal } from "./projection.js";
|
|
|
28
28
|
// the record witnesses it into the active isPending() probe.
|
|
29
29
|
$AFFECTS = Symbol(0);
|
|
30
30
|
|
|
31
|
-
const STORE_VALUE = "v", STORE_OVERRIDE = "o", STORE_OPTIMISTIC_OVERRIDE = "x", STORE_NODE = "n", STORE_HAS = "h", STORE_CUSTOM_PROTO = "c", STORE_WRAP = "w", STORE_LOOKUP = "l", STORE_FIREWALL = "f", STORE_OPTIMISTIC = "p", STORE_OPTIMISTIC_OWNERS = "t", STORE_PARENT = "u", STORE_DESC = "d";
|
|
31
|
+
const STORE_VALUE = "v", STORE_OVERRIDE = "o", STORE_OPTIMISTIC_OVERRIDE = "x", STORE_NODE = "n", STORE_HAS = "h", STORE_CUSTOM_PROTO = "c", STORE_WRAP = "w", STORE_LOOKUP = "l", STORE_FIREWALL = "f", STORE_OPTIMISTIC = "p", STORE_OPTIMISTIC_OWNERS = "t", STORE_PARENT = "u", STORE_DESC = "d", STORE_SHALLOW = "s";
|
|
32
32
|
|
|
33
33
|
const STORE_SELF_PENDING = Symbol(0);
|
|
34
34
|
|
|
@@ -52,6 +52,7 @@ function initStoreFields(e) {
|
|
|
52
52
|
e[STORE_SNAPSHOT_PROPS] = undefined;
|
|
53
53
|
e[STORE_PARENT] = undefined;
|
|
54
54
|
e[STORE_DESC] = undefined;
|
|
55
|
+
e[STORE_SHALLOW] = undefined;
|
|
55
56
|
e[$PROXY] = null;
|
|
56
57
|
}
|
|
57
58
|
|
|
@@ -76,24 +77,107 @@ function createStoreProxy(e, t = storeTraps, r) {
|
|
|
76
77
|
return n[$PROXY] = new Proxy(n, t);
|
|
77
78
|
}
|
|
78
79
|
|
|
80
|
+
// The global lookup maps raw value -> StoreNode TARGET (not proxy): reconcile
|
|
81
|
+
// and the unwrap/snapshot walks resolve targets through it constantly, and a
|
|
82
|
+
// target hit is a plain field read away from its proxy while a proxy hit
|
|
83
|
+
// costs a trap to get back to the target. Per-family STORE_LOOKUP maps
|
|
84
|
+
// (projections/optimistic) still map raw -> proxy — their wrap functions own
|
|
85
|
+
// that contract — so mixed-lookup consumers resolve through lookupTarget().
|
|
79
86
|
const storeLookup = new WeakMap;
|
|
80
87
|
|
|
81
88
|
// Node records that hold at least one user (non-`$TRACK`) symbol-keyed node.
|
|
82
89
|
// Lets reconcile enumerate symbols only for records that need it (#2851).
|
|
83
90
|
const symbolKeyedRecords = new WeakSet;
|
|
84
91
|
|
|
92
|
+
function lookupTarget(e, t) {
|
|
93
|
+
if (t !== undefined && t !== storeLookup) {
|
|
94
|
+
const r = t.get(e);
|
|
95
|
+
if (r !== undefined) return r[$TARGET];
|
|
96
|
+
}
|
|
97
|
+
return storeLookup.get(e);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Values marked raw never acquire a proxy identity: wrap() serves them as-is
|
|
101
|
+
// everywhere — deep stores hold them as leaf values replaced by reference.
|
|
102
|
+
// Once raw, always raw (identity stays single, just unwrapped). Consulted
|
|
103
|
+
// only on wrap-creation and ingest paths; reads never touch it.
|
|
104
|
+
const rawValues = new WeakSet;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Marks a value as raw: no store will ever wrap it — every store presents it
|
|
108
|
+
* as-is, tracked by reference at whatever slot holds it and updated by
|
|
109
|
+
* replacement. Useful for class instances and external objects (editors,
|
|
110
|
+
* scene graphs, Maps) and for record-shaped data updated wholesale. Sticky
|
|
111
|
+
* for the value's lifetime.
|
|
112
|
+
*/
|
|
113
|
+
// Flipped on the first mark and exported as a LIVE binding: reconcile
|
|
114
|
+
// consults it on every recursable pair, and importing the boolean directly
|
|
115
|
+
// lets those sites skip even the function call when no shallow store or raw
|
|
116
|
+
// mark exists anywhere in the app.
|
|
117
|
+
let rawValuesUsed = false;
|
|
118
|
+
|
|
119
|
+
function isRawValue(e) {
|
|
120
|
+
return rawValuesUsed && rawValues.has(e);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function markRawOne(e) {
|
|
124
|
+
if (isWrappable(e)) {
|
|
125
|
+
// A store proxy is already tracked elsewhere: the shallow boundary passes
|
|
126
|
+
// it through by reference (replaced, never edited — same slot semantics
|
|
127
|
+
// as a raw) instead of claiming it raw. The sticky mark is global, so
|
|
128
|
+
// marking a live proxy would make wrap() serve it verbatim through every
|
|
129
|
+
// OTHER store too — downstream deep stores then captured it instead of
|
|
130
|
+
// wrapping it in their own family, and their writes landed in the
|
|
131
|
+
// upstream store's override layer (#2932).
|
|
132
|
+
if (e[$TARGET] !== undefined) return;
|
|
133
|
+
rawValuesUsed = true;
|
|
134
|
+
rawValues.add(e);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function markRawIngest(e) {
|
|
139
|
+
if (Array.isArray(e)) {
|
|
140
|
+
for (let t = 0, r = e.length; t < r; t++) markRawOne(e[t]);
|
|
141
|
+
} else {
|
|
142
|
+
for (const t in e) markRawOne(e[t]);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
85
146
|
function wrap(e, t) {
|
|
147
|
+
// Raw is raw in every family: the mark must preempt family wrapping too,
|
|
148
|
+
// or a shallow projection/optimistic store would proxy its raw records.
|
|
149
|
+
if (rawValuesUsed && rawValues.has(e)) return e;
|
|
86
150
|
if (t?.[STORE_WRAP]) {
|
|
87
151
|
const r = t[STORE_WRAP](e, t);
|
|
88
152
|
const n = r[$TARGET];
|
|
89
153
|
if (n && !n[STORE_PARENT] && n !== t) n[STORE_PARENT] = t;
|
|
90
154
|
return r;
|
|
91
155
|
}
|
|
92
|
-
|
|
93
|
-
if (
|
|
94
|
-
|
|
95
|
-
|
|
156
|
+
const r = storeLookup.get(e);
|
|
157
|
+
if (r !== undefined) return r[$PROXY];
|
|
158
|
+
let n = e[$PROXY];
|
|
159
|
+
if (!n) {
|
|
160
|
+
n = createStoreProxy(e);
|
|
161
|
+
const r = n[$TARGET];
|
|
162
|
+
storeLookup.set(e, r);
|
|
163
|
+
if (t) r[STORE_PARENT] = t;
|
|
164
|
+
}
|
|
165
|
+
return n;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Shallow store root: the target itself is fully reactive (per-key nodes,
|
|
169
|
+
// membership, $TRACK); its values are served raw. Seed values are marked at
|
|
170
|
+
// creation; reconcile and the set trap mark on ingest.
|
|
171
|
+
function wrapShallow(e) {
|
|
172
|
+
const t = storeLookup.get(e);
|
|
173
|
+
if (t !== undefined) {
|
|
174
|
+
if (t[STORE_SHALLOW]) return t[$PROXY];
|
|
96
175
|
}
|
|
176
|
+
const r = createStoreProxy(e);
|
|
177
|
+
const n = r[$TARGET];
|
|
178
|
+
n[STORE_SHALLOW] = true;
|
|
179
|
+
storeLookup.set(e, n);
|
|
180
|
+
markRawIngest(e);
|
|
97
181
|
return r;
|
|
98
182
|
}
|
|
99
183
|
|
|
@@ -115,24 +199,24 @@ function writeOnly(e) {
|
|
|
115
199
|
}
|
|
116
200
|
|
|
117
201
|
function unwrapStoreValue(e, t, r) {
|
|
118
|
-
const n = e?.[$TARGET] ||
|
|
202
|
+
const n = e?.[$TARGET] || lookupTarget(e, r);
|
|
119
203
|
if (!n) return e;
|
|
120
204
|
const o = n[STORE_OVERRIDE];
|
|
121
205
|
if (!o) return n[STORE_VALUE];
|
|
122
206
|
if (!t) t = new Map;
|
|
123
207
|
if (t.has(e)) return t.get(e);
|
|
124
208
|
const i = n[STORE_VALUE];
|
|
125
|
-
const
|
|
126
|
-
const
|
|
127
|
-
t.set(e,
|
|
209
|
+
const O = Array.isArray(i);
|
|
210
|
+
const s = O ? [] : Object.create(Object.getPrototypeOf(i));
|
|
211
|
+
t.set(e, s);
|
|
128
212
|
r = n[STORE_LOOKUP] ?? storeLookup;
|
|
129
213
|
for (const e of getStoreKeys(i, o)) {
|
|
130
|
-
if (
|
|
214
|
+
if (O && e === "length") continue;
|
|
131
215
|
const n = e in o ? o[e] : i[e];
|
|
132
|
-
if (n !== $DELETED)
|
|
216
|
+
if (n !== $DELETED) s[e] = unwrapStoreValue(n, t, r);
|
|
133
217
|
}
|
|
134
|
-
if (
|
|
135
|
-
return
|
|
218
|
+
if (O) s.length = o.length ?? i.length;
|
|
219
|
+
return s;
|
|
136
220
|
}
|
|
137
221
|
|
|
138
222
|
function isPrototypePollutionKey(e) {
|
|
@@ -179,7 +263,7 @@ function ownEnumerableKeysPlain(e) {
|
|
|
179
263
|
* The value a store leaf's backing signal currently shows to readers: active
|
|
180
264
|
* override, else held pending value, else committed value.
|
|
181
265
|
*/ function visibleNodeValue(e) {
|
|
182
|
-
return e.
|
|
266
|
+
return e.Ae !== undefined && e.Ae !== NOT_PENDING ? unwrapOverride(e.Ae) : e._e !== NOT_PENDING ? e._e : e.Ue;
|
|
183
267
|
}
|
|
184
268
|
|
|
185
269
|
function hasOwnStoreProperty(e, t) {
|
|
@@ -207,10 +291,10 @@ function getNodes(e, t) {
|
|
|
207
291
|
|
|
208
292
|
function getNode(e, t, r, n, o = isEqual, i) {
|
|
209
293
|
if (t[r]) return t[r];
|
|
210
|
-
const
|
|
294
|
+
const O = signal(n, {
|
|
211
295
|
equals: o,
|
|
212
296
|
unobserved() {
|
|
213
|
-
if (t[r] ===
|
|
297
|
+
if (t[r] === O) {
|
|
214
298
|
delete t[r];
|
|
215
299
|
// Drop the symbol-record mark once the last user symbol node is
|
|
216
300
|
// gone, so reconcile's fast path stops probing a now string-only
|
|
@@ -230,28 +314,28 @@ function getNode(e, t, r, n, o = isEqual, i) {
|
|
|
230
314
|
}
|
|
231
315
|
}, e[STORE_FIREWALL]);
|
|
232
316
|
if (e[STORE_OPTIMISTIC]) {
|
|
233
|
-
|
|
317
|
+
O.Ae = NOT_PENDING;
|
|
234
318
|
}
|
|
235
319
|
if (i && r in i) {
|
|
236
320
|
const e = i[r];
|
|
237
|
-
|
|
238
|
-
snapshotSources?.add(
|
|
321
|
+
O.xe = e === undefined ? NO_SNAPSHOT : e;
|
|
322
|
+
snapshotSources?.add(O);
|
|
239
323
|
}
|
|
240
324
|
if (typeof r === "symbol" && r !== $TRACK && r !== $AFFECTS) symbolKeyedRecords.add(t);
|
|
241
325
|
// A node born inside a live mark's identity scope inherits the mark
|
|
242
326
|
// (the declaration walk could only cover nodes that existed then). The
|
|
243
327
|
// record's own $AFFECTS carrier is the mark's channel, never a member.
|
|
244
|
-
if (r !== $AFFECTS && affectsScopes.size) inheritAffectsMarks(
|
|
328
|
+
if (r !== $AFFECTS && affectsScopes.size) inheritAffectsMarks(O, e[STORE_VALUE], r);
|
|
245
329
|
// Node presence bubbles up the wrap chain (sticky), so reconcile can see
|
|
246
330
|
// "subscribers live somewhere below" through node-less intermediate
|
|
247
331
|
// records — the captured-proxy diff gate (#2902). Amortized O(1): stops at
|
|
248
332
|
// the first already-flagged ancestor.
|
|
249
|
-
let
|
|
250
|
-
while (
|
|
251
|
-
|
|
252
|
-
|
|
333
|
+
let s = e;
|
|
334
|
+
while (s && !s[STORE_DESC]) {
|
|
335
|
+
s[STORE_DESC] = true;
|
|
336
|
+
s = s[STORE_PARENT];
|
|
253
337
|
}
|
|
254
|
-
return t[r] =
|
|
338
|
+
return t[r] = O;
|
|
255
339
|
}
|
|
256
340
|
|
|
257
341
|
/**
|
|
@@ -287,43 +371,43 @@ const affectsScopes = new Map;
|
|
|
287
371
|
// holds the root, and must still descend to pick up records added since.
|
|
288
372
|
o) {
|
|
289
373
|
if (!isWrappable(e)) return;
|
|
290
|
-
const i = e[$TARGET] || (n
|
|
291
|
-
const
|
|
292
|
-
if (o.has(
|
|
293
|
-
o.add(
|
|
294
|
-
t.scope.add(
|
|
295
|
-
let
|
|
374
|
+
const i = e[$TARGET] || lookupTarget(e, n);
|
|
375
|
+
const O = i ? i[STORE_VALUE] : e;
|
|
376
|
+
if (o.has(O)) return;
|
|
377
|
+
o.add(O);
|
|
378
|
+
t.scope.add(O);
|
|
379
|
+
let s;
|
|
296
380
|
if (i) {
|
|
297
381
|
collectRecordNodes(i[STORE_NODE], r);
|
|
298
382
|
collectRecordNodes(i[STORE_HAS], r);
|
|
299
|
-
|
|
383
|
+
s = mergedOverlay(i);
|
|
300
384
|
// Carry the effective lookup into untouched descendants. Default stores
|
|
301
385
|
// use the global lookup just like snapshotImpl; without it, nested raw
|
|
302
386
|
// objects fall back to string-only enumeration and symbol branches vanish.
|
|
303
387
|
n = i[STORE_LOOKUP] ?? n ?? storeLookup;
|
|
304
388
|
}
|
|
305
|
-
if (Array.isArray(
|
|
306
|
-
const e =
|
|
389
|
+
if (Array.isArray(O)) {
|
|
390
|
+
const e = s?.length ?? O.length;
|
|
307
391
|
for (let i = 0; i < e; i++) {
|
|
308
|
-
const e =
|
|
392
|
+
const e = s && i in s ? s[i] : O[i];
|
|
309
393
|
if (e !== $DELETED) walkAffectsScope(e, t, r, n, o);
|
|
310
394
|
}
|
|
311
395
|
// Arrays can also carry symbol metadata. Enumerate symbols separately to
|
|
312
396
|
// avoid scanning large index lists twice. Outside a store tree, retain the
|
|
313
397
|
// existing index-only walk.
|
|
314
|
-
const
|
|
315
|
-
for (let e = 0, i =
|
|
316
|
-
const i =
|
|
317
|
-
const S = getPropertyDescriptor(
|
|
398
|
+
const E = i || n ? getStoreSymbols(O, s) : [];
|
|
399
|
+
for (let e = 0, i = E.length; e < i; e++) {
|
|
400
|
+
const i = E[e];
|
|
401
|
+
const S = getPropertyDescriptor(O, s, i);
|
|
318
402
|
if (!S || S.get) continue;
|
|
319
403
|
walkAffectsScope(S.value, t, r, n, o);
|
|
320
404
|
}
|
|
321
405
|
} else {
|
|
322
|
-
const e = i || n ? getStoreKeys(
|
|
323
|
-
for (let i = 0,
|
|
324
|
-
const
|
|
325
|
-
if (!
|
|
326
|
-
walkAffectsScope(
|
|
406
|
+
const e = i || n ? getStoreKeys(O, s) : getKeys(O, s);
|
|
407
|
+
for (let i = 0, E = e.length; i < E; i++) {
|
|
408
|
+
const E = getPropertyDescriptor(O, s, e[i]);
|
|
409
|
+
if (!E || E.get) continue;
|
|
410
|
+
walkAffectsScope(E.value, t, r, n, o);
|
|
327
411
|
}
|
|
328
412
|
}
|
|
329
413
|
}
|
|
@@ -352,11 +436,11 @@ o) {
|
|
|
352
436
|
// Callers guard on `pendingCheckActive`, which only flips inside
|
|
353
437
|
// isPending() — the verdict layer is loaded and its hook installed.
|
|
354
438
|
const r = e[STORE_NODE]?.[$AFFECTS];
|
|
355
|
-
if (r?.t) GlobalQueue.
|
|
439
|
+
if (r?.t) GlobalQueue.Dt(r);
|
|
356
440
|
if (affectsScopes.size) {
|
|
357
441
|
const n = e[STORE_VALUE];
|
|
358
442
|
for (const [e, o] of affectsScopes) {
|
|
359
|
-
if (e !== r && e.t && o.scope.has(n) && (o.key === undefined || o.key === t)) GlobalQueue.
|
|
443
|
+
if (e !== r && e.t && o.scope.has(n) && (o.key === undefined || o.key === t)) GlobalQueue.Dt(e);
|
|
360
444
|
}
|
|
361
445
|
}
|
|
362
446
|
}
|
|
@@ -498,8 +582,8 @@ function getPropertyDescriptor(e, t, r) {
|
|
|
498
582
|
function prepareStoreWrite(e, t, r) {
|
|
499
583
|
if (e[STORE_OPTIMISTIC]) {
|
|
500
584
|
const t = e[STORE_FIREWALL];
|
|
501
|
-
if (t?.
|
|
502
|
-
globalQueue.initTransition(t.
|
|
585
|
+
if (t?.Ie) {
|
|
586
|
+
globalQueue.initTransition(t.Ie);
|
|
503
587
|
}
|
|
504
588
|
}
|
|
505
589
|
const n = e[STORE_VALUE];
|
|
@@ -514,10 +598,10 @@ function prepareStoreWrite(e, t, r) {
|
|
|
514
598
|
}
|
|
515
599
|
}
|
|
516
600
|
const i = e[STORE_OPTIMISTIC] && !projectionWriteActive;
|
|
517
|
-
const
|
|
601
|
+
const O = i ? STORE_OPTIMISTIC_OVERRIDE : STORE_OVERRIDE;
|
|
518
602
|
return {
|
|
519
603
|
base: o,
|
|
520
|
-
overrideKey:
|
|
604
|
+
overrideKey: O,
|
|
521
605
|
state: n
|
|
522
606
|
};
|
|
523
607
|
}
|
|
@@ -551,29 +635,29 @@ function prepareStoreWrite(e, t, r) {
|
|
|
551
635
|
function upsertStoreNode(e, t, r, n, o) {
|
|
552
636
|
if (t[r]) return t[r];
|
|
553
637
|
const i = isWrappable(n) ? wrap(n, e) : n;
|
|
554
|
-
const
|
|
555
|
-
registerTransientStoreNode(
|
|
556
|
-
return
|
|
638
|
+
const O = getNode(e, t, r, i, isEqual, o);
|
|
639
|
+
registerTransientStoreNode(O);
|
|
640
|
+
return O;
|
|
557
641
|
}
|
|
558
642
|
|
|
559
643
|
function notifyStoreProperty(e, t, r, n, o, i) {
|
|
560
644
|
// Cold writes upsert a transient pending node so untracked reads batch like signals.
|
|
561
645
|
// Skip for projection writes (different commit semantics) and for optimistic stores
|
|
562
646
|
// (whose whole purpose is immediate visibility via STORE_OPTIMISTIC_OVERRIDE).
|
|
563
|
-
const
|
|
564
|
-
const
|
|
565
|
-
const
|
|
566
|
-
if (
|
|
567
|
-
setSignal(
|
|
568
|
-
} else if (!
|
|
647
|
+
const O = projectionWriteActive || e[STORE_OPTIMISTIC];
|
|
648
|
+
const s = r !== "delete";
|
|
649
|
+
const E = e[STORE_HAS]?.[t];
|
|
650
|
+
if (E) {
|
|
651
|
+
setSignal(E, s);
|
|
652
|
+
} else if (!O && r !== "invalidate" && i !== s) {
|
|
569
653
|
const r = upsertStoreNode(e, getNodes(e, STORE_HAS), t, i);
|
|
570
|
-
setSignal(r,
|
|
654
|
+
setSignal(r, s);
|
|
571
655
|
}
|
|
572
656
|
const S = getNodes(e, STORE_NODE);
|
|
573
657
|
if (r === "set") {
|
|
574
658
|
if (S[t]) {
|
|
575
659
|
setSignal(S[t], () => isWrappable(n) ? wrap(n, e) : n);
|
|
576
|
-
} else if (!
|
|
660
|
+
} else if (!O) {
|
|
577
661
|
const r = upsertStoreNode(e, S, t, o, e[STORE_SNAPSHOT_PROPS]);
|
|
578
662
|
setSignal(r, () => isWrappable(n) ? wrap(n, e) : n);
|
|
579
663
|
}
|
|
@@ -585,7 +669,7 @@ function notifyStoreProperty(e, t, r, n, o, i) {
|
|
|
585
669
|
} else {
|
|
586
670
|
if (S[t]) {
|
|
587
671
|
setSignal(S[t], undefined);
|
|
588
|
-
} else if (!
|
|
672
|
+
} else if (!O) {
|
|
589
673
|
const r = upsertStoreNode(e, S, t, o, e[STORE_SNAPSHOT_PROPS]);
|
|
590
674
|
setSignal(r, undefined);
|
|
591
675
|
}
|
|
@@ -628,55 +712,79 @@ const storeTraps = {
|
|
|
628
712
|
const r = e[STORE_NODE];
|
|
629
713
|
const n = r && r[t];
|
|
630
714
|
if (n !== undefined && e[STORE_VALUE][$TARGET] === undefined) {
|
|
631
|
-
|
|
715
|
+
// readNodeFast is read()'s plain-signal fast path hoisted over the
|
|
716
|
+
// call; READ_SLOW means a global read window (latest/pending-check/
|
|
717
|
+
// transition/lane/snapshot capture) or a node layer is active, and
|
|
718
|
+
// only then does the full read() resolution have anything to do.
|
|
719
|
+
let t = readNodeFast(n);
|
|
720
|
+
if (t === READ_SLOW) t = read(n);
|
|
632
721
|
if (t === $DELETED) t = undefined;
|
|
722
|
+
// Every node-writing site wraps wrappables before setSignal (see the
|
|
723
|
+
// dev assertion below), so re-wrapping on read is redundant — except
|
|
724
|
+
// during snapshot capture, where read() can surface a raw captured
|
|
725
|
+
// value seeded from snapshot props.
|
|
726
|
+
if (!snapshotCaptureActive) {
|
|
727
|
+
return t;
|
|
728
|
+
}
|
|
633
729
|
return isWrappable(t) ? wrap(t, e) : t;
|
|
634
730
|
}
|
|
635
731
|
}
|
|
636
732
|
const n = getObserver() === e[STORE_FIREWALL];
|
|
637
733
|
const o = getNodes(e, STORE_NODE);
|
|
638
734
|
const i = n ? undefined : o[t];
|
|
639
|
-
const
|
|
640
|
-
if (!i && !e[STORE_OVERRIDE] && !e[STORE_OPTIMISTIC_OVERRIDE] && !e[STORE_CUSTOM_PROTO] && !e[STORE_OPTIMISTIC] && !e[STORE_SNAPSHOT_PROPS] && !
|
|
735
|
+
const O = e[STORE_VALUE];
|
|
736
|
+
if (!i && !e[STORE_OVERRIDE] && !e[STORE_OPTIMISTIC_OVERRIDE] && !e[STORE_CUSTOM_PROTO] && !e[STORE_OPTIMISTIC] && !e[STORE_SNAPSHOT_PROPS] && !O[$TARGET] && !(t in O) && getObserver() && !n && !writeOnly(r)) {
|
|
641
737
|
return read(getNode(e, o, t, undefined));
|
|
642
738
|
}
|
|
643
|
-
const
|
|
644
|
-
const
|
|
739
|
+
const s = getOverlayLayer(e, t);
|
|
740
|
+
const E = !!s;
|
|
645
741
|
const S = !!e[STORE_VALUE][$TARGET];
|
|
646
|
-
const
|
|
742
|
+
const f = s ?? e[STORE_VALUE];
|
|
647
743
|
if (!i) {
|
|
648
|
-
const n = Object.getOwnPropertyDescriptor(
|
|
744
|
+
const n = Object.getOwnPropertyDescriptor(f, t);
|
|
649
745
|
if (n && n.get) return n.get.call(r);
|
|
650
|
-
if (!n && !
|
|
651
|
-
const e = unwrapStoreValue(
|
|
746
|
+
if (!n && !E && e[STORE_CUSTOM_PROTO]) {
|
|
747
|
+
const e = unwrapStoreValue(f);
|
|
652
748
|
if (hasInheritedAccessor(e, t)) {
|
|
653
|
-
return Reflect.get(
|
|
749
|
+
return Reflect.get(f, t, r);
|
|
654
750
|
}
|
|
655
751
|
}
|
|
656
752
|
}
|
|
657
753
|
if (writeOnly(r)) {
|
|
658
754
|
if (isPrototypePollutionKey(t) && !hasOwnStoreProperty(e, t)) return undefined;
|
|
659
|
-
let r = i && (
|
|
755
|
+
let r = i && (E || !S) ? visibleNodeValue(i) : f[t];
|
|
660
756
|
r === $DELETED && (r = undefined);
|
|
661
757
|
if (!isWrappable(r)) return r;
|
|
758
|
+
// Shallow boundary: records are replaced, never edited in place. Reads
|
|
759
|
+
// inside a setter serve the raw so read-then-replace, filter/pop and
|
|
760
|
+
// projection derives all work; in-place mutation of a raw is inert by
|
|
761
|
+
// construction — the same contract as a markRaw child in a deep store.
|
|
762
|
+
if (e[STORE_SHALLOW]) return r;
|
|
662
763
|
const n = wrap(r, e);
|
|
663
764
|
Writing?.add(n);
|
|
664
765
|
return n;
|
|
665
766
|
}
|
|
666
|
-
let
|
|
667
|
-
|
|
767
|
+
let R = i ? E || !S ? read(o[t]) : (read(o[t]), f[t]) : f[t];
|
|
768
|
+
R === $DELETED && (R = undefined);
|
|
668
769
|
if (!i) {
|
|
669
|
-
if (!
|
|
770
|
+
if (!E && typeof R === "function" && !Object.prototype.hasOwnProperty.call(f, t)) {
|
|
670
771
|
let t;
|
|
671
|
-
return !Array.isArray(e[STORE_VALUE]) && (t = Object.getPrototypeOf(e[STORE_VALUE])) && t !== Object.prototype ?
|
|
772
|
+
return !Array.isArray(e[STORE_VALUE]) && (t = Object.getPrototypeOf(e[STORE_VALUE])) && t !== Object.prototype ? R.bind(f) : R;
|
|
672
773
|
} else if (getObserver() && !n) {
|
|
673
|
-
return read(getNode(e, o, t, isWrappable(
|
|
774
|
+
return read(getNode(e, o, t, isWrappable(R) ? wrap(R, e) : R, isEqual, e[STORE_SNAPSHOT_PROPS]));
|
|
674
775
|
}
|
|
675
776
|
}
|
|
676
777
|
// Untracked fall-through (tracked reads already threw via their node in
|
|
677
778
|
// read(); the dev strictRead error above wins first for memo parity).
|
|
678
|
-
|
|
679
|
-
|
|
779
|
+
// Observer-present reads must NOT re-consult the flag here: during the
|
|
780
|
+
// settle flush the firewall has recomputed (first values live on the
|
|
781
|
+
// pending rail, served by read() above) but its UNINITIALIZED clear is
|
|
782
|
+
// deferred to batch commit — vetoing read()'s verdict with the stale flag
|
|
783
|
+
// threw a fresh NotReadyError for an already-settled source, which no
|
|
784
|
+
// sweep would ever release (#2938: projection over an async store wedged
|
|
785
|
+
// its Loading boundary on `undefined`).
|
|
786
|
+
if (!n && !getObserver()) throwIfUninitialized(e);
|
|
787
|
+
return isWrappable(R) ? wrap(R, e) : R;
|
|
680
788
|
},
|
|
681
789
|
has(e, t) {
|
|
682
790
|
if (t === $PROXY || t === $TRACK || t === "__proto__") return true;
|
|
@@ -703,55 +811,67 @@ const storeTraps = {
|
|
|
703
811
|
const n = e[$PROXY];
|
|
704
812
|
if (writeOnly(n)) {
|
|
705
813
|
untrack(() => {
|
|
706
|
-
const {base: o, overrideKey: i, state:
|
|
707
|
-
const
|
|
708
|
-
const
|
|
709
|
-
const S =
|
|
710
|
-
|
|
814
|
+
const {base: o, overrideKey: i, state: O} = prepareStoreWrite(e, n, t);
|
|
815
|
+
const s = getOverlayLayer(e, t);
|
|
816
|
+
const E = s ? s[t] : o;
|
|
817
|
+
const S = s ? s[t] !== $DELETED : t in e[STORE_VALUE];
|
|
818
|
+
// Shallow slots hold store proxies verbatim (pass-through reference,
|
|
819
|
+
// never raw-marked — see markRawOne/#2932); everything else unwraps
|
|
820
|
+
// and marks as usual.
|
|
821
|
+
const f = !!e[STORE_SHALLOW] && r?.[$TARGET] !== undefined;
|
|
822
|
+
const R = f ? r : unwrapStoreValue(r);
|
|
823
|
+
if (e[STORE_SHALLOW] && !f && isWrappable(R)) {
|
|
824
|
+
// Flip the live gate too: a bare add was inert unless something else
|
|
825
|
+
// had already marked a raw somewhere (wrap() checks rawValuesUsed
|
|
826
|
+
// first), so the documented set-trap ingest mark silently no-oped in
|
|
827
|
+
// apps whose only shallow data arrived through writes.
|
|
828
|
+
rawValuesUsed = true;
|
|
829
|
+
rawValues.add(R);
|
|
830
|
+
}
|
|
711
831
|
// Symbol-keyed writes on arrays are metadata, not index writes — never run
|
|
712
832
|
// them through the numeric index/length machinery (`parseInt` on a symbol
|
|
713
833
|
// throws). #2769
|
|
714
|
-
const
|
|
715
|
-
const
|
|
716
|
-
const
|
|
717
|
-
const
|
|
718
|
-
const l =
|
|
719
|
-
if (
|
|
834
|
+
const c = typeof t === "string" ? Number(t) : -1;
|
|
835
|
+
const T = Array.isArray(O) && Number.isInteger(c) && c >= 0 && c < 4294967295 && String(c) === t;
|
|
836
|
+
const u = T ? c + 1 : 0;
|
|
837
|
+
const a = T && (getOverlayLayer(e, "length") ?? O).length;
|
|
838
|
+
const l = T && u > a ? u : undefined;
|
|
839
|
+
if (E === R && l === undefined) return true;
|
|
720
840
|
armOptimisticStoreWrite(e, n);
|
|
721
|
-
if (
|
|
841
|
+
if (R !== undefined && R === o && l === undefined) {
|
|
722
842
|
delete e[i]?.[t];
|
|
723
843
|
if (i === STORE_OPTIMISTIC_OVERRIDE) delete e[STORE_OPTIMISTIC_OWNERS]?.[t];
|
|
724
844
|
} else {
|
|
725
845
|
const r = e[i] || (e[i] = Object.create(null));
|
|
726
|
-
r[t] =
|
|
846
|
+
r[t] = R;
|
|
727
847
|
stampOptimisticOwner(e, i, t);
|
|
728
848
|
if (l !== undefined) {
|
|
729
849
|
r.length = l;
|
|
730
850
|
stampOptimisticOwner(e, i, "length");
|
|
731
851
|
}
|
|
732
852
|
}
|
|
733
|
-
notifyStoreProperty(e, t, "set",
|
|
853
|
+
notifyStoreProperty(e, t, "set", R, E, S);
|
|
734
854
|
// Shrinking an array's length must remove the truncated indices, otherwise
|
|
735
855
|
// they leak through `has`, `ownKeys`, and (tracked) index reads from the
|
|
736
856
|
// underlying value. Mark each as deleted and notify so reactive reads update. #2768
|
|
737
|
-
if (Array.isArray(
|
|
857
|
+
if (Array.isArray(O) && t === "length" && typeof R === "number" && typeof E === "number" && R < E) {
|
|
738
858
|
const t = e[i] || (e[i] = Object.create(null));
|
|
739
|
-
for (let r =
|
|
859
|
+
for (let r = R; r < E; r++) {
|
|
740
860
|
if (t[r] === $DELETED) continue;
|
|
741
|
-
const n = r in t ? t[r] :
|
|
742
|
-
if (!(r in t) && !(r in
|
|
861
|
+
const n = r in t ? t[r] : O[r];
|
|
862
|
+
if (!(r in t) && !(r in O)) continue;
|
|
743
863
|
t[r] = $DELETED;
|
|
744
864
|
stampOptimisticOwner(e, i, r);
|
|
745
865
|
notifyStoreProperty(e, r, "delete", undefined, n, true);
|
|
746
866
|
}
|
|
747
867
|
}
|
|
748
868
|
// notify length change
|
|
749
|
-
if (Array.isArray(
|
|
869
|
+
if (Array.isArray(O) && t !== "length" && l !== undefined) {
|
|
750
870
|
const t = getNodes(e, STORE_NODE);
|
|
751
871
|
if (t.length) {
|
|
752
872
|
setSignal(t.length, l);
|
|
753
873
|
} else if (!projectionWriteActive && !e[STORE_OPTIMISTIC]) {
|
|
754
|
-
const r = upsertStoreNode(e, t, "length",
|
|
874
|
+
const r = upsertStoreNode(e, t, "length", a, e[STORE_SNAPSHOT_PROPS]);
|
|
755
875
|
setSignal(r, l);
|
|
756
876
|
}
|
|
757
877
|
}
|
|
@@ -767,11 +887,11 @@ const storeTraps = {
|
|
|
767
887
|
untrack(() => {
|
|
768
888
|
const {base: o, overrideKey: i} = prepareStoreWrite(e, n, t);
|
|
769
889
|
armOptimisticStoreWrite(e, n);
|
|
770
|
-
const
|
|
890
|
+
const O = "value" in r ? {
|
|
771
891
|
...r,
|
|
772
892
|
value: unwrapStoreValue(r.value)
|
|
773
893
|
} : r;
|
|
774
|
-
Object.defineProperty(e[i] || (e[i] = Object.create(null)), t,
|
|
894
|
+
Object.defineProperty(e[i] || (e[i] = Object.create(null)), t, O);
|
|
775
895
|
stampOptimisticOwner(e, i, t);
|
|
776
896
|
notifyStoreProperty(e, t, "invalidate");
|
|
777
897
|
if (false) ;
|
|
@@ -899,7 +1019,7 @@ function storeSetter(e, t) {
|
|
|
899
1019
|
}
|
|
900
1020
|
|
|
901
1021
|
function createStore(e, t, r) {
|
|
902
|
-
const n = typeof e === "function", o = n ? createProjectionInternal(e, t, r).store : wrap(e);
|
|
1022
|
+
const n = typeof e === "function", o = n ? createProjectionInternal(e, t, r).store : t?.shallow ? wrapShallow(e) : wrap(e);
|
|
903
1023
|
return [ o, n ? e => {
|
|
904
1024
|
// Mark the projection as manually written before notifying property nodes.
|
|
905
1025
|
suppressComputedRecompute(o[$REFRESH]);
|
|
@@ -907,4 +1027,4 @@ function createStore(e, t, r) {
|
|
|
907
1027
|
} : e => storeSetter(o, e) ];
|
|
908
1028
|
}
|
|
909
1029
|
|
|
910
|
-
export { $AFFECTS, $DELETED, $PROXY, $TARGET, $TRACK, STORE_CUSTOM_PROTO, STORE_DESC, STORE_FIREWALL, STORE_HAS, STORE_LOOKUP, STORE_NODE, STORE_OPTIMISTIC, STORE_OPTIMISTIC_OVERRIDE, STORE_OPTIMISTIC_OWNERS, STORE_OVERRIDE, STORE_PARENT, STORE_VALUE, STORE_WRAP, createStore, createStoreProxy, getKeys, getOverlayLayer, getPropertyDescriptor, getStoreAffectsNodes, getStoreKeys, getStoreSymbols, isWrappable, mergedOverlay, notifySelf, ownEnumerableKeys, setWriteOverride, storeLookup, storeSetter, storeTraps, symbolKeyedRecords, trackSelf, visibleNodeValue, witnessAffectsMark, wrap };
|
|
1030
|
+
export { $AFFECTS, $DELETED, $PROXY, $TARGET, $TRACK, STORE_CUSTOM_PROTO, STORE_DESC, STORE_FIREWALL, STORE_HAS, STORE_LOOKUP, STORE_NODE, STORE_OPTIMISTIC, STORE_OPTIMISTIC_OVERRIDE, STORE_OPTIMISTIC_OWNERS, STORE_OVERRIDE, STORE_PARENT, STORE_SHALLOW, STORE_VALUE, STORE_WRAP, createStore, createStoreProxy, getKeys, getOverlayLayer, getPropertyDescriptor, getStoreAffectsNodes, getStoreKeys, getStoreSymbols, isRawValue, isWrappable, lookupTarget, markRawIngest, mergedOverlay, notifySelf, ownEnumerableKeys, rawValuesUsed, setWriteOverride, storeLookup, storeSetter, storeTraps, symbolKeyedRecords, trackSelf, visibleNodeValue, witnessAffectsMark, wrap, wrapShallow };
|