@solidjs/signals 2.0.0-beta.19 → 2.0.0-beta.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.js +670 -371
- package/dist/node.cjs +1878 -1606
- package/dist/prod/affects.js +17 -21
- package/dist/prod/core/action.js +19 -6
- package/dist/prod/core/async.js +57 -71
- package/dist/prod/core/constants.js +15 -1
- package/dist/prod/core/core.js +39 -39
- package/dist/prod/core/effect.js +14 -14
- package/dist/prod/core/graph.js +1 -1
- package/dist/prod/core/heap.js +2 -2
- package/dist/prod/core/lanes.js +23 -13
- package/dist/prod/core/optimistic.js +107 -99
- package/dist/prod/core/owner.js +23 -23
- package/dist/prod/core/scheduler.js +180 -180
- package/dist/prod/core/verdict.js +12 -13
- package/dist/prod/map.js +196 -167
- package/dist/prod/signals.js +1 -1
- package/dist/prod/store/optimistic.js +103 -68
- package/dist/prod/store/reconcile.js +80 -46
- package/dist/prod/store/store.js +204 -73
- package/dist/prod/store/utils.js +61 -42
- package/dist/types/core/action.d.ts +19 -6
- package/dist/types/core/constants.d.ts +12 -0
- package/dist/types/core/dev.d.ts +7 -0
- package/dist/types/core/lanes.d.ts +2 -0
- package/dist/types/core/scheduler.d.ts +2 -6
- package/dist/types/core/types.d.ts +9 -1
- package/dist/types/store/store.d.ts +8 -2
- package/dist/types-cjs/core/action.d.cts +19 -6
- package/dist/types-cjs/core/constants.d.cts +12 -0
- package/dist/types-cjs/core/dev.d.cts +7 -0
- package/dist/types-cjs/core/lanes.d.cts +2 -0
- package/dist/types-cjs/core/scheduler.d.cts +2 -6
- package/dist/types-cjs/core/types.d.cts +9 -1
- package/dist/types-cjs/store/store.d.cts +8 -2
- package/package.json +1 -1
package/dist/prod/store/store.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import { $REFRESH, STORE_SNAPSHOT_PROPS, NOT_PENDING, STATUS_PENDING, NO_SNAPSHOT } from "../core/constants.js";
|
|
1
|
+
import { $REFRESH, STORE_SNAPSHOT_PROPS, NOT_PENDING, unwrapOverride, STATUS_UNINITIALIZED, STATUS_PENDING, NO_SNAPSHOT } from "../core/constants.js";
|
|
2
2
|
|
|
3
3
|
import { suppressComputedRecompute, isEqual, signal, pendingCheckActive, untrack, setSignal, read, snapshotCaptureActive, snapshotSources } from "../core/core.js";
|
|
4
4
|
|
|
5
5
|
import { DEV } from "../core/dev.js";
|
|
6
6
|
|
|
7
|
+
import { NotReadyError } from "../core/error.js";
|
|
8
|
+
|
|
7
9
|
import { getObserver } from "../core/owner.js";
|
|
8
10
|
|
|
9
|
-
import { GlobalQueue, registerTransientStoreNode, projectionWriteActive, globalQueue } from "../core/scheduler.js";
|
|
11
|
+
import { GlobalQueue, registerTransientStoreNode, projectionWriteActive, activeTransition, globalQueue } from "../core/scheduler.js";
|
|
10
12
|
|
|
11
13
|
import "../core/invariants.js";
|
|
12
14
|
|
|
@@ -26,7 +28,7 @@ import { createProjectionInternal } from "./projection.js";
|
|
|
26
28
|
// the record witnesses it into the active isPending() probe.
|
|
27
29
|
$AFFECTS = Symbol(0);
|
|
28
30
|
|
|
29
|
-
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";
|
|
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";
|
|
30
32
|
|
|
31
33
|
const STORE_SELF_PENDING = Symbol(0);
|
|
32
34
|
|
|
@@ -56,9 +58,17 @@ const storeLookup = new WeakMap;
|
|
|
56
58
|
const symbolKeyedRecords = new WeakSet;
|
|
57
59
|
|
|
58
60
|
function wrap(e, t) {
|
|
59
|
-
if (t?.[STORE_WRAP])
|
|
61
|
+
if (t?.[STORE_WRAP]) {
|
|
62
|
+
const r = t[STORE_WRAP](e, t);
|
|
63
|
+
const n = r[$TARGET];
|
|
64
|
+
if (n && !n[STORE_PARENT] && n !== t) n[STORE_PARENT] = t;
|
|
65
|
+
return r;
|
|
66
|
+
}
|
|
60
67
|
let r = e[$PROXY] || storeLookup.get(e);
|
|
61
|
-
if (!r)
|
|
68
|
+
if (!r) {
|
|
69
|
+
storeLookup.set(e, r = createStoreProxy(e));
|
|
70
|
+
if (t) r[$TARGET][STORE_PARENT] = t;
|
|
71
|
+
}
|
|
62
72
|
return r;
|
|
63
73
|
}
|
|
64
74
|
|
|
@@ -91,7 +101,7 @@ function unwrapStoreValue(e, t, r) {
|
|
|
91
101
|
const E = s ? [] : Object.create(Object.getPrototypeOf(i));
|
|
92
102
|
t.set(e, E);
|
|
93
103
|
r = n[STORE_LOOKUP] ?? storeLookup;
|
|
94
|
-
for (const e of
|
|
104
|
+
for (const e of getStoreKeys(i, o)) {
|
|
95
105
|
if (s && e === "length") continue;
|
|
96
106
|
const n = e in o ? o[e] : i[e];
|
|
97
107
|
if (n !== $DELETED) E[e] = unwrapStoreValue(n, t, r);
|
|
@@ -109,6 +119,23 @@ function ownEnumerableKeys(e) {
|
|
|
109
119
|
return Reflect.ownKeys(e).filter(t => Object.prototype.propertyIsEnumerable.call(e, t));
|
|
110
120
|
}
|
|
111
121
|
|
|
122
|
+
function ownEnumerableSymbols(e) {
|
|
123
|
+
const t = Object.getOwnPropertySymbols(e);
|
|
124
|
+
const r = [];
|
|
125
|
+
for (let n = 0, o = t.length; n < o; n++) {
|
|
126
|
+
const o = t[n];
|
|
127
|
+
if (Object.prototype.propertyIsEnumerable.call(e, o)) r.push(o);
|
|
128
|
+
}
|
|
129
|
+
return r;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Plain-object variant that keeps Object.keys() as the fast path and only pays
|
|
133
|
+
// descriptor checks for symbols. Do not use this on store proxies: splitting
|
|
134
|
+
// strings/symbols would invoke their ownKeys trap twice.
|
|
135
|
+
function ownEnumerableKeysPlain(e) {
|
|
136
|
+
return Object.keys(e).concat(ownEnumerableSymbols(e));
|
|
137
|
+
}
|
|
138
|
+
|
|
112
139
|
/**
|
|
113
140
|
* Single chokepoint for the store's layered value resolution: returns the
|
|
114
141
|
* override layer (optimistic first, then regular) that shadows `property`, or
|
|
@@ -127,7 +154,7 @@ function ownEnumerableKeys(e) {
|
|
|
127
154
|
* The value a store leaf's backing signal currently shows to readers: active
|
|
128
155
|
* override, else held pending value, else committed value.
|
|
129
156
|
*/ function visibleNodeValue(e) {
|
|
130
|
-
return e.be !== undefined && e.be !== NOT_PENDING ? e.be : e.ge !== NOT_PENDING ? e.ge : e.Ue;
|
|
157
|
+
return e.be !== undefined && e.be !== NOT_PENDING ? unwrapOverride(e.be) : e.ge !== NOT_PENDING ? e.ge : e.Ue;
|
|
131
158
|
}
|
|
132
159
|
|
|
133
160
|
function hasOwnStoreProperty(e, t) {
|
|
@@ -186,24 +213,34 @@ function getNode(e, t, r, n, o = isEqual, i) {
|
|
|
186
213
|
snapshotSources?.add(s);
|
|
187
214
|
}
|
|
188
215
|
if (typeof r === "symbol" && r !== $TRACK && r !== $AFFECTS) symbolKeyedRecords.add(t);
|
|
189
|
-
// A node born inside a live
|
|
216
|
+
// A node born inside a live mark's identity scope inherits the mark
|
|
190
217
|
// (the declaration walk could only cover nodes that existed then). The
|
|
191
218
|
// record's own $AFFECTS carrier is the mark's channel, never a member.
|
|
192
|
-
if (r !== $AFFECTS && affectsScopes.size) inheritAffectsMarks(s, e[STORE_VALUE]);
|
|
219
|
+
if (r !== $AFFECTS && affectsScopes.size) inheritAffectsMarks(s, e[STORE_VALUE], r);
|
|
220
|
+
// Node presence bubbles up the wrap chain (sticky), so reconcile can see
|
|
221
|
+
// "subscribers live somewhere below" through node-less intermediate
|
|
222
|
+
// records — the captured-proxy diff gate (#2902). Amortized O(1): stops at
|
|
223
|
+
// the first already-flagged ancestor.
|
|
224
|
+
let E = e;
|
|
225
|
+
while (E && !E[STORE_DESC]) {
|
|
226
|
+
E[STORE_DESC] = true;
|
|
227
|
+
E = E[STORE_PARENT];
|
|
228
|
+
}
|
|
193
229
|
return t[r] = s;
|
|
194
230
|
}
|
|
195
231
|
|
|
196
232
|
/**
|
|
197
|
-
* Scope inheritance for late-created nodes: every live
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
|
|
233
|
+
* Scope inheritance for late-created nodes: every live mark whose identity
|
|
234
|
+
* scope contains the owning record's raw — and, for keyed marks, whose key
|
|
235
|
+
* is this property — gets counted on the new node. Inherited marks live
|
|
236
|
+
* exactly as long as the scope's carrier — the release hook below drops
|
|
237
|
+
* them with the entry.
|
|
238
|
+
*/ function inheritAffectsMarks(e, t, r) {
|
|
202
239
|
// A live scope exists, so affects.ts already installed the mark engine.
|
|
203
|
-
for (const [
|
|
204
|
-
if (
|
|
240
|
+
for (const [n, o] of affectsScopes) {
|
|
241
|
+
if (n.M && o.scope.has(t) && (o.key === undefined || o.key === r)) {
|
|
205
242
|
GlobalQueue.D(e);
|
|
206
|
-
|
|
243
|
+
o.inherited.push(e);
|
|
207
244
|
}
|
|
208
245
|
}
|
|
209
246
|
}
|
|
@@ -235,7 +272,10 @@ o) {
|
|
|
235
272
|
collectRecordNodes(i[STORE_NODE], r);
|
|
236
273
|
collectRecordNodes(i[STORE_HAS], r);
|
|
237
274
|
E = mergedOverlay(i);
|
|
238
|
-
|
|
275
|
+
// Carry the effective lookup into untouched descendants. Default stores
|
|
276
|
+
// use the global lookup just like snapshotImpl; without it, nested raw
|
|
277
|
+
// objects fall back to string-only enumeration and symbol branches vanish.
|
|
278
|
+
n = i[STORE_LOOKUP] ?? n ?? storeLookup;
|
|
239
279
|
}
|
|
240
280
|
if (Array.isArray(s)) {
|
|
241
281
|
const e = E?.length ?? s.length;
|
|
@@ -243,8 +283,18 @@ o) {
|
|
|
243
283
|
const e = E && i in E ? E[i] : s[i];
|
|
244
284
|
if (e !== $DELETED) walkAffectsScope(e, t, r, n, o);
|
|
245
285
|
}
|
|
286
|
+
// Arrays can also carry symbol metadata. Enumerate symbols separately to
|
|
287
|
+
// avoid scanning large index lists twice. Outside a store tree, retain the
|
|
288
|
+
// existing index-only walk.
|
|
289
|
+
const O = i || n ? getStoreSymbols(s, E) : [];
|
|
290
|
+
for (let e = 0, i = O.length; e < i; e++) {
|
|
291
|
+
const i = O[e];
|
|
292
|
+
const c = getPropertyDescriptor(s, E, i);
|
|
293
|
+
if (!c || c.get) continue;
|
|
294
|
+
walkAffectsScope(c.value, t, r, n, o);
|
|
295
|
+
}
|
|
246
296
|
} else {
|
|
247
|
-
const e = getKeys(s, E);
|
|
297
|
+
const e = i || n ? getStoreKeys(s, E) : getKeys(s, E);
|
|
248
298
|
for (let i = 0, O = e.length; i < O; i++) {
|
|
249
299
|
const O = getPropertyDescriptor(s, E, e[i]);
|
|
250
300
|
if (!O || O.get) continue;
|
|
@@ -273,15 +323,15 @@ o) {
|
|
|
273
323
|
* Callers guard on `pendingCheckActive`, so plain reads never pay for this.
|
|
274
324
|
*
|
|
275
325
|
* @internal
|
|
276
|
-
*/ function witnessAffectsMark(e) {
|
|
326
|
+
*/ function witnessAffectsMark(e, t) {
|
|
277
327
|
// Callers guard on `pendingCheckActive`, which only flips inside
|
|
278
328
|
// isPending() — the verdict layer is loaded and its hook installed.
|
|
279
|
-
const
|
|
280
|
-
if (
|
|
329
|
+
const r = e[STORE_NODE]?.[$AFFECTS];
|
|
330
|
+
if (r?.M) GlobalQueue.wt(r);
|
|
281
331
|
if (affectsScopes.size) {
|
|
282
|
-
const
|
|
283
|
-
for (const [e,
|
|
284
|
-
if (e !==
|
|
332
|
+
const n = e[STORE_VALUE];
|
|
333
|
+
for (const [e, o] of affectsScopes) {
|
|
334
|
+
if (e !== r && e.M && o.scope.has(n) && (o.key === undefined || o.key === t)) GlobalQueue.wt(e);
|
|
285
335
|
}
|
|
286
336
|
}
|
|
287
337
|
}
|
|
@@ -298,14 +348,14 @@ o) {
|
|
|
298
348
|
* @internal
|
|
299
349
|
*/ function getStoreAffectsNodes(e, t) {
|
|
300
350
|
const r = getNodes(e, STORE_NODE);
|
|
351
|
+
GlobalQueue.T ||= e => {
|
|
352
|
+
const t = affectsScopes.get(e);
|
|
353
|
+
if (!t) return;
|
|
354
|
+
affectsScopes.delete(e);
|
|
355
|
+
for (let e = 0; e < t.inherited.length; e++) GlobalQueue.F(t.inherited[e]);
|
|
356
|
+
};
|
|
301
357
|
if (t === undefined) {
|
|
302
358
|
const t = getNode(e, r, $AFFECTS, undefined, false);
|
|
303
|
-
GlobalQueue.T ||= e => {
|
|
304
|
-
const t = affectsScopes.get(e);
|
|
305
|
-
if (!t) return;
|
|
306
|
-
affectsScopes.delete(e);
|
|
307
|
-
for (let e = 0; e < t.inherited.length; e++) GlobalQueue.F(t.inherited[e]);
|
|
308
|
-
};
|
|
309
359
|
let n = affectsScopes.get(t);
|
|
310
360
|
if (!n) affectsScopes.set(t, n = {
|
|
311
361
|
scope: new Set,
|
|
@@ -315,11 +365,25 @@ o) {
|
|
|
315
365
|
walkAffectsScope(e[$PROXY], n, o, e[STORE_LOOKUP], new Set);
|
|
316
366
|
return o;
|
|
317
367
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
368
|
+
let n = r[t];
|
|
369
|
+
if (!n) {
|
|
370
|
+
const o = getOverlayLayer(e, t);
|
|
371
|
+
const i = o ? o[t] : e[STORE_VALUE][t];
|
|
372
|
+
n = upsertStoreNode(e, r, t, i === $DELETED ? undefined : i, e[STORE_SNAPSHOT_PROPS]);
|
|
373
|
+
}
|
|
374
|
+
// Keyed marks resolve by identity too (#2904): another store family's
|
|
375
|
+
// proxy can share this record's raw (a derived store swaps its backing to
|
|
376
|
+
// the source's raw when its projection lands), and reads through it never
|
|
377
|
+
// touch this target's node map. Scope is exactly the owning record's raw,
|
|
378
|
+
// narrowed to this key for witness and birth inheritance.
|
|
379
|
+
let o = affectsScopes.get(n);
|
|
380
|
+
if (!o) affectsScopes.set(n, o = {
|
|
381
|
+
scope: new Set,
|
|
382
|
+
inherited: [],
|
|
383
|
+
key: t
|
|
384
|
+
});
|
|
385
|
+
o.scope.add(e[STORE_VALUE]);
|
|
386
|
+
return [ n ];
|
|
323
387
|
}
|
|
324
388
|
|
|
325
389
|
function trackSelf(e, t = $TRACK) {
|
|
@@ -354,17 +418,36 @@ function notifySelf(e) {
|
|
|
354
418
|
} : r ?? t;
|
|
355
419
|
}
|
|
356
420
|
|
|
357
|
-
function
|
|
421
|
+
function getKeysImpl(e, t, r, n) {
|
|
358
422
|
// Plain objects can't trigger proxy traps — only pay for the untrack
|
|
359
423
|
// closure when the source is itself a wrapped store (store-in-store).
|
|
360
|
-
const
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
424
|
+
const o = e[$TARGET] ? untrack(() => r ? n ? ownEnumerableKeys(e) : Object.keys(e) : Reflect.ownKeys(e)) : r ? n ? ownEnumerableKeysPlain(e) : Object.keys(e) : Reflect.ownKeys(e);
|
|
425
|
+
return t ? mergeOverrideKeys(o, t) : o;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function getKeys(e, t, r = true) {
|
|
429
|
+
return getKeysImpl(e, t, r, false);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function getStoreKeys(e, t) {
|
|
433
|
+
return getKeysImpl(e, t, true, true);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function getStoreSymbols(e, t) {
|
|
437
|
+
const r = e[$TARGET] ? untrack(() => ownEnumerableSymbols(e)) : ownEnumerableSymbols(e);
|
|
438
|
+
return t ? mergeOverrideKeys(r, t, true) : r;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Shared override-layer merge for key enumeration: adds live override keys,
|
|
442
|
+
// drops $DELETED ones. `symbolsOnly` scopes the override scan for the
|
|
443
|
+
// array-metadata passes.
|
|
444
|
+
function mergeOverrideKeys(e, t, r) {
|
|
445
|
+
const n = new Set(e);
|
|
446
|
+
const o = r ? Object.getOwnPropertySymbols(t) : Reflect.ownKeys(t);
|
|
447
|
+
for (const e of o) {
|
|
448
|
+
if (t[e] !== $DELETED) n.add(e); else n.delete(e);
|
|
366
449
|
}
|
|
367
|
-
return Array.from(
|
|
450
|
+
return Array.from(n);
|
|
368
451
|
}
|
|
369
452
|
|
|
370
453
|
function getPropertyDescriptor(e, t, r) {
|
|
@@ -424,10 +507,22 @@ function prepareStoreWrite(e, t, r) {
|
|
|
424
507
|
// STORE_OPTIMISTIC is only set by createOptimisticStore, which installs the
|
|
425
508
|
// optimistic engine before wrapping.
|
|
426
509
|
if (e[STORE_OPTIMISTIC] && !projectionWriteActive) {
|
|
427
|
-
GlobalQueue.
|
|
510
|
+
GlobalQueue.mn(t);
|
|
428
511
|
}
|
|
429
512
|
}
|
|
430
513
|
|
|
514
|
+
/**
|
|
515
|
+
* Records which transition owns an optimistic layer entry (#2899), so a
|
|
516
|
+
* settling action only consumes its own keys — the layer is store-wide, but
|
|
517
|
+
* concurrent actions writing disjoint keys must revert independently, exactly
|
|
518
|
+
* like optimistic signal nodes do via the transition's _optimisticNodes.
|
|
519
|
+
* `activeTransition` is the write's transaction (action() opens it before the
|
|
520
|
+
* body runs); null marks an ambient write that clears at plain flush end.
|
|
521
|
+
* Same-key writes across actions keep last-write-wins layer semantics.
|
|
522
|
+
*/ function stampOptimisticOwner(e, t, r) {
|
|
523
|
+
if (t === STORE_OPTIMISTIC_OVERRIDE) (e[STORE_OPTIMISTIC_OWNERS] ??= Object.create(null))[r] = activeTransition;
|
|
524
|
+
}
|
|
525
|
+
|
|
431
526
|
function upsertStoreNode(e, t, r, n, o) {
|
|
432
527
|
if (t[r]) return t[r];
|
|
433
528
|
const i = isWrappable(n) ? wrap(n, e) : n;
|
|
@@ -475,12 +570,25 @@ function notifyStoreProperty(e, t, r, n, o, i) {
|
|
|
475
570
|
|
|
476
571
|
let Writing = null;
|
|
477
572
|
|
|
573
|
+
/**
|
|
574
|
+
* A derived store's seed is a draft for the derive function, never an
|
|
575
|
+
* observable value (#2897): until the firewall first resolves there is
|
|
576
|
+
* nothing to read, so every consumer path throws NotReady — tracked reads
|
|
577
|
+
* through their node (core read()), and the untracked fall-throughs in the
|
|
578
|
+
* traps through this guard. Returning the seed leaked it; returning
|
|
579
|
+
* `undefined` would break non-nullable types. Callers exempt the firewall
|
|
580
|
+
* itself (the derive function works its own draft while uninitialized).
|
|
581
|
+
*/ function throwIfUninitialized(e) {
|
|
582
|
+
const t = e[STORE_FIREWALL];
|
|
583
|
+
if (t && t.i & STATUS_UNINITIALIZED) throw t.k ?? new NotReadyError(t);
|
|
584
|
+
}
|
|
585
|
+
|
|
478
586
|
const storeTraps = {
|
|
479
587
|
get(e, t, r) {
|
|
480
588
|
if (t === $TARGET) return e;
|
|
481
589
|
if (t === $PROXY) return r;
|
|
482
590
|
if (t === $REFRESH) return e[STORE_FIREWALL];
|
|
483
|
-
if (pendingCheckActive) witnessAffectsMark(e);
|
|
591
|
+
if (pendingCheckActive) witnessAffectsMark(e, t);
|
|
484
592
|
if (t === $TRACK) {
|
|
485
593
|
trackSelf(e);
|
|
486
594
|
return r;
|
|
@@ -495,41 +603,44 @@ const storeTraps = {
|
|
|
495
603
|
const E = getOverlayLayer(e, t);
|
|
496
604
|
const O = !!E;
|
|
497
605
|
const c = !!e[STORE_VALUE][$TARGET];
|
|
498
|
-
const
|
|
606
|
+
const S = E ?? e[STORE_VALUE];
|
|
499
607
|
if (!i) {
|
|
500
|
-
const n = Object.getOwnPropertyDescriptor(
|
|
608
|
+
const n = Object.getOwnPropertyDescriptor(S, t);
|
|
501
609
|
if (n && n.get) return n.get.call(r);
|
|
502
610
|
if (!n && !O && e[STORE_CUSTOM_PROTO]) {
|
|
503
|
-
const e = unwrapStoreValue(
|
|
611
|
+
const e = unwrapStoreValue(S);
|
|
504
612
|
if (hasInheritedAccessor(e, t)) {
|
|
505
|
-
return Reflect.get(
|
|
613
|
+
return Reflect.get(S, t, r);
|
|
506
614
|
}
|
|
507
615
|
}
|
|
508
616
|
}
|
|
509
617
|
if (writeOnly(r)) {
|
|
510
618
|
if (isPrototypePollutionKey(t) && !hasOwnStoreProperty(e, t)) return undefined;
|
|
511
|
-
let r = i && (O || !c) ? visibleNodeValue(i) :
|
|
619
|
+
let r = i && (O || !c) ? visibleNodeValue(i) : S[t];
|
|
512
620
|
r === $DELETED && (r = undefined);
|
|
513
621
|
if (!isWrappable(r)) return r;
|
|
514
622
|
const n = wrap(r, e);
|
|
515
623
|
Writing?.add(n);
|
|
516
624
|
return n;
|
|
517
625
|
}
|
|
518
|
-
let
|
|
519
|
-
|
|
626
|
+
let f = i ? O || !c ? read(o[t]) : (read(o[t]), S[t]) : S[t];
|
|
627
|
+
f === $DELETED && (f = undefined);
|
|
520
628
|
if (!i) {
|
|
521
|
-
if (!O && typeof
|
|
629
|
+
if (!O && typeof f === "function" && !Object.prototype.hasOwnProperty.call(S, t)) {
|
|
522
630
|
let t;
|
|
523
|
-
return !Array.isArray(e[STORE_VALUE]) && (t = Object.getPrototypeOf(e[STORE_VALUE])) && t !== Object.prototype ?
|
|
631
|
+
return !Array.isArray(e[STORE_VALUE]) && (t = Object.getPrototypeOf(e[STORE_VALUE])) && t !== Object.prototype ? f.bind(S) : f;
|
|
524
632
|
} else if (getObserver() && !n) {
|
|
525
|
-
return read(getNode(e, o, t, isWrappable(
|
|
633
|
+
return read(getNode(e, o, t, isWrappable(f) ? wrap(f, e) : f, isEqual, e[STORE_SNAPSHOT_PROPS]));
|
|
526
634
|
}
|
|
527
635
|
}
|
|
528
|
-
|
|
636
|
+
// Untracked fall-through (tracked reads already threw via their node in
|
|
637
|
+
// read(); the dev strictRead error above wins first for memo parity).
|
|
638
|
+
if (!n) throwIfUninitialized(e);
|
|
639
|
+
return isWrappable(f) ? wrap(f, e) : f;
|
|
529
640
|
},
|
|
530
641
|
has(e, t) {
|
|
531
642
|
if (t === $PROXY || t === $TRACK || t === "__proto__") return true;
|
|
532
|
-
if (pendingCheckActive) witnessAffectsMark(e);
|
|
643
|
+
if (pendingCheckActive) witnessAffectsMark(e, t);
|
|
533
644
|
const r = getOverlayLayer(e, t);
|
|
534
645
|
const n = r ? r[t] !== $DELETED : t in e[STORE_VALUE];
|
|
535
646
|
if (writeOnly(e[$PROXY]) || getObserver() === e[STORE_FIREWALL]) return n;
|
|
@@ -544,6 +655,7 @@ const storeTraps = {
|
|
|
544
655
|
if (getObserver()) {
|
|
545
656
|
return read(getNode(e, o, t, n));
|
|
546
657
|
}
|
|
658
|
+
throwIfUninitialized(e);
|
|
547
659
|
return n;
|
|
548
660
|
},
|
|
549
661
|
set(e, t, r) {
|
|
@@ -555,33 +667,41 @@ const storeTraps = {
|
|
|
555
667
|
const E = getOverlayLayer(e, t);
|
|
556
668
|
const O = E ? E[t] : o;
|
|
557
669
|
const c = E ? E[t] !== $DELETED : t in e[STORE_VALUE];
|
|
558
|
-
const
|
|
670
|
+
const S = unwrapStoreValue(r);
|
|
559
671
|
// Symbol-keyed writes on arrays are metadata, not index writes — never run
|
|
560
672
|
// them through the numeric index/length machinery (`parseInt` on a symbol
|
|
561
673
|
// throws). #2769
|
|
562
|
-
const
|
|
563
|
-
const
|
|
564
|
-
const
|
|
565
|
-
const u =
|
|
566
|
-
const l =
|
|
567
|
-
if (O ===
|
|
674
|
+
const f = typeof t === "string" ? Number(t) : -1;
|
|
675
|
+
const T = Array.isArray(s) && Number.isInteger(f) && f >= 0 && f < 4294967295 && String(f) === t;
|
|
676
|
+
const R = T ? f + 1 : 0;
|
|
677
|
+
const u = T && (getOverlayLayer(e, "length") ?? s).length;
|
|
678
|
+
const l = T && R > u ? R : undefined;
|
|
679
|
+
if (O === S && l === undefined) return true;
|
|
568
680
|
armOptimisticStoreWrite(e, n);
|
|
569
|
-
if (
|
|
681
|
+
if (S !== undefined && S === o && l === undefined) {
|
|
682
|
+
delete e[i]?.[t];
|
|
683
|
+
if (i === STORE_OPTIMISTIC_OVERRIDE) delete e[STORE_OPTIMISTIC_OWNERS]?.[t];
|
|
684
|
+
} else {
|
|
570
685
|
const r = e[i] || (e[i] = Object.create(null));
|
|
571
|
-
r[t] =
|
|
572
|
-
|
|
686
|
+
r[t] = S;
|
|
687
|
+
stampOptimisticOwner(e, i, t);
|
|
688
|
+
if (l !== undefined) {
|
|
689
|
+
r.length = l;
|
|
690
|
+
stampOptimisticOwner(e, i, "length");
|
|
691
|
+
}
|
|
573
692
|
}
|
|
574
|
-
notifyStoreProperty(e, t, "set",
|
|
693
|
+
notifyStoreProperty(e, t, "set", S, O, c);
|
|
575
694
|
// Shrinking an array's length must remove the truncated indices, otherwise
|
|
576
695
|
// they leak through `has`, `ownKeys`, and (tracked) index reads from the
|
|
577
696
|
// underlying value. Mark each as deleted and notify so reactive reads update. #2768
|
|
578
|
-
if (Array.isArray(s) && t === "length" && typeof
|
|
697
|
+
if (Array.isArray(s) && t === "length" && typeof S === "number" && typeof O === "number" && S < O) {
|
|
579
698
|
const t = e[i] || (e[i] = Object.create(null));
|
|
580
|
-
for (let r =
|
|
699
|
+
for (let r = S; r < O; r++) {
|
|
581
700
|
if (t[r] === $DELETED) continue;
|
|
582
701
|
const n = r in t ? t[r] : s[r];
|
|
583
702
|
if (!(r in t) && !(r in s)) continue;
|
|
584
703
|
t[r] = $DELETED;
|
|
704
|
+
stampOptimisticOwner(e, i, r);
|
|
585
705
|
notifyStoreProperty(e, r, "delete", undefined, n, true);
|
|
586
706
|
}
|
|
587
707
|
}
|
|
@@ -612,6 +732,7 @@ const storeTraps = {
|
|
|
612
732
|
value: unwrapStoreValue(r.value)
|
|
613
733
|
} : r;
|
|
614
734
|
Object.defineProperty(e[i] || (e[i] = Object.create(null)), t, s);
|
|
735
|
+
stampOptimisticOwner(e, i, t);
|
|
615
736
|
notifyStoreProperty(e, t, "invalidate");
|
|
616
737
|
if (false) ;
|
|
617
738
|
});
|
|
@@ -632,9 +753,11 @@ const storeTraps = {
|
|
|
632
753
|
if (t in e[STORE_VALUE] || e[STORE_OVERRIDE] && t in e[STORE_OVERRIDE]) {
|
|
633
754
|
armOptimisticStoreWrite(e, e[$PROXY]);
|
|
634
755
|
(e[n] || (e[n] = Object.create(null)))[t] = $DELETED;
|
|
756
|
+
stampOptimisticOwner(e, n, t);
|
|
635
757
|
} else if (e[n] && t in e[n]) {
|
|
636
758
|
armOptimisticStoreWrite(e, e[$PROXY]);
|
|
637
759
|
delete e[n][t];
|
|
760
|
+
if (n === STORE_OPTIMISTIC_OVERRIDE) delete e[STORE_OPTIMISTIC_OWNERS]?.[t];
|
|
638
761
|
} else return true;
|
|
639
762
|
notifyStoreProperty(e, t, "delete", undefined, i, true);
|
|
640
763
|
});
|
|
@@ -643,7 +766,15 @@ const storeTraps = {
|
|
|
643
766
|
},
|
|
644
767
|
ownKeys(e) {
|
|
645
768
|
if (pendingCheckActive) witnessAffectsMark(e);
|
|
646
|
-
if (getObserver() !== e[STORE_FIREWALL])
|
|
769
|
+
if (getObserver() !== e[STORE_FIREWALL]) {
|
|
770
|
+
trackSelf(e);
|
|
771
|
+
// trackSelf no-ops untracked, so enumeration of an unresolved derived
|
|
772
|
+
// store would otherwise leak the seed's structure (#2897). The write
|
|
773
|
+
// path is exempt (like the get/has traps' writeOnly early returns):
|
|
774
|
+
// the first landing's reconcile enumerates the store while
|
|
775
|
+
// STATUS_UNINITIALIZED is still set — it IS the initialization.
|
|
776
|
+
if (!getObserver() && !writeOnly(e[$PROXY])) throwIfUninitialized(e);
|
|
777
|
+
}
|
|
647
778
|
// Merge optimistic override with regular override for key enumeration
|
|
648
779
|
let t = getKeys(e[STORE_VALUE], e[STORE_OVERRIDE], false);
|
|
649
780
|
if (e[STORE_OPTIMISTIC_OVERRIDE]) {
|
|
@@ -736,4 +867,4 @@ function createStore(e, t, r) {
|
|
|
736
867
|
} : e => storeSetter(o, e) ];
|
|
737
868
|
}
|
|
738
869
|
|
|
739
|
-
export { $AFFECTS, $DELETED, $PROXY, $TARGET, $TRACK, STORE_CUSTOM_PROTO, STORE_FIREWALL, STORE_HAS, STORE_LOOKUP, STORE_NODE, STORE_OPTIMISTIC, STORE_OPTIMISTIC_OVERRIDE, STORE_OVERRIDE, STORE_VALUE, STORE_WRAP, createStore, createStoreProxy, getKeys, getOverlayLayer, getPropertyDescriptor, getStoreAffectsNodes, isWrappable, mergedOverlay, notifySelf, ownEnumerableKeys, setWriteOverride, storeLookup, storeSetter, storeTraps, symbolKeyedRecords, trackSelf, visibleNodeValue, witnessAffectsMark, wrap };
|
|
870
|
+
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 };
|
package/dist/prod/store/utils.js
CHANGED
|
@@ -12,10 +12,10 @@ import "../core/effect.js";
|
|
|
12
12
|
|
|
13
13
|
import { createMemo } from "../signals.js";
|
|
14
14
|
|
|
15
|
-
import { ownEnumerableKeys, $PROXY, isWrappable, $TARGET, trackSelf, witnessAffectsMark, mergedOverlay, STORE_VALUE, storeLookup, STORE_LOOKUP, $DELETED, wrap,
|
|
15
|
+
import { ownEnumerableKeys, $PROXY, isWrappable, $TARGET, trackSelf, witnessAffectsMark, mergedOverlay, STORE_VALUE, storeLookup, STORE_LOOKUP, $DELETED, wrap, getStoreSymbols, getPropertyDescriptor, getStoreKeys, getKeys, $TRACK } from "./store.js";
|
|
16
16
|
|
|
17
17
|
function snapshotImpl(e, t, r, n) {
|
|
18
|
-
let o, s,
|
|
18
|
+
let o, s, i, c, f, u;
|
|
19
19
|
if (!isWrappable(e)) return e;
|
|
20
20
|
if (r && r.has(e)) return r.get(e);
|
|
21
21
|
if (!r) r = new Map;
|
|
@@ -26,9 +26,9 @@ function snapshotImpl(e, t, r, n) {
|
|
|
26
26
|
// traps — witness the record's affects() channel like a trap read would.
|
|
27
27
|
if (pendingCheckActive) witnessAffectsMark(o);
|
|
28
28
|
}
|
|
29
|
-
|
|
29
|
+
i = mergedOverlay(o);
|
|
30
30
|
s = Array.isArray(o[STORE_VALUE]);
|
|
31
|
-
r.set(e,
|
|
31
|
+
r.set(e, i ? c = s ? [] : Object.create(Object.getPrototypeOf(o[STORE_VALUE])) : o[STORE_VALUE]);
|
|
32
32
|
e = o[STORE_VALUE];
|
|
33
33
|
n = o[STORE_LOOKUP] ?? storeLookup;
|
|
34
34
|
} else {
|
|
@@ -36,56 +36,75 @@ function snapshotImpl(e, t, r, n) {
|
|
|
36
36
|
r.set(e, e);
|
|
37
37
|
}
|
|
38
38
|
if (s) {
|
|
39
|
-
const s =
|
|
39
|
+
const s = i?.length ?? e.length;
|
|
40
40
|
for (let p = 0; p < s; p++) {
|
|
41
|
-
u =
|
|
41
|
+
u = i && p in i ? i[p] : e[p];
|
|
42
42
|
if (u === $DELETED) continue;
|
|
43
43
|
if (t && isWrappable(u)) wrap(u, o);
|
|
44
|
-
if ((f = snapshotImpl(u, t, r, n)) !== u ||
|
|
45
|
-
if (!
|
|
46
|
-
|
|
44
|
+
if ((f = snapshotImpl(u, t, r, n)) !== u || c) {
|
|
45
|
+
if (!c) r.set(e, c = [ ...e ]);
|
|
46
|
+
c[p] = f;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Enumerate array symbols separately to avoid scanning indices twice.
|
|
50
|
+
// Spread copies omit symbols, so assign them after the numeric walk.
|
|
51
|
+
const p = n ? getStoreSymbols(e, i) : [];
|
|
52
|
+
for (let s = 0, l = p.length; s < l; s++) {
|
|
53
|
+
const l = p[s];
|
|
54
|
+
const a = getPropertyDescriptor(e, i, l);
|
|
55
|
+
if (!a || a.get) continue;
|
|
56
|
+
u = i && l in i ? i[l] : e[l];
|
|
57
|
+
if (t && isWrappable(u)) wrap(u, o);
|
|
58
|
+
f = snapshotImpl(u, t, r, n);
|
|
59
|
+
if (f !== u || c) {
|
|
60
|
+
if (!c) r.set(e, c = Object.assign([ ...e ], e));
|
|
61
|
+
c[l] = f;
|
|
47
62
|
}
|
|
48
63
|
}
|
|
49
64
|
// Deleted trailing slots are skipped above, so restore length to preserve
|
|
50
65
|
// holes instead of truncating the copy (#2846) — mirrors unwrapStoreValue.
|
|
51
|
-
if (
|
|
52
|
-
} else if (!
|
|
66
|
+
if (c) c.length = s;
|
|
67
|
+
} else if (!i) {
|
|
53
68
|
// Specialized walk for the common no-overlay case (from #2756): the own
|
|
54
69
|
// descriptor gives the value directly, so each property is read once with
|
|
55
70
|
// no overlay membership checks.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
71
|
+
// A lookup means this object belongs to an immutable store backing tree,
|
|
72
|
+
// even if that nested value has not needed its own proxy yet.
|
|
73
|
+
const s = n ? getStoreKeys(e, undefined) : getKeys(e, undefined);
|
|
74
|
+
for (let i = 0, p = s.length; i < p; i++) {
|
|
75
|
+
const p = s[i];
|
|
59
76
|
const l = Object.getOwnPropertyDescriptor(e, p);
|
|
60
77
|
if (l.get) continue;
|
|
61
78
|
u = l.value;
|
|
62
79
|
if (t && isWrappable(u)) wrap(u, o);
|
|
63
|
-
if ((f = snapshotImpl(u, t, r, n)) !== u ||
|
|
64
|
-
if (!
|
|
65
|
-
|
|
66
|
-
Object.assign(
|
|
80
|
+
if ((f = snapshotImpl(u, t, r, n)) !== u || c) {
|
|
81
|
+
if (!c) {
|
|
82
|
+
c = Object.create(Object.getPrototypeOf(e));
|
|
83
|
+
Object.assign(c, e);
|
|
67
84
|
}
|
|
68
|
-
|
|
85
|
+
c[p] = f;
|
|
69
86
|
}
|
|
70
87
|
}
|
|
71
88
|
} else {
|
|
72
|
-
|
|
89
|
+
// An override only exists on a store record, and the target branch above
|
|
90
|
+
// always set `lookup` alongside it — so this branch is always store-keyed.
|
|
91
|
+
const s = getStoreKeys(e, i);
|
|
73
92
|
for (let p = 0, l = s.length; p < l; p++) {
|
|
74
93
|
let l = s[p];
|
|
75
|
-
const a = getPropertyDescriptor(e,
|
|
94
|
+
const a = getPropertyDescriptor(e, i, l);
|
|
76
95
|
if (a.get) continue;
|
|
77
|
-
u = l in
|
|
96
|
+
u = l in i ? i[l] : e[l];
|
|
78
97
|
if (t && isWrappable(u)) wrap(u, o);
|
|
79
|
-
if ((f = snapshotImpl(u, t, r, n)) !== e[l] ||
|
|
80
|
-
if (!
|
|
81
|
-
|
|
82
|
-
Object.assign(
|
|
98
|
+
if ((f = snapshotImpl(u, t, r, n)) !== e[l] || c) {
|
|
99
|
+
if (!c) {
|
|
100
|
+
c = Object.create(Object.getPrototypeOf(e));
|
|
101
|
+
Object.assign(c, e);
|
|
83
102
|
}
|
|
84
|
-
|
|
103
|
+
c[l] = f;
|
|
85
104
|
}
|
|
86
105
|
}
|
|
87
106
|
}
|
|
88
|
-
return
|
|
107
|
+
return c || e;
|
|
89
108
|
}
|
|
90
109
|
|
|
91
110
|
function snapshot(e, t, r) {
|
|
@@ -218,14 +237,14 @@ const $SOURCES = Symbol(0);
|
|
|
218
237
|
e === s && s--;
|
|
219
238
|
continue;
|
|
220
239
|
}
|
|
221
|
-
const
|
|
222
|
-
for (let r =
|
|
223
|
-
const
|
|
224
|
-
if (
|
|
225
|
-
if (!n[
|
|
240
|
+
const i = Object.getOwnPropertyNames(t);
|
|
241
|
+
for (let r = i.length - 1; r >= 0; r--) {
|
|
242
|
+
const c = i[r];
|
|
243
|
+
if (c === "__proto__" || c === "constructor") continue;
|
|
244
|
+
if (!n[c]) {
|
|
226
245
|
o = o || e !== s;
|
|
227
|
-
const r = Object.getOwnPropertyDescriptor(t,
|
|
228
|
-
n[
|
|
246
|
+
const r = Object.getOwnPropertyDescriptor(t, c);
|
|
247
|
+
n[c] = r.get ? {
|
|
229
248
|
enumerable: true,
|
|
230
249
|
configurable: true,
|
|
231
250
|
get: r.get.bind(t)
|
|
@@ -234,14 +253,14 @@ const $SOURCES = Symbol(0);
|
|
|
234
253
|
}
|
|
235
254
|
}
|
|
236
255
|
if (!o) return r[s];
|
|
237
|
-
const
|
|
238
|
-
const
|
|
239
|
-
for (let e =
|
|
240
|
-
const t =
|
|
241
|
-
if (r.get) Object.defineProperty(
|
|
256
|
+
const i = {};
|
|
257
|
+
const c = Object.keys(n);
|
|
258
|
+
for (let e = c.length - 1; e >= 0; e--) {
|
|
259
|
+
const t = c[e], r = n[t];
|
|
260
|
+
if (r.get) Object.defineProperty(i, t, r); else i[t] = r.value;
|
|
242
261
|
}
|
|
243
|
-
|
|
244
|
-
return
|
|
262
|
+
i[$SOURCES] = r;
|
|
263
|
+
return i;
|
|
245
264
|
}
|
|
246
265
|
|
|
247
266
|
/**
|