@solidjs/signals 2.0.0-rc.14 → 2.0.0-rc.15
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 +248 -189
- package/dist/dev.attribution.js +124 -41
- package/dist/dev.js +553 -322
- package/dist/observe/affects.js +34 -31
- package/dist/observe/boundaries.js +166 -132
- package/dist/observe/core/async.js +90 -83
- package/dist/observe/core/attribution-queries.js +1 -1
- package/dist/observe/core/attribution.js +281 -197
- package/dist/observe/core/constants.js +7 -2
- package/dist/observe/core/context.js +3 -3
- package/dist/observe/core/core.js +277 -245
- package/dist/observe/core/dev.js +3 -3
- package/dist/observe/core/effect.js +21 -21
- package/dist/observe/core/external.js +2 -2
- package/dist/observe/core/graph.js +25 -25
- package/dist/observe/core/heap.js +9 -9
- package/dist/observe/core/lanes.js +206 -160
- package/dist/observe/core/owner.js +27 -27
- package/dist/observe/core/scheduler.js +92 -86
- package/dist/observe/core/verdict.js +66 -54
- package/dist/observe/map.js +35 -35
- package/dist/observe/reveal.js +24 -24
- package/dist/observe/signals.js +38 -28
- package/dist/observe/store/affects.js +75 -68
- package/dist/observe/store/hooks.js +13 -0
- package/dist/observe/store/index.js +0 -2
- package/dist/observe/store/optimistic.js +67 -34
- package/dist/observe/store/projection.js +37 -31
- package/dist/observe/store/reconcile.js +32 -30
- package/dist/observe/store/store.js +128 -85
- package/dist/prod/affects.js +34 -31
- package/dist/prod/boundaries.js +143 -109
- package/dist/prod/core/async.js +64 -67
- package/dist/prod/core/constants.js +7 -2
- package/dist/prod/core/context.js +3 -3
- package/dist/prod/core/core.js +302 -270
- package/dist/prod/core/effect.js +38 -38
- package/dist/prod/core/external.js +4 -4
- package/dist/prod/core/graph.js +27 -27
- package/dist/prod/core/heap.js +30 -30
- package/dist/prod/core/lanes.js +257 -211
- package/dist/prod/core/owner.js +39 -39
- package/dist/prod/core/scheduler.js +125 -119
- package/dist/prod/core/verdict.js +70 -58
- package/dist/prod/map.js +73 -73
- package/dist/prod/reveal.js +38 -38
- package/dist/prod/signals.js +30 -20
- package/dist/prod/store/affects.js +75 -68
- package/dist/prod/store/hooks.js +13 -0
- package/dist/prod/store/index.js +0 -2
- package/dist/prod/store/optimistic.js +114 -81
- package/dist/prod/store/projection.js +30 -24
- package/dist/prod/store/reconcile.js +32 -30
- package/dist/prod/store/store.js +136 -93
- package/dist/types/boundaries.d.ts +4 -0
- package/dist/types/core/attribution.d.ts +6 -2
- package/dist/types/core/constants.d.ts +5 -0
- package/dist/types/core/core.d.ts +33 -3
- package/dist/types/core/lanes.d.ts +10 -0
- package/dist/types/core/scheduler.d.ts +12 -6
- package/dist/types/core/types.d.ts +0 -6
- package/dist/types/store/affects.d.ts +11 -0
- package/dist/types/store/hooks.d.ts +48 -0
- package/dist/types/store/index.d.ts +0 -1
- package/dist/types/store/store.d.ts +3 -38
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@ import { attrHooks } from "../core/attribution-hooks.js";
|
|
|
2
2
|
|
|
3
3
|
import { CONFIG_MANUAL_WRITE, CONFIG_OVERRIDE, CONFIG_HELD, $REFRESH, NOT_PENDING, STATUS_PENDING, STATUS_ERROR, EFFECT_RENDER, REACTIVE_RECOMPUTING_DEPS, REACTIVE_STAGED_READ, CONFIG_VERDICT, CONFIG_CHILDREN_FORBIDDEN, CONFIG_PROMOTED, REACTIVE_DIRTY, REACTIVE_CHECK } from "../core/constants.js";
|
|
4
4
|
|
|
5
|
-
import { setSignal, isEqual, read, verdict, context, notePromotedWrite, untrack, slotSignal, stagedScreen, stagedRead } from "../core/core.js";
|
|
5
|
+
import { setSignal, isEqual, read, verdict, snapshotCaptureActive, captureWriteSnapshot, context, notePromotedWrite, untrack, slotSignal, stagedScreen, stagedRead } from "../core/core.js";
|
|
6
6
|
|
|
7
7
|
import { registerGraph } from "../core/dev.js";
|
|
8
8
|
|
|
@@ -16,6 +16,8 @@ import { txOf, flushTransaction, holdNode, joinFuture, insertSubs, schedule, pas
|
|
|
16
16
|
|
|
17
17
|
import { lookupTarget, storeOwners, setLookup, $OWNER, isOwned, markDescendants, devAssertNeverUserMutation } from "./target.js";
|
|
18
18
|
|
|
19
|
+
import { optHooks, affectsHooks } from "./hooks.js";
|
|
20
|
+
|
|
19
21
|
import { $TARGET, rawValuesUsed, isRawValue, isWrappable, markRawIngest, writeOverride, markRawOne, $RECORD, $PROXY, $TRACK } from "./types.js";
|
|
20
22
|
|
|
21
23
|
export { markRaw } from "./types.js";
|
|
@@ -49,18 +51,7 @@ export { markRaw } from "./types.js";
|
|
|
49
51
|
* Perf contracts (plan sec. 31.4): lazy nodes; one literal per node; O(written) per
|
|
50
52
|
* setter (`wk`); overlay drafts for large plain records; no new named
|
|
51
53
|
* fields on array targets; zero allocation for adopted-but-unread objects.
|
|
52
|
-
*/
|
|
53
|
-
|
|
54
|
-
function installOptHooks(e) {
|
|
55
|
-
optHooks = e;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
let affectsHooks = null;
|
|
59
|
-
|
|
60
|
-
function installAffectsHooks(e) {
|
|
61
|
-
affectsHooks = e;
|
|
62
|
-
}
|
|
63
|
-
|
|
54
|
+
*/
|
|
64
55
|
/** Key of the deep-witness slot node (never a user key). */ const $DEEP = Symbol(0);
|
|
65
56
|
|
|
66
57
|
// ---------------------------------------------------------------------------
|
|
@@ -247,7 +238,7 @@ function releaseSlot(e) {
|
|
|
247
238
|
/** A node was born on `target`: a family target enters the live index. */ function noteNode(e, n, t) {
|
|
248
239
|
if (e.fam !== null) e.fam.live.add(e);
|
|
249
240
|
markDescendants(e);
|
|
250
|
-
if (affectsHooks !== null) affectsHooks.
|
|
241
|
+
if (affectsHooks !== null) affectsHooks.fo(e, n, t);
|
|
251
242
|
}
|
|
252
243
|
|
|
253
244
|
// Shared slot-node release handler: registered once; the core sweep
|
|
@@ -257,7 +248,7 @@ function releaseSlot(e) {
|
|
|
257
248
|
setSlotUnobserved(e => {
|
|
258
249
|
// A staged write — or a lane's value (a guess, S4) — is state only the
|
|
259
250
|
// node holds: defer the release to the commit that resolves it.
|
|
260
|
-
if (e.
|
|
251
|
+
if (e.Oe !== NOT_PENDING || e.i & CONFIG_OVERRIDE) deferredReleases.add(e); else releaseSlot(e);
|
|
261
252
|
});
|
|
262
253
|
|
|
263
254
|
/** A node born while its container has a staging is born from the two
|
|
@@ -265,19 +256,19 @@ setSlotUnobserved(e => {
|
|
|
265
256
|
* pending one — and held by the container's transaction when the container
|
|
266
257
|
* is held (#3706: the unit of the hold is the key — a key the batch left
|
|
267
258
|
* unchanged stages nothing and holds no one). */ function bornStaged(e, n, t, r) {
|
|
268
|
-
if (n.
|
|
259
|
+
if (n.en && n.en(n.Ae, t)) return;
|
|
269
260
|
queuePendingNode(n);
|
|
270
|
-
n.
|
|
261
|
+
n.Oe = t;
|
|
271
262
|
const o = e.k;
|
|
272
263
|
if (o === null) return;
|
|
273
264
|
// a node-less staging holds nothing (`stageOn`)
|
|
274
265
|
// Held by the container's transaction only for a key the HELD staging
|
|
275
266
|
// changed — a mainline layer's change above it stages mainline. (A
|
|
276
267
|
// container carrying a guess is a lane's: the flush's park decides.)
|
|
277
|
-
if ((o.
|
|
268
|
+
if ((o.i & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && heldKeyChanged(e, r)) holdNode(n, txOf(o));
|
|
278
269
|
// A28 (4): a batch written inside a creation-time pass is promoted — its
|
|
279
270
|
// late-materialized leaf with it.
|
|
280
|
-
if (o.
|
|
271
|
+
if (o.i & CONFIG_PROMOTED) notePromotedWrite(n);
|
|
281
272
|
}
|
|
282
273
|
|
|
283
274
|
function getNode(e, n,
|
|
@@ -339,8 +330,8 @@ function getHasNode(e, n) {
|
|
|
339
330
|
// pending backing staged — so whoever asked for it reads the frame it
|
|
340
331
|
// would have read had the node been there from the write.
|
|
341
332
|
if (e.pb !== null && !directCommit) {
|
|
342
|
-
n.
|
|
343
|
-
n.
|
|
333
|
+
n.Ae = preBatch(e);
|
|
334
|
+
n.Oe = e.pb;
|
|
344
335
|
queuePendingNode(n);
|
|
345
336
|
}
|
|
346
337
|
}
|
|
@@ -357,10 +348,10 @@ function getHasNode(e, n) {
|
|
|
357
348
|
* re-created, queued, swept and dropped ~7000 container nodes). If the
|
|
358
349
|
* flush parks, the seam materializes and holds the node then
|
|
359
350
|
* (`_storePark`) — the one case the staging home exists for. */ function stageOn(e, n) {
|
|
360
|
-
if (e.k === null && passLane === null && (context === null || globalQueue.
|
|
351
|
+
if (e.k === null && passLane === null && (context === null || globalQueue.X) && !deriveHolds(e)) return;
|
|
361
352
|
const t = getContainerNode(e);
|
|
362
|
-
if (t.
|
|
363
|
-
t.
|
|
353
|
+
if (t.Oe === NOT_PENDING) queuePendingNode(t);
|
|
354
|
+
t.Oe = n;
|
|
364
355
|
// A28 (4): a write issued inside a pass outside a flush (a creation-time
|
|
365
356
|
// derive) is promoted at the pass's end — the batch with it.
|
|
366
357
|
if (context !== null) notePromotedWrite(t);
|
|
@@ -370,10 +361,10 @@ function getHasNode(e, n) {
|
|
|
370
361
|
/** The projection's derive is held by a transaction and this is its own
|
|
371
362
|
* write (not a user setter's): the staging is the derive's (`holdWithDerive`). */ function deriveHolds(e) {
|
|
372
363
|
const n = e.fam?.node;
|
|
373
|
-
return n != null && !userWrite && (n.
|
|
364
|
+
return n != null && !userWrite && (n.i & CONFIG_HELD) !== 0;
|
|
374
365
|
}
|
|
375
366
|
|
|
376
|
-
GlobalQueue.
|
|
367
|
+
GlobalQueue.An = e => {
|
|
377
368
|
for (let n = 0; n < foldList.length; n++) {
|
|
378
369
|
const t = foldList[n];
|
|
379
370
|
if (t.k === null && t.pb !== null) holdNode(getContainerNode(t), e);
|
|
@@ -402,8 +393,9 @@ function bumpDeep(e) {
|
|
|
402
393
|
// on the guess (`laneWrite` → `supersede`, judged by `containerEquals`);
|
|
403
394
|
// otherwise the structural readers are walked (the staging is already on
|
|
404
395
|
// the node; the comparator is the store's, done).
|
|
405
|
-
if (e.
|
|
406
|
-
if (e.
|
|
396
|
+
if (e.i & CONFIG_OVERRIDE) setSignal(e, n); else {
|
|
397
|
+
if (e.i & CONFIG_HELD) joinFuture(txOf(e));
|
|
398
|
+
if (snapshotCaptureActive) captureWriteSnapshot(e, e.Ae);
|
|
407
399
|
insertSubs(e);
|
|
408
400
|
schedule();
|
|
409
401
|
}
|
|
@@ -421,7 +413,7 @@ function storeRootOwner(e) {
|
|
|
421
413
|
}
|
|
422
414
|
|
|
423
415
|
function stampNodeOwner(e, n) {
|
|
424
|
-
e.
|
|
416
|
+
e.L = storeRootOwner(n) ?? null;
|
|
425
417
|
}
|
|
426
418
|
|
|
427
419
|
const storeNames = new WeakMap;
|
|
@@ -540,7 +532,7 @@ const plainProto = e => {
|
|
|
540
532
|
e.pb = t;
|
|
541
533
|
e.ovl = false;
|
|
542
534
|
// The staging IS the pending backing: re-point the container node.
|
|
543
|
-
if (e.k !== null && e.k.
|
|
535
|
+
if (e.k !== null && e.k.Oe === n) e.k.Oe = t;
|
|
544
536
|
return t;
|
|
545
537
|
}
|
|
546
538
|
|
|
@@ -561,7 +553,7 @@ const plainProto = e => {
|
|
|
561
553
|
// mainline setter mid-hold publishes mainline; the held keys stay held).
|
|
562
554
|
n = e.pb = cloneRaw(n, e);
|
|
563
555
|
const t = e.k;
|
|
564
|
-
if (t !== null && !(t.
|
|
556
|
+
if (t !== null && !(t.i & CONFIG_HELD)) t.Oe = n;
|
|
565
557
|
} else if (n === null) {
|
|
566
558
|
const t = e.v;
|
|
567
559
|
if (!directCommit) queueFold(e);
|
|
@@ -640,7 +632,7 @@ const plainProto = e => {
|
|
|
640
632
|
} else r = e.v;
|
|
641
633
|
if (directCommit) {
|
|
642
634
|
e.pb = null;
|
|
643
|
-
if (e.k !== null) e.k.
|
|
635
|
+
if (e.k !== null) e.k.Ae = n;
|
|
644
636
|
} else {
|
|
645
637
|
// Eager (INTERNALS §3): the backing swaps now — handlers read the
|
|
646
638
|
// adopted object before the flush — while the container node, when
|
|
@@ -736,7 +728,7 @@ let foldIndex = null;
|
|
|
736
728
|
if (r.pb === null) continue;
|
|
737
729
|
// folded already (a direct commit)
|
|
738
730
|
const o = r.k;
|
|
739
|
-
if (o !== null && o.
|
|
731
|
+
if (o !== null && o.Oe !== NOT_PENDING) {
|
|
740
732
|
// held: commits with its hold
|
|
741
733
|
foldList.push(r);
|
|
742
734
|
foldOlds.push(n[t]);
|
|
@@ -747,7 +739,7 @@ let foldIndex = null;
|
|
|
747
739
|
}
|
|
748
740
|
if (deferredReleases.size !== 0) {
|
|
749
741
|
for (const e of deferredReleases) {
|
|
750
|
-
if (e.
|
|
742
|
+
if (e.Oe === NOT_PENDING && !(e.i & CONFIG_OVERRIDE) && e.t === null) {
|
|
751
743
|
deferredReleases.delete(e);
|
|
752
744
|
releaseSlot(e);
|
|
753
745
|
}
|
|
@@ -758,29 +750,34 @@ let foldIndex = null;
|
|
|
758
750
|
/** Core asks: is a slot node's truth in flight? Its family's derive is —
|
|
759
751
|
* lanes (A17, #2864: a guess mid-refetch stands in for the flight) and
|
|
760
752
|
* marks (an ambient `affects()` over a declared reload lives to the
|
|
761
|
-
* landing) read it. */ GlobalQueue.
|
|
753
|
+
* landing) read it. */ GlobalQueue.C = e => {
|
|
762
754
|
const n = e.yt?.fam;
|
|
763
|
-
return n != null && n.node !== null && (n.node.
|
|
755
|
+
return n != null && n.node !== null && (n.node.S & STATUS_PENDING) !== 0;
|
|
764
756
|
};
|
|
765
757
|
|
|
766
758
|
/** Core asks (lanes.ts `covered`): the value a slot node's guess covered.
|
|
767
759
|
* A chained link's committed value is the inner store's at the last
|
|
768
760
|
* read-through — refreshed here, since a guess served the reads since
|
|
769
|
-
* (#3672: the revert compares against the base's live value). */ GlobalQueue.
|
|
761
|
+
* (#3672: the revert compares against the base's live value). */ GlobalQueue.In = e => {
|
|
770
762
|
const n = e.yt;
|
|
771
763
|
if (n.ch) {
|
|
772
764
|
const t = e.bt;
|
|
773
765
|
const r = n.v;
|
|
774
|
-
if (n.h !== null && n.h[t] === e) e.
|
|
766
|
+
if (n.h !== null && n.h[t] === e) e.Ae = untrack(() => t in r); else if (n.n !== null && n.n[t] === e) e.Ae = untrack(() => unwrapValue(r[t]));
|
|
775
767
|
}
|
|
776
|
-
return e.
|
|
768
|
+
return e.Ae;
|
|
777
769
|
};
|
|
778
770
|
|
|
779
|
-
GlobalQueue.
|
|
771
|
+
GlobalQueue.Rn = () => {
|
|
780
772
|
drainFolds();
|
|
781
773
|
optHooks?.sweep();
|
|
782
774
|
};
|
|
783
775
|
|
|
776
|
+
/** The store half of `affects()` (store/affects.ts) makes its marks' nodes
|
|
777
|
+
* and walks records through these: it never imports the engine. */ GlobalQueue.no = getNode;
|
|
778
|
+
|
|
779
|
+
GlobalQueue.so = isWrappable;
|
|
780
|
+
|
|
784
781
|
/** A projection's creation run commits directly (a memo's first value is
|
|
785
782
|
* its `_value`, not a staging): the draft still writes a clone of the seed,
|
|
786
783
|
* nothing is staged on the nodes, and the setter's exit folds the touched
|
|
@@ -807,7 +804,7 @@ function runDirect(e) {
|
|
|
807
804
|
// the adopted object (shared ownership — never cloned).
|
|
808
805
|
e.pb = null;
|
|
809
806
|
e.wk = null;
|
|
810
|
-
} else if (e.ovl && (e.v !== n || !overlayRebuilds(e, r))) {
|
|
807
|
+
} else if (e.ovl && (e.v !== n || !overlayRebuilds(e, r) && t?.m?.ot !== n)) {
|
|
811
808
|
// Overlay flatten (#3044): the backing keeps its identity, so the
|
|
812
809
|
// `t.v === old` gate below skips path copying (the parent slot
|
|
813
810
|
// already points here) and the adopted-notify (setter
|
|
@@ -849,7 +846,7 @@ function runDirect(e) {
|
|
|
849
846
|
// subscribes to was a transient staging home (INTERNALS §5: an
|
|
850
847
|
// observer-less write holds a transient record until the fold,
|
|
851
848
|
// discarded with it) — gone with the batch.
|
|
852
|
-
if (t.t === null) e.k = null; else if (t.
|
|
849
|
+
if (t.t === null) e.k = null; else if (t.Ae instanceof LaneView) t.Ae.base = e.v; else t.Ae = e.v;
|
|
853
850
|
}
|
|
854
851
|
if (e.v !== n && e.u) {
|
|
855
852
|
const t = parentSlotKey(e, n);
|
|
@@ -922,9 +919,9 @@ function reportReplacedContainers(e, n, t, r) {
|
|
|
922
919
|
|
|
923
920
|
function heldDerivation(e, n, t) {
|
|
924
921
|
if (e.fam === null || !userWrite) return false;
|
|
925
|
-
if (n !== undefined) return (n.
|
|
922
|
+
if (n !== undefined) return (n.i & (CONFIG_HELD | CONFIG_MANUAL_WRITE)) === CONFIG_HELD && txOf(n) !== flushTransaction;
|
|
926
923
|
const r = e.k;
|
|
927
|
-
return r !== null && (r.
|
|
924
|
+
return r !== null && (r.i & CONFIG_HELD) !== 0 && txOf(r) !== flushTransaction && heldKeyChanged(e, t);
|
|
928
925
|
}
|
|
929
926
|
|
|
930
927
|
/** Setter exit: one node write per changed observed key (the leaves stage
|
|
@@ -970,15 +967,18 @@ function heldDerivation(e, n, t) {
|
|
|
970
967
|
if (o.acc === true || hasOwn.call(n, i) && lookupGetter.call(n, i) !== undefined) {
|
|
971
968
|
// Accessor keys: the node is linked for shape-change notification,
|
|
972
969
|
// its value is never served (the getter runs with the proxy
|
|
973
|
-
// receiver on read) — FORCE wakes the readers.
|
|
970
|
+
// receiver on read) — FORCE wakes the readers. A data descriptor
|
|
971
|
+
// falls through so the cleared-acc hot read serves it (#3949);
|
|
972
|
+
// an own getter's descriptor has no `value`, so the compare below
|
|
973
|
+
// would otherwise keep the getter's result (or FORCE) for `undefined`.
|
|
974
974
|
o.acc = isOwnAccessor(n, i);
|
|
975
975
|
const e = Object.getOwnPropertyDescriptor(t, i);
|
|
976
976
|
const r = Object.getOwnPropertyDescriptor(n, i);
|
|
977
|
-
if (
|
|
977
|
+
if (!r || r.get || r.set) {
|
|
978
978
|
if (e?.get !== r?.get || e?.set !== r?.set || e?.value !== r?.value) setSignal(o, () => FORCE);
|
|
979
979
|
continue;
|
|
980
980
|
}
|
|
981
|
-
if (!isEqual(e?.value, r
|
|
981
|
+
if (e?.get || e?.set || !isEqual(e?.value, r.value)) setSignal(o, () => r.value);
|
|
982
982
|
continue;
|
|
983
983
|
}
|
|
984
984
|
const l = e.del !== null && e.del.has(i) ? undefined : n[i];
|
|
@@ -987,9 +987,9 @@ function heldDerivation(e, n, t) {
|
|
|
987
987
|
// The derive's write is a derivation; a user setter's is a proposal
|
|
988
988
|
// (A34 (3)'s discriminator).
|
|
989
989
|
if (!userWrite) {
|
|
990
|
-
o.
|
|
990
|
+
o.i &= ~CONFIG_MANUAL_WRITE;
|
|
991
991
|
holdWithDerive(e, o);
|
|
992
|
-
} else o.
|
|
992
|
+
} else o.i |= CONFIG_MANUAL_WRITE;
|
|
993
993
|
}
|
|
994
994
|
}
|
|
995
995
|
}
|
|
@@ -1020,7 +1020,7 @@ function heldDerivation(e, n, t) {
|
|
|
1020
1020
|
// container carrying an arrangement guess is told whether or not anyone
|
|
1021
1021
|
// subscribes: the write is a landing on the guess (`notifyContainer`).
|
|
1022
1022
|
const u = e.k;
|
|
1023
|
-
if (u !== null && (u.t !== null || u.
|
|
1023
|
+
if (u !== null && (u.t !== null || u.i & CONFIG_OVERRIDE)) {
|
|
1024
1024
|
let r;
|
|
1025
1025
|
if (e.ovl) {
|
|
1026
1026
|
r = e.del !== null && e.del.size !== 0;
|
|
@@ -1069,13 +1069,14 @@ function membershipChanged(e, n) {
|
|
|
1069
1069
|
e.acc = isOwnAccessor(r, n);
|
|
1070
1070
|
const o = Object.getOwnPropertyDescriptor(t, n);
|
|
1071
1071
|
const i = Object.getOwnPropertyDescriptor(r, n);
|
|
1072
|
-
|
|
1072
|
+
// Data descriptors fall through, as in notifyWrites (#3949).
|
|
1073
|
+
if (!i || i.get || i.set) {
|
|
1073
1074
|
if (o?.get !== i?.get || o?.set !== i?.set || o?.value !== i?.value) setSignal(e, () => FORCE);
|
|
1074
1075
|
return;
|
|
1075
1076
|
}
|
|
1076
1077
|
const l = o?.value;
|
|
1077
1078
|
const u = i?.value;
|
|
1078
|
-
if (!isEqual(l, u) && !targetsEqual(l, u)) setSignal(e, typeof u === "function" ? () => u : u);
|
|
1079
|
+
if (o?.get || o?.set || !isEqual(l, u) && !targetsEqual(l, u)) setSignal(e, typeof u === "function" ? () => u : u);
|
|
1079
1080
|
} else {
|
|
1080
1081
|
const o = t[n];
|
|
1081
1082
|
const i = r[n];
|
|
@@ -1121,7 +1122,7 @@ function hasAccessorFlag(e) {
|
|
|
1121
1122
|
for (const o of Reflect.ownKeys(r)) if (e || o in n !== o in t) setSignal(r[o], o in t);
|
|
1122
1123
|
}
|
|
1123
1124
|
const o = e.k;
|
|
1124
|
-
if (o !== null && (o.t !== null || o.
|
|
1125
|
+
if (o !== null && (o.t !== null || o.i & CONFIG_OVERRIDE)) {
|
|
1125
1126
|
const e = Array.isArray(t) && Array.isArray(n) ? arrayStructureChanged(n, t) : membershipChanged(n, t);
|
|
1126
1127
|
if (e) notifyContainer(o, t);
|
|
1127
1128
|
}
|
|
@@ -1178,7 +1179,7 @@ function inDraft(e) {
|
|
|
1178
1179
|
* "adoption is eager by contract" — and the node keeps the frame committed
|
|
1179
1180
|
* readers under a hold still see), `t.v` otherwise. */ function committed(e) {
|
|
1180
1181
|
const n = e.k;
|
|
1181
|
-
if (n !== null) return n.
|
|
1182
|
+
if (n !== null) return n.Oe !== NOT_PENDING ? asBacking(n.Ae) : e.v;
|
|
1182
1183
|
// A node-less staging (`stageOn`): the fold queue remembers the frame.
|
|
1183
1184
|
return e.pb !== null ? preBatch(e) : e.v;
|
|
1184
1185
|
}
|
|
@@ -1191,7 +1192,7 @@ function inDraft(e) {
|
|
|
1191
1192
|
let e = context;
|
|
1192
1193
|
if (e === null) return null;
|
|
1193
1194
|
if (e.Ht) e = e.gt ?? null;
|
|
1194
|
-
return e === null || e.
|
|
1195
|
+
return e === null || e.i & CONFIG_CHILDREN_FORBIDDEN ? null : e;
|
|
1195
1196
|
}
|
|
1196
1197
|
|
|
1197
1198
|
/** The backing a read serves (RUL-1, signal parity): the draft and the write
|
|
@@ -1229,10 +1230,10 @@ function inDraft(e) {
|
|
|
1229
1230
|
if (writeOverride && !userWrite) {
|
|
1230
1231
|
const t = n !== undefined ? e.n?.[n] : undefined;
|
|
1231
1232
|
if (t !== undefined) {
|
|
1232
|
-
if ((t.
|
|
1233
|
+
if ((t.i & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD) joinPassTx(txOf(t));
|
|
1233
1234
|
} else {
|
|
1234
1235
|
const t = e.k;
|
|
1235
|
-
if (t !== null && (t.
|
|
1236
|
+
if (t !== null && (t.i & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && (n === undefined || heldKeyChanged(e, n))) joinPassTx(txOf(t));
|
|
1236
1237
|
}
|
|
1237
1238
|
}
|
|
1238
1239
|
return r;
|
|
@@ -1244,9 +1245,9 @@ function inDraft(e) {
|
|
|
1244
1245
|
// it).
|
|
1245
1246
|
if (n !== undefined) {
|
|
1246
1247
|
const t = e.n?.[n];
|
|
1247
|
-
if (t !== undefined && t.
|
|
1248
|
+
if (t !== undefined && t.i & CONFIG_OVERRIDE) return committed(e);
|
|
1248
1249
|
}
|
|
1249
|
-
const i = o !== null && (o.
|
|
1250
|
+
const i = o !== null && (o.i & CONFIG_HELD) !== 0;
|
|
1250
1251
|
// A held container: the hold's unit is the key (#3706). A key the HELD
|
|
1251
1252
|
// staging left unchanged reads the ambient view — a mainline layer's
|
|
1252
1253
|
// write above the hold publishes mainline (#3688, #3612); a key it
|
|
@@ -1264,7 +1265,7 @@ function inDraft(e) {
|
|
|
1264
1265
|
// probe) judges the container like any node — for a key the batch
|
|
1265
1266
|
// changed; an unchanged key is committed and final (A22: pending is per
|
|
1266
1267
|
// key). A held container's committed frame is the node's.
|
|
1267
|
-
if (verdict === null || !i && n !== undefined && !keyChanged(e, n, t)) return i ? asBacking(o.
|
|
1268
|
+
if (verdict === null || !i && n !== undefined && !keyChanged(e, n, t)) return i ? asBacking(o.Ae) : e.v;
|
|
1268
1269
|
return o === null ? committed(e) : asBacking(read(o));
|
|
1269
1270
|
}
|
|
1270
1271
|
// A pass reading a key the batch left unchanged: the same value in both
|
|
@@ -1281,13 +1282,13 @@ function inDraft(e) {
|
|
|
1281
1282
|
// frames, a changed one is what the mark already stands for. The
|
|
1282
1283
|
// per-key question is asked once per pass, not once per key (a
|
|
1283
1284
|
// `mapArray` over 1k shifted rows asked it 1k times).
|
|
1284
|
-
if (l.R & REACTIVE_STAGED_READ && !(l.
|
|
1285
|
+
if (l.R & REACTIVE_STAGED_READ && !(l.i & (CONFIG_VERDICT | CONFIG_CHILDREN_FORBIDDEN))) return r;
|
|
1285
1286
|
if (n !== undefined && !keyChanged(e, n, t)) return committed(e);
|
|
1286
1287
|
// A node-less staging (`stageOn`): what `read()` of its node would do
|
|
1287
1288
|
// for this pass — a verdict reader sees the screen, any other derives
|
|
1288
1289
|
// from the staging and is marked as having read it.
|
|
1289
1290
|
if (o === null) {
|
|
1290
|
-
if (l.
|
|
1291
|
+
if (l.i & CONFIG_VERDICT && stagedScreen(l)) return committed(e);
|
|
1291
1292
|
stagedRead(l);
|
|
1292
1293
|
return r;
|
|
1293
1294
|
}
|
|
@@ -1297,11 +1298,52 @@ function inDraft(e) {
|
|
|
1297
1298
|
|
|
1298
1299
|
/** Did the HELD staging (the container node's, not a mainline layer above
|
|
1299
1300
|
* it) change `key`? */ function heldKeyChanged(e, n, t = true) {
|
|
1300
|
-
return changedBetween(e, e.k.
|
|
1301
|
+
return changedBetween(e, e.k.Ae, e.k.Oe, n, t) && !adoptChild(e, n);
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
/** The values of a held frame, built once per frame: neither the committed
|
|
1305
|
+
* frame nor the held staging changes while the hold stands (a draft writes a
|
|
1306
|
+
* clone), so the set stays true for every first read of the hold. */ const frameRaws = new WeakMap;
|
|
1307
|
+
|
|
1308
|
+
function rawsOf(e) {
|
|
1309
|
+
let n = frameRaws.get(e);
|
|
1310
|
+
if (n === undefined) frameRaws.set(e, n = new Set(Object.values(e)));
|
|
1311
|
+
return n;
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
/** The same row at a slot, as the adoption pairs rows (reconcile.ts): two
|
|
1315
|
+
* objects whose keys agree — positionally for an unkeyed family. */ function sameRow(e, n, t) {
|
|
1316
|
+
return e !== null && typeof e === "object" && n !== null && typeof n === "object" && (t === null || sameKey(t(e), t(n)));
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
/** A key of a held adoption whose old and new values are the same row that
|
|
1320
|
+
* nothing had proxied when the adoption ran, so `descend` never paired
|
|
1321
|
+
* them: paired now, as it would have been (#3712) — one target, the old
|
|
1322
|
+
* raw its committed frame, the new raw its staging, held by the same
|
|
1323
|
+
* transaction. Both raws resolve to it, so the slot reads as unchanged and
|
|
1324
|
+
* the row's own leaves decide: `id` the same in both frames holds no one,
|
|
1325
|
+
* `version` holds. A record's keys pair by name; an array's by the family's
|
|
1326
|
+
* key — without a family the pairing is not known here and the hold stands. */ function adoptChild(e, n) {
|
|
1327
|
+
const t = e.k;
|
|
1328
|
+
const r = asBacking(t.Ae);
|
|
1329
|
+
const o = r[n];
|
|
1330
|
+
const i = t.Oe[n];
|
|
1331
|
+
const l = Array.isArray(r) ? e.fam?.key : null;
|
|
1332
|
+
if (e.s || l === undefined || !sameRow(o, i, l) || !isWrappable(o) || !isWrappable(i) || Array.isArray(o) !== Array.isArray(i) || lookupTarget(o, e.fam) !== undefined || lookupTarget(i, e.fam) !== undefined ||
|
|
1333
|
+
// A raw in both frames (the same objects swapped or reordered) is its
|
|
1334
|
+
// own row: pairing it with another would alias the two after the commit.
|
|
1335
|
+
rawsOf(r).has(i) || rawsOf(t.Oe).has(o)) return false;
|
|
1336
|
+
// A raw-marked value wraps to itself, a proxy to its own target: neither
|
|
1337
|
+
// is a fresh target around `ov`.
|
|
1338
|
+
const u = wrap(o, e, n)[$TARGET];
|
|
1339
|
+
if (u === undefined || u.v !== o) return false;
|
|
1340
|
+
adoptPB(u, i, false);
|
|
1341
|
+
holdNode(getContainerNode(u), txOf(t));
|
|
1342
|
+
return true;
|
|
1301
1343
|
}
|
|
1302
1344
|
|
|
1303
1345
|
/** A family target's unheld staging, read with no pass (a handler). */ function familyAhead(e) {
|
|
1304
|
-
return e.fam !== null && context === null && !(e.k !== null && e.k.
|
|
1346
|
+
return e.fam !== null && context === null && !(e.k !== null && e.k.i & CONFIG_HELD);
|
|
1305
1347
|
}
|
|
1306
1348
|
|
|
1307
1349
|
/** Did this batch change `key` — value (slot equality), presence, and (for
|
|
@@ -1335,7 +1377,7 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1335
1377
|
* tracked or not, applies the hold and A28 rules a signal's read does. A
|
|
1336
1378
|
* projection's unheld staging is ahead for a handler (`familyAhead`). */ function nodeValue(e, n, t) {
|
|
1337
1379
|
let r = read(e);
|
|
1338
|
-
if (e.
|
|
1380
|
+
if (e.Oe !== NOT_PENDING && !(e.i & CONFIG_HELD) && context === null && t.fam !== null) r = e.Oe;
|
|
1339
1381
|
return r === FORCE ? n : r;
|
|
1340
1382
|
}
|
|
1341
1383
|
|
|
@@ -1355,8 +1397,8 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1355
1397
|
if (l) {
|
|
1356
1398
|
// The link's committed value follows the inner store's (what a lane
|
|
1357
1399
|
// over it covers — its reveal and revert compare against it, S4).
|
|
1358
|
-
o.
|
|
1359
|
-
if (o.
|
|
1400
|
+
o.Ae = t;
|
|
1401
|
+
if (o.i & CONFIG_OVERRIDE) u = n;
|
|
1360
1402
|
} else u = n;
|
|
1361
1403
|
}
|
|
1362
1404
|
}
|
|
@@ -1377,7 +1419,7 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1377
1419
|
}
|
|
1378
1420
|
|
|
1379
1421
|
/** Is `n` among `c`'s dependencies (any pass's — a kept tail included)? */ function linkedTo(e, n) {
|
|
1380
|
-
for (let t = e.
|
|
1422
|
+
for (let t = e.G; t !== null; t = t._) if (t.I === n) return true;
|
|
1381
1423
|
return false;
|
|
1382
1424
|
}
|
|
1383
1425
|
|
|
@@ -1396,7 +1438,7 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1396
1438
|
// reader of a memo would — linked, so it re-runs at the landing (the
|
|
1397
1439
|
// next settled pass pulls without linking and the stale link trims), a
|
|
1398
1440
|
// verdict reader registered, a tracked pass suspended.
|
|
1399
|
-
if (n.
|
|
1441
|
+
if (n.S & (STATUS_PENDING | STATUS_ERROR)) {
|
|
1400
1442
|
// A render effect outside the flight's own flush is the frame, not a
|
|
1401
1443
|
// derivation: it keeps what it shows and learns of the landing from the
|
|
1402
1444
|
// leaves the landing changes (unchanged leaves say nothing — it is not a
|
|
@@ -1404,9 +1446,10 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1404
1446
|
// One that already observes the flight (linked by the pass that saw it
|
|
1405
1447
|
// go up — the frame's hold, `blocked`) keeps observing it: a re-run for
|
|
1406
1448
|
// another reason (a guess it read, the seam) that dropped the link would
|
|
1407
|
-
// release the hold with the flight still up.
|
|
1449
|
+
// release the hold with the flight still up. An errored derive has no
|
|
1450
|
+
// landing to learn of: the error is what the frame shows.
|
|
1408
1451
|
const t = context;
|
|
1409
|
-
if (t !== null && t.
|
|
1452
|
+
if (t !== null && t.u === EFFECT_RENDER && !(n.S & STATUS_ERROR) && n.i & CONFIG_HELD && flushTransaction !== txOf(n) && !linkedTo(t, n)) return;
|
|
1410
1453
|
// An optimistic family carrying guesses that stand in for the flight
|
|
1411
1454
|
// (A17): the guesses are the display — a reader is served the leaves
|
|
1412
1455
|
// (committed plus the guesses) and does not suspend on the derive; a
|
|
@@ -1414,7 +1457,7 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1414
1457
|
// up). The family's, not the target's: the edit stands in for the one
|
|
1415
1458
|
// flight the family has.
|
|
1416
1459
|
const r = e.fam.overlaid;
|
|
1417
|
-
if (verdict === null && n.
|
|
1460
|
+
if (verdict === null && n.S & STATUS_PENDING && r !== undefined && r.size !== 0) return;
|
|
1418
1461
|
read(n);
|
|
1419
1462
|
} else untrack(() => read(n));
|
|
1420
1463
|
}
|
|
@@ -1427,10 +1470,10 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1427
1470
|
* classification, as core reads it — `heldNotFinal`). */ function holdWithDerive(e, n) {
|
|
1428
1471
|
const t = e.fam?.node;
|
|
1429
1472
|
if (t !== undefined && t !== null && !userWrite && // the derive's own write (its draft or landing), not a user setter's
|
|
1430
|
-
t.
|
|
1473
|
+
t.i & CONFIG_HELD && n.Oe !== NOT_PENDING &&
|
|
1431
1474
|
// A node carrying a guess is the lane's: its staging is the truth beneath
|
|
1432
1475
|
// the guess (`setSignal` → `laneWrite`), never re-homed by the store.
|
|
1433
|
-
!(n.
|
|
1476
|
+
!(n.i & (CONFIG_HELD | CONFIG_OVERRIDE))) {
|
|
1434
1477
|
holdNode(n, txOf(t));
|
|
1435
1478
|
n.m.It = t.m.It;
|
|
1436
1479
|
}
|
|
@@ -1503,7 +1546,7 @@ const traps = {
|
|
|
1503
1546
|
}
|
|
1504
1547
|
// (The witness before the pull: a mark on an uninitialized derived
|
|
1505
1548
|
// store is witnessed, then the pull throws — loading, declared pending.)
|
|
1506
|
-
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.
|
|
1549
|
+
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.ro(e, n);
|
|
1507
1550
|
if (e.fam !== null) pullFamily(e);
|
|
1508
1551
|
// Hot inline case: existing PLAIN node (non-accessor) read outside any
|
|
1509
1552
|
// draft — the dbmon/uibench effect re-read shape. Core's `read()` serves
|
|
@@ -1518,7 +1561,7 @@ const traps = {
|
|
|
1518
1561
|
const r = e.n?.[n];
|
|
1519
1562
|
if (writeScopes === null && !writeOverride) {
|
|
1520
1563
|
const t = r;
|
|
1521
|
-
if (t !== undefined && t.acc !== true && (!e.ch || t.
|
|
1564
|
+
if (t !== undefined && t.acc !== true && (!e.ch || t.i & CONFIG_OVERRIDE)) {
|
|
1522
1565
|
const r = nodeValue(t, undefined, e);
|
|
1523
1566
|
if (r === null || typeof r !== "object") return r;
|
|
1524
1567
|
if (e.s) return serveShallow(e, n, r);
|
|
@@ -1600,7 +1643,7 @@ const traps = {
|
|
|
1600
1643
|
if (n === $OWNER || n === $RECORD) return false;
|
|
1601
1644
|
// (The witness before the pull: a mark on an uninitialized derived
|
|
1602
1645
|
// store is witnessed, then the pull throws — loading, declared pending.)
|
|
1603
|
-
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.
|
|
1646
|
+
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.ro(e, n);
|
|
1604
1647
|
if (e.fam !== null) pullFamily(e);
|
|
1605
1648
|
const t = readSource(e, n);
|
|
1606
1649
|
// A tracked reader's presence node answers (born from the two frames;
|
|
@@ -1613,9 +1656,9 @@ const traps = {
|
|
|
1613
1656
|
const o = read(r);
|
|
1614
1657
|
if (!e.ch || t !== e.v) return !!o;
|
|
1615
1658
|
const i = n in t;
|
|
1616
|
-
r.
|
|
1659
|
+
r.Ae = i;
|
|
1617
1660
|
// the link follows the inner store (S4)
|
|
1618
|
-
if (r.
|
|
1661
|
+
if (r.i & CONFIG_OVERRIDE) return !!o;
|
|
1619
1662
|
return i;
|
|
1620
1663
|
} else if (optRead(e)) {
|
|
1621
1664
|
const t = optHooks.has(e, n);
|
|
@@ -1625,14 +1668,14 @@ const traps = {
|
|
|
1625
1668
|
return n in t && !(e.del !== null && t === e.pb && e.del.has(n));
|
|
1626
1669
|
},
|
|
1627
1670
|
ownKeys(e) {
|
|
1628
|
-
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.
|
|
1671
|
+
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.ro(e, undefined);
|
|
1629
1672
|
if (e.fam !== null) pullFamily(e);
|
|
1630
1673
|
return visibleKeys(e, enumerationSource(e));
|
|
1631
1674
|
},
|
|
1632
1675
|
getOwnPropertyDescriptor(e, n) {
|
|
1633
1676
|
if (n === $OWNER || n === $RECORD) return undefined;
|
|
1634
1677
|
const t = getObserver();
|
|
1635
|
-
if (verdict !== null && affectsHooks !== null && t === null) affectsHooks.
|
|
1678
|
+
if (verdict !== null && affectsHooks !== null && t === null) affectsHooks.ro(e, n);
|
|
1636
1679
|
if (e.fam !== null) pullFamily(e);
|
|
1637
1680
|
// An enumerator (spread, Object.entries) already holds the container
|
|
1638
1681
|
// node and reads its frame; a descriptor read on its own tracks
|
|
@@ -1650,7 +1693,7 @@ const traps = {
|
|
|
1650
1693
|
const t = getHasNode(e, n);
|
|
1651
1694
|
const r = read(t);
|
|
1652
1695
|
i = readSource(e, n, true);
|
|
1653
|
-
if (!r && (!e.ch || t.
|
|
1696
|
+
if (!r && (!e.ch || t.i & CONFIG_OVERRIDE || !(n in i))) return undefined;
|
|
1654
1697
|
} else i = readSource(e, n, true);
|
|
1655
1698
|
let l;
|
|
1656
1699
|
if (!o && optRead(e)) {
|
|
@@ -1744,7 +1787,7 @@ const traps = {
|
|
|
1744
1787
|
const t = e.k;
|
|
1745
1788
|
if (t === null) return false;
|
|
1746
1789
|
const r = t.Dt;
|
|
1747
|
-
return r !== null && r.
|
|
1790
|
+
return r !== null && r.l === n && (!(n.R & REACTIVE_RECOMPUTING_DEPS) || r.Pe === n.pe);
|
|
1748
1791
|
}
|
|
1749
1792
|
|
|
1750
1793
|
function storeSetter(e, n, t = true) {
|
|
@@ -1944,7 +1987,7 @@ function snapshotWalk(e, n, t) {
|
|
|
1944
1987
|
// R27: a snapshot sees the batch's pending backing — a held one (another
|
|
1945
1988
|
// transaction's future) only from its own draft.
|
|
1946
1989
|
let i = o.v;
|
|
1947
|
-
if (o.pb !== null && (inDraft(o) || writeOverride || o.k === null || !(o.k.
|
|
1990
|
+
if (o.pb !== null && (inDraft(o) || writeOverride || o.k === null || !(o.k.i & CONFIG_HELD))) {
|
|
1948
1991
|
if (o.ovl) materializePB(o);
|
|
1949
1992
|
i = o.pb;
|
|
1950
1993
|
}
|
|
@@ -1996,4 +2039,4 @@ function snapshotWalk(e, n, t) {
|
|
|
1996
2039
|
return i ?? r;
|
|
1997
2040
|
}
|
|
1998
2041
|
|
|
1999
|
-
export { $PROXY, $TARGET, $TRACK, LaneView, adoptPB,
|
|
2042
|
+
export { $PROXY, $TARGET, $TRACK, LaneView, adoptPB, arrayStructureChanged, asBacking, bumpDeep, cloneRaw, committed, containerEquals, createStore, deep, getContainerNode, getHasNode, getNode, hasAccessorFlag, materializePB, membershipChanged, nameStore, notifyFold, notifyFoldTail, notifyKeyDiff, notifyKeyValue, resolveChainedRaw, runDirect, sameKey, sameRow, snapshot, storeHasFamily, storeHasOptimisticFamily, storeIsShallow, storeSetter, targetsEqual, unwrapValue, userWriting, wrap };
|