@solidjs/signals 2.0.0-rc.16 → 2.0.0-rc.18
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 +1972 -1751
- package/dist/dev.attribution.js +16 -16
- package/dist/dev.js +672 -319
- package/dist/observe/boundaries.js +2 -2
- package/dist/observe/core/async.js +151 -116
- package/dist/observe/core/constants.js +3 -1
- package/dist/observe/core/context.js +2 -0
- package/dist/observe/core/core.js +51 -35
- package/dist/observe/core/effect.js +29 -30
- package/dist/observe/core/graph.js +13 -11
- package/dist/observe/core/lanes.js +25 -14
- package/dist/observe/core/owner.js +77 -51
- package/dist/observe/core/scheduler.js +202 -175
- package/dist/observe/core/verdict.js +5 -2
- package/dist/observe/deferred.js +16 -13
- package/dist/observe/from.js +248 -0
- package/dist/observe/index.js +2 -0
- package/dist/observe/map.js +2 -2
- package/dist/observe/signals.js +6 -1
- package/dist/observe/store/affects.js +13 -13
- package/dist/observe/store/optimistic.js +112 -71
- package/dist/observe/store/reconcile.js +137 -121
- package/dist/observe/store/store.js +612 -626
- package/dist/observe/store/target.js +1 -5
- package/dist/prod/boundaries.js +2 -2
- package/dist/prod/core/async.js +211 -176
- package/dist/prod/core/constants.js +3 -1
- package/dist/prod/core/context.js +2 -0
- package/dist/prod/core/core.js +63 -47
- package/dist/prod/core/effect.js +24 -25
- package/dist/prod/core/graph.js +12 -10
- package/dist/prod/core/heap.js +1 -1
- package/dist/prod/core/lanes.js +39 -28
- package/dist/prod/core/owner.js +70 -44
- package/dist/prod/core/scheduler.js +78 -51
- package/dist/prod/core/truth.js +4 -4
- package/dist/prod/core/verdict.js +11 -8
- package/dist/prod/deferred.js +17 -14
- package/dist/prod/from.js +248 -0
- package/dist/prod/index.js +2 -0
- package/dist/prod/map.js +2 -2
- package/dist/prod/signals.js +18 -13
- package/dist/prod/store/optimistic.js +112 -71
- package/dist/prod/store/reconcile.js +137 -121
- package/dist/prod/store/store.js +109 -121
- package/dist/prod/store/target.js +13 -17
- package/dist/types/core/async.d.ts +12 -2
- package/dist/types/core/constants.d.ts +5 -0
- package/dist/types/core/core.d.ts +29 -6
- package/dist/types/core/dev.d.ts +9 -1
- package/dist/types/core/error.d.ts +9 -1
- package/dist/types/core/owner.d.ts +4 -0
- package/dist/types/core/scheduler.d.ts +38 -7
- package/dist/types/from.d.ts +47 -0
- package/dist/types/index.d.ts +1 -0
- package/package.json +1 -1
package/dist/prod/store/store.js
CHANGED
|
@@ -10,9 +10,9 @@ import { getObserver } from "../core/owner.js";
|
|
|
10
10
|
|
|
11
11
|
import { insertIntoHeap } from "../core/heap.js";
|
|
12
12
|
|
|
13
|
-
import { txOf, flushTransaction, holdNode, joinFuture, insertSubs, schedule, passLane, globalQueue, queuePendingNode, GlobalQueue, installStoreHooks, joinPassTx, dirtyQueue } from "../core/scheduler.js";
|
|
13
|
+
import { txOf, flushTransaction, holdNode, joinFuture, insertSubs, schedule, passLane, globalQueue, queuePendingNode, resolveTx, GlobalQueue, installStoreHooks, joinPassTx, dirtyQueue } from "../core/scheduler.js";
|
|
14
14
|
|
|
15
|
-
import { lookupTarget, setLookup, $OWNER, isOwned, markDescendants
|
|
15
|
+
import { lookupTarget, setLookup, $OWNER, isOwned, markDescendants } from "./target.js";
|
|
16
16
|
|
|
17
17
|
import { optHooks, affectsHooks } from "./hooks.js";
|
|
18
18
|
|
|
@@ -134,7 +134,7 @@ function wrap(e, n = null, t = null, r = n?.fam ?? null) {
|
|
|
134
134
|
const i = lookupTarget(e, r);
|
|
135
135
|
if (i !== undefined) return i.px;
|
|
136
136
|
const o = e[$TARGET];
|
|
137
|
-
if (o
|
|
137
|
+
if (o?.px === e) {
|
|
138
138
|
// Foreign-family proxies re-wrap into THIS family (writes stay isolated);
|
|
139
139
|
// same-family and plain-store proxies pass through.
|
|
140
140
|
if (r === null || o.fam === r) return e;
|
|
@@ -206,7 +206,7 @@ function sameKey(e, n) {
|
|
|
206
206
|
/** The container node's comparator: a staging is a backing (identity — a
|
|
207
207
|
* pending backing is never "equal" to the committed one); a lane value is
|
|
208
208
|
* an arrangement (`LaneView`), judged by the optimistic machinery. */ const containerEquals = function(e, n) {
|
|
209
|
-
return e === n || optHooks
|
|
209
|
+
return e === n || !!optHooks?.arrangement(this.O, e, n);
|
|
210
210
|
};
|
|
211
211
|
|
|
212
212
|
function sameLogicalSlot(e, n, t) {
|
|
@@ -225,10 +225,10 @@ function releaseSlot(e) {
|
|
|
225
225
|
if (n.k === e) n.k = null;
|
|
226
226
|
} else if (t === $DEEP) {
|
|
227
227
|
if (n.dk === e) n.dk = null;
|
|
228
|
-
} else if (n.n
|
|
228
|
+
} else if (n.n?.[t] === e) {
|
|
229
229
|
delete n.n[t];
|
|
230
230
|
n.nc--;
|
|
231
|
-
} else if (n.h
|
|
231
|
+
} else if (n.h?.[t] === e) delete n.h[t];
|
|
232
232
|
// The family's index keeps only targets that still carry a node.
|
|
233
233
|
if (n.fam !== null && n.nc === 0 && n.k === null && n.dk === null && (n.h === null || Reflect.ownKeys(n.h).length === 0)) n.fam.live.delete(n);
|
|
234
234
|
}
|
|
@@ -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);
|
|
@@ -260,14 +260,19 @@ setSlotUnobserved(e => {
|
|
|
260
260
|
* unchanged stages nothing and holds no one). */ function bornStaged(e, n, t, r) {
|
|
261
261
|
if (n.xe && n.xe(n.Ie, t)) return;
|
|
262
262
|
queuePendingNode(n);
|
|
263
|
-
n.
|
|
263
|
+
n.ue = t;
|
|
264
264
|
const i = e.k;
|
|
265
265
|
if (i === null) return;
|
|
266
266
|
// a node-less staging holds nothing (`stageOn`)
|
|
267
267
|
// Held by the container's transaction only for a key the HELD staging
|
|
268
268
|
// changed — a mainline layer's change above it stages mainline. (A
|
|
269
|
-
// container carrying a guess is a lane's: the flush's park decides
|
|
270
|
-
|
|
269
|
+
// container carrying a guess is a lane's: the flush's park decides —
|
|
270
|
+
// unless a correction folded under the guess, D-34: the key is that
|
|
271
|
+
// correction's hold's.)
|
|
272
|
+
if (i.l & CONFIG_HELD && heldKeyChanged(e, r)) {
|
|
273
|
+
const e = txOf(i);
|
|
274
|
+
if (!(i.l & CONFIG_OVERRIDE) || e.folded) holdNode(n, resolveTx(e._parent ?? e));
|
|
275
|
+
}
|
|
271
276
|
// A28 (4): a batch written inside a creation-time pass is promoted — its
|
|
272
277
|
// late-materialized leaf with it.
|
|
273
278
|
if (i.l & CONFIG_PROMOTED) notePromotedWrite(n);
|
|
@@ -287,7 +292,7 @@ t = -1) {
|
|
|
287
292
|
// receiver.
|
|
288
293
|
const o = e.pb;
|
|
289
294
|
const l = i = slotSignal(committed(e)[n], slotNodeEquals, e, n, t === -1 ? isOwnAccessor(o ?? e.v, n) : t === 1);
|
|
290
|
-
if (o !== null && !l.acc) bornStaged(e, l, e.del
|
|
295
|
+
if (o !== null && !l.acc) bornStaged(e, l, e.del?.has(n) ? undefined : o[n], n);
|
|
291
296
|
r[n] = i;
|
|
292
297
|
e.nc++;
|
|
293
298
|
noteNode(e, l, n);
|
|
@@ -301,7 +306,7 @@ function getHasNode(e, n) {
|
|
|
301
306
|
if (r === undefined) {
|
|
302
307
|
const i = r = slotSignal(n in committed(e), isEqual, e, n, false);
|
|
303
308
|
const o = e.pb;
|
|
304
|
-
if (o !== null) bornStaged(e, i, n in o && !
|
|
309
|
+
if (o !== null) bornStaged(e, i, n in o && !e.del?.has(n), n);
|
|
305
310
|
t[n] = r;
|
|
306
311
|
noteNode(e, i, n);
|
|
307
312
|
}
|
|
@@ -321,7 +326,7 @@ function getHasNode(e, n) {
|
|
|
321
326
|
// would have read had the node been there from the write.
|
|
322
327
|
if (e.pb !== null && !directCommit) {
|
|
323
328
|
n.Ie = preBatch(e);
|
|
324
|
-
n.
|
|
329
|
+
n.ue = e.pb;
|
|
325
330
|
queuePendingNode(n);
|
|
326
331
|
}
|
|
327
332
|
}
|
|
@@ -340,8 +345,8 @@ function getHasNode(e, n) {
|
|
|
340
345
|
* (`storeParkHook`) — the one case the staging home exists for. */ function stageOn(e, n) {
|
|
341
346
|
if (e.k === null && passLane === null && (context === null || globalQueue.q) && !deriveHolds(e)) return;
|
|
342
347
|
const t = getContainerNode(e);
|
|
343
|
-
if (t.
|
|
344
|
-
t.
|
|
348
|
+
if (t.ue === NOT_PENDING) queuePendingNode(t);
|
|
349
|
+
t.ue = n;
|
|
345
350
|
// A28 (4): a write issued inside a pass outside a flush (a creation-time
|
|
346
351
|
// derive) is promoted at the pass's end — the batch with it.
|
|
347
352
|
if (context !== null) notePromotedWrite(t);
|
|
@@ -444,7 +449,7 @@ function copyOwn(e, n, t) {
|
|
|
444
449
|
const t = Reflect.ownKeys(n);
|
|
445
450
|
let r = Object.getPrototypeOf(n) === Object.prototype;
|
|
446
451
|
for (const i of t) {
|
|
447
|
-
if (
|
|
452
|
+
if (isOwnAccessor(n, i)) {
|
|
448
453
|
e.a = true;
|
|
449
454
|
r = false;
|
|
450
455
|
break;
|
|
@@ -474,7 +479,7 @@ const plainProto = e => {
|
|
|
474
479
|
/** An overlay with many adds (or any delete) rebuilds the committed
|
|
475
480
|
* container at flatten so its shape stays fast (#3689). */ function overlayRebuilds(e, n) {
|
|
476
481
|
if (e.kc > OVERLAY_REBUILD_MAX_KEYS) return false;
|
|
477
|
-
if (e.del
|
|
482
|
+
if (e.del?.size) return true;
|
|
478
483
|
const t = e.wk;
|
|
479
484
|
if (t !== null && t !== WK_ALL && t.u === undefined && t.size <= OVERLAY_REBUILD_MIN_ADDS) return false;
|
|
480
485
|
const r = e.v;
|
|
@@ -486,17 +491,10 @@ const plainProto = e => {
|
|
|
486
491
|
/** Downgrade an overlay to a real clone (a consumer needs a container). */ function materializePB(e) {
|
|
487
492
|
const n = e.pb;
|
|
488
493
|
if (!e.ovl) return n;
|
|
489
|
-
const t = e.sc === 2 ? wideClone(e.v, e) : cloneRaw(e.v, e);
|
|
490
|
-
if (e.del !== null) {
|
|
491
|
-
for (const n of e.del) delete t[n];
|
|
492
|
-
e.kc -= e.del.size;
|
|
493
|
-
e.del = null;
|
|
494
|
-
}
|
|
495
|
-
for (const r of Reflect.ownKeys(n)) e.sc === 2 ? t[r] = n[r] : copyOwn(t, n, r);
|
|
494
|
+
const t = overlayOnto(e, e.sc === 2 ? wideClone(e.v, e) : cloneRaw(e.v, e), n);
|
|
496
495
|
e.pb = t;
|
|
497
|
-
e.ovl = false;
|
|
498
496
|
// The staging IS the pending backing: re-point the container node.
|
|
499
|
-
if (e.k
|
|
497
|
+
if (e.k?.ue === n) e.k.ue = t;
|
|
500
498
|
return t;
|
|
501
499
|
}
|
|
502
500
|
|
|
@@ -517,7 +515,7 @@ const plainProto = e => {
|
|
|
517
515
|
// mainline setter mid-hold publishes mainline; the held keys stay held).
|
|
518
516
|
n = e.pb = cloneRaw(n, e);
|
|
519
517
|
const t = e.k;
|
|
520
|
-
if (t !== null && !(t.l & CONFIG_HELD)) t.
|
|
518
|
+
if (t !== null && !(t.l & CONFIG_HELD)) t.ue = n;
|
|
521
519
|
} else if (n === null) {
|
|
522
520
|
const t = e.v;
|
|
523
521
|
if (!directCommit) queueFold(e);
|
|
@@ -541,7 +539,6 @@ const plainProto = e => {
|
|
|
541
539
|
e.ch = false;
|
|
542
540
|
if (e.u) {
|
|
543
541
|
privatizeCommitted(e.u);
|
|
544
|
-
devAssertNeverUserMutation(e.u.v);
|
|
545
542
|
const t = e.u.v;
|
|
546
543
|
const r = parentSlotKey(e, n);
|
|
547
544
|
if (t[r] === n) t[r] = e.v;
|
|
@@ -565,17 +562,20 @@ const plainProto = e => {
|
|
|
565
562
|
/** Overlay flatten (#3044): own keys onto the owned committed backing in
|
|
566
563
|
* place — the backing keeps its identity. */ function flattenOverlay(e, n) {
|
|
567
564
|
privatizeCommitted(e);
|
|
568
|
-
|
|
569
|
-
|
|
565
|
+
overlayOnto(e, e.v, n);
|
|
566
|
+
// the caller (`foldTarget`) closes the staging
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/** An overlay's own keys and deletions onto `to` (the two are disjoint: a
|
|
570
|
+
* write un-deletes its key); the overlay is closed. */ function overlayOnto(e, n, t) {
|
|
571
|
+
for (const r of Reflect.ownKeys(t)) e.sc === 2 ? n[r] = t[r] : copyOwn(n, t, r);
|
|
570
572
|
if (e.del !== null) {
|
|
571
|
-
for (const
|
|
573
|
+
for (const t of e.del) delete n[t];
|
|
572
574
|
e.kc -= e.del.size;
|
|
573
575
|
e.del = null;
|
|
574
576
|
}
|
|
575
|
-
e.pb = null;
|
|
576
577
|
e.ovl = false;
|
|
577
|
-
|
|
578
|
-
// written-keys window closes with the commit
|
|
578
|
+
return n;
|
|
579
579
|
}
|
|
580
580
|
|
|
581
581
|
/** Adoption (a setter's returned replacement; reconcile and projection
|
|
@@ -615,7 +615,7 @@ const plainProto = e => {
|
|
|
615
615
|
e.wk = null;
|
|
616
616
|
// adoption supersedes staged trap writes
|
|
617
617
|
const i = n[$OWNER];
|
|
618
|
-
if (i
|
|
618
|
+
if (i?.fam === e.fam) n[$OWNER] = e; else setLookup(n, e);
|
|
619
619
|
if (t && r !== n) notifyFold(e, r, n);
|
|
620
620
|
}
|
|
621
621
|
|
|
@@ -692,7 +692,7 @@ let foldIndex = null;
|
|
|
692
692
|
if (r.pb === null) continue;
|
|
693
693
|
// folded already (a direct commit)
|
|
694
694
|
const i = r.k;
|
|
695
|
-
if (i !== null && i.
|
|
695
|
+
if (i !== null && i.ue !== NOT_PENDING) {
|
|
696
696
|
// held: commits with its hold
|
|
697
697
|
foldList.push(r);
|
|
698
698
|
foldOlds.push(n[t]);
|
|
@@ -716,7 +716,7 @@ let foldIndex = null;
|
|
|
716
716
|
* marks (an ambient `affects()` over a declared reload lives to the
|
|
717
717
|
* landing) read it. */ GlobalQueue.k = e => {
|
|
718
718
|
const n = e.O?.fam;
|
|
719
|
-
return
|
|
719
|
+
return (n?.node?.m & STATUS_PENDING) !== 0;
|
|
720
720
|
};
|
|
721
721
|
|
|
722
722
|
/** Core asks (lanes.ts `covered`): the value a slot node's guess covered.
|
|
@@ -727,7 +727,7 @@ let foldIndex = null;
|
|
|
727
727
|
if (n.ch) {
|
|
728
728
|
const t = e.ze;
|
|
729
729
|
const r = n.v;
|
|
730
|
-
if (n.h
|
|
730
|
+
if (n.h?.[t] === e) e.Ie = untrack(() => t in r); else if (n.n?.[t] === e) e.Ie = untrack(() => unwrapValue(r[t]));
|
|
731
731
|
}
|
|
732
732
|
return e.Ie;
|
|
733
733
|
};
|
|
@@ -763,12 +763,7 @@ function runDirect(e) {
|
|
|
763
763
|
const t = e.k;
|
|
764
764
|
const r = e.pb;
|
|
765
765
|
if (r !== null) {
|
|
766
|
-
if (r === e.v) {
|
|
767
|
-
// An eager adoption nothing wrote after: the backing is already
|
|
768
|
-
// the adopted object (shared ownership — never cloned).
|
|
769
|
-
e.pb = null;
|
|
770
|
-
e.wk = null;
|
|
771
|
-
} else if (e.ovl && (e.v !== n || !overlayRebuilds(e, r) && t?.T?.Pe !== n)) {
|
|
766
|
+
if (r === e.v) ; else if (e.ovl && (e.v !== n || !overlayRebuilds(e, r) && t?.T?.Pe !== n)) {
|
|
772
767
|
// Overlay flatten (#3044): the backing keeps its identity, so the
|
|
773
768
|
// `t.v === old` gate below skips path copying (the parent slot
|
|
774
769
|
// already points here) and the adopted-notify (setter
|
|
@@ -786,25 +781,21 @@ function runDirect(e) {
|
|
|
786
781
|
if (hasOwn.call(r, n)) copyOwn(t, r, n); else delete t[n];
|
|
787
782
|
}
|
|
788
783
|
} else {
|
|
789
|
-
for (const e of Reflect.ownKeys(r))
|
|
790
|
-
const i = Object.getOwnPropertyDescriptor(r, e);
|
|
791
|
-
if (i.get || i.set || !i.enumerable || !i.writable || !i.configurable) Object.defineProperty(t, e, i); else if (i.value !== n[e] || !hasOwn.call(n, e)) t[e] = i.value;
|
|
792
|
-
}
|
|
784
|
+
for (const e of Reflect.ownKeys(r)) copyOwn(t, r, e);
|
|
793
785
|
for (const e of Reflect.ownKeys(n)) {
|
|
794
786
|
if (!hasOwn.call(r, e)) delete t[e];
|
|
795
787
|
}
|
|
796
788
|
}
|
|
797
|
-
e.pb = null;
|
|
798
|
-
e.wk = null;
|
|
799
789
|
} else {
|
|
800
790
|
// The clone (or adopted object) becomes the committed backing
|
|
801
791
|
// (path copy below).
|
|
802
792
|
e.v = e.ovl ? materializePB(e) : r;
|
|
803
793
|
e.ch = e.v[$TARGET] !== undefined;
|
|
804
|
-
e.pb = null;
|
|
805
|
-
e.wk = null;
|
|
806
794
|
}
|
|
807
|
-
|
|
795
|
+
e.pb = null;
|
|
796
|
+
e.wk = null;
|
|
797
|
+
// the written-keys window closes with the commit
|
|
798
|
+
}
|
|
808
799
|
if (t !== null) {
|
|
809
800
|
// The node's committed value is the backing. A container nobody
|
|
810
801
|
// subscribes to was a transient staging home (INTERNALS §5: an
|
|
@@ -816,7 +807,6 @@ function runDirect(e) {
|
|
|
816
807
|
const t = parentSlotKey(e, n);
|
|
817
808
|
if (e.u.v[t] === n) {
|
|
818
809
|
privatizeCommitted(e.u);
|
|
819
|
-
devAssertNeverUserMutation(e.u.v);
|
|
820
810
|
e.u.v[t] = e.v;
|
|
821
811
|
}
|
|
822
812
|
}
|
|
@@ -901,7 +891,7 @@ function heldDerivation(e, n, t) {
|
|
|
901
891
|
if (e?.get || e?.set || !isEqual(e?.value, r.value)) setSignal(i, () => r.value);
|
|
902
892
|
continue;
|
|
903
893
|
}
|
|
904
|
-
const l = e.del
|
|
894
|
+
const l = e.del?.has(o) ? undefined : n[o];
|
|
905
895
|
setSignal(i, () => l);
|
|
906
896
|
if (e.fam !== null) {
|
|
907
897
|
// The derive's write is a derivation; a user setter's is a proposal
|
|
@@ -919,13 +909,13 @@ function heldDerivation(e, n, t) {
|
|
|
919
909
|
for (const r of t) {
|
|
920
910
|
const t = l[r];
|
|
921
911
|
if (t !== undefined) {
|
|
922
|
-
setSignal(t, r in n && !
|
|
912
|
+
setSignal(t, r in n && !e.del?.has(r));
|
|
923
913
|
if (e.fam !== null) holdWithDerive(e, t);
|
|
924
914
|
}
|
|
925
915
|
}
|
|
926
916
|
}
|
|
927
917
|
if (e.dk !== null) {
|
|
928
|
-
if (e.del
|
|
918
|
+
if (e.del?.size) bumpDeep(e); else for (const r of o ?? Reflect.ownKeys(n)) {
|
|
929
919
|
if (r === $OWNER) continue;
|
|
930
920
|
const i = n[r];
|
|
931
921
|
const o = t[r];
|
|
@@ -943,7 +933,7 @@ function heldDerivation(e, n, t) {
|
|
|
943
933
|
if (u !== null && (u.o !== null || u.l & CONFIG_OVERRIDE)) {
|
|
944
934
|
let r;
|
|
945
935
|
if (e.ovl) {
|
|
946
|
-
r = e.del
|
|
936
|
+
r = !!e.del?.size;
|
|
947
937
|
if (!r) {
|
|
948
938
|
for (const e of Reflect.ownKeys(n)) {
|
|
949
939
|
if (!hasOwn.call(t, e)) {
|
|
@@ -1089,7 +1079,7 @@ function inDraft(e) {
|
|
|
1089
1079
|
/** A child proxy served to a draft joins the draft's scope. */ function draftServe(e, n) {
|
|
1090
1080
|
if (writeScopes !== null && inDraft(e)) {
|
|
1091
1081
|
const e = n?.[$TARGET];
|
|
1092
|
-
if (e
|
|
1082
|
+
if (e?.v !== undefined) writeScopes.add(scopeKey(e));
|
|
1093
1083
|
}
|
|
1094
1084
|
return n;
|
|
1095
1085
|
}
|
|
@@ -1099,7 +1089,7 @@ function inDraft(e) {
|
|
|
1099
1089
|
* "adoption is eager by contract" — and the node keeps the frame committed
|
|
1100
1090
|
* readers under a hold still see), `t.v` otherwise. */ function committed(e) {
|
|
1101
1091
|
const n = e.k;
|
|
1102
|
-
if (n !== null) return n.
|
|
1092
|
+
if (n !== null) return n.ue !== NOT_PENDING ? asBacking(n.Ie) : e.v;
|
|
1103
1093
|
// A node-less staging (`stageOn`): the fold queue remembers the frame.
|
|
1104
1094
|
return e.pb !== null ? preBatch(e) : e.v;
|
|
1105
1095
|
}
|
|
@@ -1111,7 +1101,7 @@ function inDraft(e) {
|
|
|
1111
1101
|
* committed frame. */ function stagingReader() {
|
|
1112
1102
|
let e = context;
|
|
1113
1103
|
if (e === null) return null;
|
|
1114
|
-
if (e.
|
|
1104
|
+
if (e.Qe) e = e.Ye ?? null;
|
|
1115
1105
|
return e === null || e.l & CONFIG_CHILDREN_FORBIDDEN ? null : e;
|
|
1116
1106
|
}
|
|
1117
1107
|
|
|
@@ -1132,7 +1122,7 @@ function inDraft(e) {
|
|
|
1132
1122
|
// sees — the lanes' values and the tick's own guesses over the truth
|
|
1133
1123
|
// (#3665), never a staging awaiting its fold: the draft is born on the
|
|
1134
1124
|
// read (and notified at the setter's exit like any).
|
|
1135
|
-
if (userWrite && e.fam
|
|
1125
|
+
if (userWrite && e.fam?.opt === true && !pendingNotify.has(e) && inDraft(e)) {
|
|
1136
1126
|
const n = ensurePB(e);
|
|
1137
1127
|
pendingNotify.add(e);
|
|
1138
1128
|
return n;
|
|
@@ -1165,7 +1155,7 @@ function inDraft(e) {
|
|
|
1165
1155
|
// it).
|
|
1166
1156
|
if (n !== undefined) {
|
|
1167
1157
|
const t = e.n?.[n];
|
|
1168
|
-
if (t
|
|
1158
|
+
if (t?.l & CONFIG_OVERRIDE) return committed(e);
|
|
1169
1159
|
}
|
|
1170
1160
|
const o = i !== null && (i.l & CONFIG_HELD) !== 0;
|
|
1171
1161
|
// A held container: the hold's unit is the key (#3706). A key the HELD
|
|
@@ -1218,7 +1208,7 @@ function inDraft(e) {
|
|
|
1218
1208
|
|
|
1219
1209
|
/** Did the HELD staging (the container node's, not a mainline layer above
|
|
1220
1210
|
* it) change `key`? */ function heldKeyChanged(e, n, t = true) {
|
|
1221
|
-
return changedBetween(e, e.k.Ie, e.k.
|
|
1211
|
+
return changedBetween(e, e.k.Ie, e.k.ue, n, t) && !adoptChild(e, n);
|
|
1222
1212
|
}
|
|
1223
1213
|
|
|
1224
1214
|
/** The values of a held frame, built once per frame: neither the committed
|
|
@@ -1247,23 +1237,23 @@ function rawsOf(e) {
|
|
|
1247
1237
|
const t = e.k;
|
|
1248
1238
|
const r = asBacking(t.Ie);
|
|
1249
1239
|
const i = r[n];
|
|
1250
|
-
const o = t.
|
|
1240
|
+
const o = t.ue[n];
|
|
1251
1241
|
const l = Array.isArray(r) ? e.fam?.key : null;
|
|
1252
1242
|
if (e.s || l === undefined || !sameRow(i, o, l) || !isWrappable(i) || !isWrappable(o) || Array.isArray(i) !== Array.isArray(o) || lookupTarget(i, e.fam) !== undefined || lookupTarget(o, e.fam) !== undefined ||
|
|
1253
1243
|
// A raw in both frames (the same objects swapped or reordered) is its
|
|
1254
1244
|
// own row: pairing it with another would alias the two after the commit.
|
|
1255
|
-
rawsOf(r).has(o) || rawsOf(t.
|
|
1245
|
+
rawsOf(r).has(o) || rawsOf(t.ue).has(i)) return false;
|
|
1256
1246
|
// A raw-marked value wraps to itself, a proxy to its own target: neither
|
|
1257
1247
|
// is a fresh target around `ov`.
|
|
1258
1248
|
const u = wrap(i, e, n)[$TARGET];
|
|
1259
|
-
if (u
|
|
1249
|
+
if (u?.v !== i) return false;
|
|
1260
1250
|
adoptPB(u, o, false);
|
|
1261
1251
|
holdNode(getContainerNode(u), txOf(t));
|
|
1262
1252
|
return true;
|
|
1263
1253
|
}
|
|
1264
1254
|
|
|
1265
1255
|
/** A family target's unheld staging, read with no pass (a handler). */ function familyAhead(e) {
|
|
1266
|
-
return e.fam !== null && context === null && !(e.k
|
|
1256
|
+
return e.fam !== null && context === null && !(e.k?.l & CONFIG_HELD);
|
|
1267
1257
|
}
|
|
1268
1258
|
|
|
1269
1259
|
/** Did this batch change `key` — value (slot equality), presence, and (for
|
|
@@ -1283,7 +1273,7 @@ function rawsOf(e) {
|
|
|
1283
1273
|
|
|
1284
1274
|
function changedBetween(e, n, t, r, i) {
|
|
1285
1275
|
if (e.ch || t[$TARGET] !== undefined || Object.getPrototypeOf(t) !== Object.getPrototypeOf(n) || !plainProto(n)) return true;
|
|
1286
|
-
if (e.del
|
|
1276
|
+
if (e.del?.has(r)) return r in n;
|
|
1287
1277
|
const o = hasOwn.call(t, r) || e.ovl && hasOwn.call(e.v, r);
|
|
1288
1278
|
if (o !== hasOwn.call(n, r)) return true;
|
|
1289
1279
|
if (!o) return false;
|
|
@@ -1297,7 +1287,7 @@ function changedBetween(e, n, t, r, i) {
|
|
|
1297
1287
|
* tracked or not, applies the hold and A28 rules a signal's read does. A
|
|
1298
1288
|
* projection's unheld staging is ahead for a handler (`familyAhead`). */ function nodeValue(e, n, t) {
|
|
1299
1289
|
let r = read(e);
|
|
1300
|
-
if (e.
|
|
1290
|
+
if (e.ue !== NOT_PENDING && !(e.l & CONFIG_HELD) && context === null && t.fam !== null) r = e.ue;
|
|
1301
1291
|
return r === FORCE ? n : r;
|
|
1302
1292
|
}
|
|
1303
1293
|
|
|
@@ -1390,7 +1380,7 @@ function changedBetween(e, n, t, r, i) {
|
|
|
1390
1380
|
* classification, as core reads it — `heldNotFinal`). */ function holdWithDerive(e, n) {
|
|
1391
1381
|
const t = e.fam?.node;
|
|
1392
1382
|
if (t !== undefined && t !== null && !userWrite && // the derive's own write (its draft or landing), not a user setter's
|
|
1393
|
-
t.l & CONFIG_HELD && n.
|
|
1383
|
+
t.l & CONFIG_HELD && n.ue !== NOT_PENDING &&
|
|
1394
1384
|
// A node carrying a guess is the lane's: its staging is the truth beneath
|
|
1395
1385
|
// the guess (`setSignal` → `laneWrite`), never re-homed by the store.
|
|
1396
1386
|
!(n.l & (CONFIG_HELD | CONFIG_OVERRIDE))) {
|
|
@@ -1431,7 +1421,7 @@ function changedBetween(e, n, t, r, i) {
|
|
|
1431
1421
|
/** A read on an optimistic family that composes the lanes' values: not the
|
|
1432
1422
|
* draft's (it composed already), not a derive's write-override read (the
|
|
1433
1423
|
* derive composes on the truth). */ function optRead(e) {
|
|
1434
|
-
return e.fam
|
|
1424
|
+
return e.fam?.opt === true && !inDraft(e) && !writeOverride;
|
|
1435
1425
|
}
|
|
1436
1426
|
|
|
1437
1427
|
/** Pollution keys are never served from the prototype (core R30). */ const UNSAFE_KEYS = new Set([ "__proto__", "prototype", "constructor" ]);
|
|
@@ -1514,7 +1504,7 @@ const traps = {
|
|
|
1514
1504
|
const i = readSource(e, n);
|
|
1515
1505
|
// Overlay delete (#3044): a prototype overlay cannot shadow a delete, so
|
|
1516
1506
|
// deleted keys are tracked aside and read as absent in the pending view.
|
|
1517
|
-
if (
|
|
1507
|
+
if (i === e.pb && e.del?.has(n)) {
|
|
1518
1508
|
if (!inDraft(e) && getObserver() !== null) read(getNode(e, n));
|
|
1519
1509
|
return undefined;
|
|
1520
1510
|
}
|
|
@@ -1601,7 +1591,7 @@ const traps = {
|
|
|
1601
1591
|
if (t !== undefined) return t;
|
|
1602
1592
|
}
|
|
1603
1593
|
}
|
|
1604
|
-
return n in t && !(
|
|
1594
|
+
return n in t && !(t === e.pb && e.del?.has(n));
|
|
1605
1595
|
},
|
|
1606
1596
|
ownKeys(e) {
|
|
1607
1597
|
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.ro(e, undefined);
|
|
@@ -1644,12 +1634,13 @@ const traps = {
|
|
|
1644
1634
|
},
|
|
1645
1635
|
set(e, n, t) {
|
|
1646
1636
|
const r = inDraft(e);
|
|
1647
|
-
|
|
1648
|
-
|
|
1637
|
+
if (!r && !writeOverride) {
|
|
1638
|
+
return true;
|
|
1639
|
+
}
|
|
1649
1640
|
if (n === "__proto__") return true;
|
|
1650
1641
|
// pollution guard (core R30)
|
|
1651
|
-
const
|
|
1652
|
-
const
|
|
1642
|
+
const i = e.s ? t : unwrapValue(t);
|
|
1643
|
+
const o = ensurePB(e);
|
|
1653
1644
|
// #4146 (R1: node existence is unobservable; #3706: the hold's unit is
|
|
1654
1645
|
// the key): a user setter writing a key a HELD container's staging left
|
|
1655
1646
|
// unchanged would write it into the held staging — read as the hold's
|
|
@@ -1661,59 +1652,55 @@ const traps = {
|
|
|
1661
1652
|
// A34) — as it would with a reader's leaf. Kept while the container is
|
|
1662
1653
|
// held (`deferredReleases`).
|
|
1663
1654
|
if (userWrite && e.k?.l & CONFIG_HELD) deferredReleases.add(getNode(e, n));
|
|
1664
|
-
if (Array.isArray(
|
|
1655
|
+
if (Array.isArray(o)) {
|
|
1665
1656
|
touch(e, n);
|
|
1666
1657
|
if (n === "length") e.wk = WK_ALL; else touch(e, "length");
|
|
1667
1658
|
} else {
|
|
1668
1659
|
touch(e, n);
|
|
1669
|
-
if (!(n in
|
|
1670
|
-
}
|
|
1671
|
-
if (UNSAFE_KEYS.has(n)) {
|
|
1672
|
-
Object.defineProperty(l, n, {
|
|
1673
|
-
value: o,
|
|
1674
|
-
writable: true,
|
|
1675
|
-
enumerable: true,
|
|
1676
|
-
configurable: true
|
|
1677
|
-
});
|
|
1678
|
-
if (e.del !== null) e.del.delete(n);
|
|
1679
|
-
return true;
|
|
1660
|
+
if (!(n in o)) e.kc++;
|
|
1680
1661
|
}
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1662
|
+
const l = UNSAFE_KEYS.has(n);
|
|
1663
|
+
if (l || e.ovl && e.sc !== 2 && !hasOwn.call(o, n)) {
|
|
1664
|
+
Object.defineProperty(o, n, {
|
|
1665
|
+
value: i,
|
|
1684
1666
|
writable: true,
|
|
1685
1667
|
enumerable: true,
|
|
1686
1668
|
configurable: true
|
|
1687
1669
|
});
|
|
1688
|
-
} else
|
|
1689
|
-
|
|
1690
|
-
if (
|
|
1691
|
-
if (i)
|
|
1692
|
-
|
|
1670
|
+
} else o[n] = i;
|
|
1671
|
+
e.del?.delete(n);
|
|
1672
|
+
if (l) return true;
|
|
1673
|
+
if (e.s && i !== null && typeof i === "object") markRawOne(i);
|
|
1674
|
+
if (!r) notifyWrites(e);
|
|
1675
|
+
// a write-override write
|
|
1676
|
+
return true;
|
|
1693
1677
|
},
|
|
1694
1678
|
defineProperty(e, n, t) {
|
|
1695
1679
|
const r = inDraft(e);
|
|
1696
|
-
|
|
1697
|
-
|
|
1680
|
+
if (!r && !writeOverride) {
|
|
1681
|
+
return true;
|
|
1682
|
+
}
|
|
1698
1683
|
if (n === "__proto__") return true;
|
|
1699
1684
|
if (t.get || t.set) e.a = true;
|
|
1700
1685
|
if ("value" in t) t = {
|
|
1701
1686
|
...t,
|
|
1702
1687
|
value: unwrapValue(t.value)
|
|
1703
1688
|
};
|
|
1704
|
-
const
|
|
1689
|
+
const i = ensurePB(e);
|
|
1705
1690
|
if (e.a || !(t.enumerable && t.writable && t.configurable)) e.sc = 1;
|
|
1706
1691
|
touch(e, n);
|
|
1707
|
-
Object.defineProperty(
|
|
1708
|
-
|
|
1709
|
-
if (
|
|
1710
|
-
|
|
1692
|
+
Object.defineProperty(i, n, t);
|
|
1693
|
+
e.del?.delete(n);
|
|
1694
|
+
if (!r) notifyWrites(e);
|
|
1695
|
+
// a write-override write
|
|
1696
|
+
return true;
|
|
1711
1697
|
},
|
|
1712
1698
|
deleteProperty(e, n) {
|
|
1713
1699
|
const t = inDraft(e);
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1700
|
+
if (!t && !writeOverride) {
|
|
1701
|
+
return true;
|
|
1702
|
+
}
|
|
1703
|
+
const r = ensurePB(e);
|
|
1717
1704
|
touch(e, n);
|
|
1718
1705
|
// The key count (`kc`, the overlay gate and the rebuild cap) follows a
|
|
1719
1706
|
// delete the way it follows an add: an own key of the draft leaving — a
|
|
@@ -1724,14 +1711,15 @@ const traps = {
|
|
|
1724
1711
|
// one" shape drifted the count up by one per write until a three-key
|
|
1725
1712
|
// object crossed the overlay threshold and paid a rebuild on every
|
|
1726
1713
|
// commit after.
|
|
1727
|
-
const
|
|
1728
|
-
if (hasOwn.call(
|
|
1729
|
-
delete
|
|
1730
|
-
|
|
1714
|
+
const i = e.ovl && hasOwn.call(e.v, n);
|
|
1715
|
+
if (hasOwn.call(r, n)) {
|
|
1716
|
+
delete r[n];
|
|
1717
|
+
i || e.kc--;
|
|
1731
1718
|
}
|
|
1732
|
-
if (
|
|
1733
|
-
if (
|
|
1734
|
-
|
|
1719
|
+
if (i) (e.del ??= new Set).add(n);
|
|
1720
|
+
if (!t) notifyWrites(e);
|
|
1721
|
+
// a write-override write
|
|
1722
|
+
return true;
|
|
1735
1723
|
}
|
|
1736
1724
|
};
|
|
1737
1725
|
|
|
@@ -1813,17 +1801,17 @@ function createStore(e, n) {
|
|
|
1813
1801
|
* replaced by reference — #2932). The list driver uses this to choose the
|
|
1814
1802
|
* slot-patch channel (collected row bodies) over per-record registration. */ function storeIsShallow(e) {
|
|
1815
1803
|
const n = e?.[$TARGET];
|
|
1816
|
-
return n
|
|
1804
|
+
return n?.s === true;
|
|
1817
1805
|
}
|
|
1818
1806
|
|
|
1819
1807
|
/** True when `proxy` belongs to a projection family (S3). */ function storeHasFamily(e) {
|
|
1820
1808
|
const n = e?.[$TARGET];
|
|
1821
|
-
return n
|
|
1809
|
+
return n?.fam != null;
|
|
1822
1810
|
}
|
|
1823
1811
|
|
|
1824
1812
|
/** True when `proxy` belongs to an OPTIMISTIC family (S4). */ function storeHasOptimisticFamily(e) {
|
|
1825
1813
|
const n = e?.[$TARGET];
|
|
1826
|
-
return n
|
|
1814
|
+
return n?.fam?.opt === true;
|
|
1827
1815
|
}
|
|
1828
1816
|
|
|
1829
1817
|
// ---------------------------------------------------------------------------
|
|
@@ -1833,7 +1821,7 @@ function visibleKeys(e, n) {
|
|
|
1833
1821
|
if (e.ovl && n === e.pb) {
|
|
1834
1822
|
t = Reflect.ownKeys(e.v);
|
|
1835
1823
|
const r = e.del;
|
|
1836
|
-
if (r
|
|
1824
|
+
if (r?.size) t = t.filter(e => !r.has(e));
|
|
1837
1825
|
for (const r of Reflect.ownKeys(n)) {
|
|
1838
1826
|
if (!hasOwn.call(e.v, r)) t.push(r);
|
|
1839
1827
|
}
|
|
@@ -1851,7 +1839,7 @@ function visibleKeys(e, n) {
|
|
|
1851
1839
|
function visibleDescriptor(e, n, t) {
|
|
1852
1840
|
let r = Object.getOwnPropertyDescriptor(n, t);
|
|
1853
1841
|
if (e.ovl && n === e.pb) {
|
|
1854
|
-
if (e.del
|
|
1842
|
+
if (e.del?.has(t)) return undefined;
|
|
1855
1843
|
if (r === undefined) r = Object.getOwnPropertyDescriptor(e.v, t);
|
|
1856
1844
|
}
|
|
1857
1845
|
return r;
|
|
@@ -1862,7 +1850,7 @@ function visibleDescriptor(e, n, t) {
|
|
|
1862
1850
|
/** Tracking snapshot: subscribes every reachable record's container and deep
|
|
1863
1851
|
* witness (one node per record, not one per path), then snapshots. */ function deep(e) {
|
|
1864
1852
|
const n = e?.[$TARGET];
|
|
1865
|
-
if (n
|
|
1853
|
+
if (n?.px !== e) return e;
|
|
1866
1854
|
const t = new Set;
|
|
1867
1855
|
const childTarget = (e, n, t) => {
|
|
1868
1856
|
let r = lookupTarget(n, e.fam);
|
|
@@ -17,31 +17,27 @@ const $OWNER = Symbol(0);
|
|
|
17
17
|
/** raw → target for plain (family-less) stores. Family stores use their own
|
|
18
18
|
* map so one raw can wrap once per family (§7b). */ const storeLookup = new WeakMap;
|
|
19
19
|
|
|
20
|
-
function setLookup(
|
|
21
|
-
(
|
|
20
|
+
function setLookup(o, e) {
|
|
21
|
+
(e.fam?.map ?? storeLookup).set(o, e);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
function isOwned(
|
|
25
|
-
return
|
|
24
|
+
function isOwned(o) {
|
|
25
|
+
return o[$OWNER] !== undefined;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
/** The target wrapping `raw` in `fam` (or the plain lookup). An owned raw
|
|
29
|
-
* answers from its stamp; a user raw from the map. */ function lookupTarget(
|
|
30
|
-
const
|
|
31
|
-
return
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function devAssertNeverUserMutation(e) {
|
|
35
|
-
return;
|
|
29
|
+
* answers from its stamp; a user raw from the map. */ function lookupTarget(o, e) {
|
|
30
|
+
const n = o[$OWNER];
|
|
31
|
+
return n !== undefined && n.fam === e ? n : (e?.map ?? storeLookup).get(o);
|
|
36
32
|
}
|
|
37
33
|
|
|
38
34
|
/** Sticky descendants flag up the parent chain (§6d): adoption descends
|
|
39
|
-
* into a changed child pair only where a target with nodes exists below. */ function markDescendants(
|
|
40
|
-
let
|
|
41
|
-
while (
|
|
42
|
-
|
|
43
|
-
|
|
35
|
+
* into a changed child pair only where a target with nodes exists below. */ function markDescendants(o) {
|
|
36
|
+
let e = o;
|
|
37
|
+
while (e && !e.d) {
|
|
38
|
+
e.d = true;
|
|
39
|
+
e = e.u;
|
|
44
40
|
}
|
|
45
41
|
}
|
|
46
42
|
|
|
47
|
-
export { $OWNER,
|
|
43
|
+
export { $OWNER, isOwned, lookupTarget, markDescendants, setLookup, storeLookup };
|