@solidjs/signals 2.0.0-rc.14 → 2.0.0-rc.16
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 +882 -405
- package/dist/dev.attribution.js +172 -66
- package/dist/dev.js +1529 -745
- package/dist/observe/affects-marks.js +86 -0
- package/dist/observe/affects.js +32 -80
- package/dist/observe/boundaries.js +202 -166
- package/dist/observe/core/async.js +192 -141
- package/dist/observe/core/attribution-queries.js +1 -1
- package/dist/observe/core/attribution.js +315 -214
- package/dist/observe/core/constants.js +23 -4
- package/dist/observe/core/context.js +3 -3
- package/dist/observe/core/core.js +532 -360
- package/dist/observe/core/dev.js +63 -35
- package/dist/observe/core/effect.js +47 -47
- package/dist/observe/core/error.js +5 -3
- package/dist/observe/core/external.js +5 -6
- package/dist/observe/core/graph.js +56 -42
- package/dist/observe/core/heap.js +33 -33
- package/dist/observe/core/identities.js +62 -0
- package/dist/observe/core/lanes.js +242 -229
- package/dist/observe/core/owner.js +62 -50
- package/dist/observe/core/scheduler.js +322 -168
- package/dist/observe/core/truth.js +45 -0
- package/dist/observe/core/verdict.js +114 -64
- package/dist/observe/deferred.js +184 -0
- package/dist/observe/index.js +7 -1
- package/dist/observe/map.js +339 -254
- package/dist/observe/reveal.js +77 -77
- package/dist/observe/signals.js +54 -33
- package/dist/observe/store/affects.js +76 -69
- package/dist/observe/store/edges.js +39 -0
- package/dist/observe/store/hooks.js +13 -0
- package/dist/observe/store/index.js +0 -2
- package/dist/observe/store/optimistic.js +82 -44
- package/dist/observe/store/projection.js +83 -64
- package/dist/observe/store/reconcile.js +32 -30
- package/dist/observe/store/store.js +219 -123
- package/dist/prod/affects-marks.js +86 -0
- package/dist/prod/affects.js +33 -79
- package/dist/prod/boundaries.js +185 -149
- package/dist/prod/core/async.js +176 -135
- package/dist/prod/core/constants.js +23 -4
- package/dist/prod/core/context.js +3 -3
- package/dist/prod/core/core.js +521 -353
- package/dist/prod/core/effect.js +66 -66
- package/dist/prod/core/error.js +5 -3
- package/dist/prod/core/external.js +5 -6
- package/dist/prod/core/graph.js +56 -42
- package/dist/prod/core/heap.js +38 -38
- package/dist/prod/core/identities.js +62 -0
- package/dist/prod/core/lanes.js +237 -224
- package/dist/prod/core/owner.js +62 -51
- package/dist/prod/core/scheduler.js +312 -158
- package/dist/prod/core/truth.js +45 -0
- package/dist/prod/core/verdict.js +113 -63
- package/dist/prod/deferred.js +182 -0
- package/dist/prod/index.js +7 -1
- package/dist/prod/map.js +366 -281
- package/dist/prod/reveal.js +77 -77
- package/dist/prod/signals.js +59 -38
- package/dist/prod/store/affects.js +76 -69
- package/dist/prod/store/edges.js +39 -0
- package/dist/prod/store/hooks.js +13 -0
- package/dist/prod/store/index.js +0 -2
- package/dist/prod/store/optimistic.js +121 -84
- package/dist/prod/store/projection.js +46 -34
- package/dist/prod/store/reconcile.js +32 -30
- package/dist/prod/store/store.js +205 -116
- package/dist/types/affects-marks.d.ts +14 -0
- package/dist/types/boundaries.d.ts +5 -1
- package/dist/types/core/async.d.ts +10 -0
- package/dist/types/core/attribution.d.ts +6 -2
- package/dist/types/core/constants.d.ts +18 -1
- package/dist/types/core/core.d.ts +77 -5
- package/dist/types/core/dev.d.ts +43 -13
- package/dist/types/core/graph.d.ts +11 -0
- package/dist/types/core/identities.d.ts +17 -0
- package/dist/types/core/index.d.ts +2 -1
- package/dist/types/core/lanes.d.ts +2 -4
- package/dist/types/core/owner.d.ts +5 -3
- package/dist/types/core/scheduler.d.ts +59 -34
- package/dist/types/core/truth.d.ts +16 -0
- package/dist/types/core/types.d.ts +21 -6
- package/dist/types/core/verdict.d.ts +32 -0
- package/dist/types/deferred.d.ts +28 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/map.d.ts +2 -2
- package/dist/types/signals.d.ts +28 -7
- package/dist/types/store/affects.d.ts +11 -0
- package/dist/types/store/edges.d.ts +9 -0
- package/dist/types/store/hooks.d.ts +48 -0
- package/dist/types/store/index.d.ts +10 -4
- package/dist/types/store/optimistic.d.ts +3 -2
- package/dist/types/store/projection.d.ts +4 -3
- package/dist/types/store/store.d.ts +4 -39
- package/dist/types/store/target.d.ts +16 -8
- package/dist/types/store/types.d.ts +14 -0
- package/package.json +1 -1
package/dist/prod/store/store.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { CONFIG_MANUAL_WRITE, CONFIG_OVERRIDE, CONFIG_HELD, $REFRESH, NOT_PENDING, STATUS_PENDING, STATUS_ERROR, EFFECT_RENDER, REACTIVE_RECOMPUTING_DEPS, REACTIVE_STAGED_READ, CONFIG_VERDICT, CONFIG_CHILDREN_FORBIDDEN, CONFIG_PROMOTED, REACTIVE_DIRTY, REACTIVE_CHECK } from "../core/constants.js";
|
|
2
2
|
|
|
3
|
-
import { setSignal, isEqual, read, verdict, context, notePromotedWrite, untrack, slotSignal, stagedScreen, stagedRead } from "../core/core.js";
|
|
3
|
+
import { writeHook, setSignal, isEqual, read, verdict, snapshotCaptureActive, captureWriteSnapshot, context, notePromotedWrite, untrack, slotSignal, stagedScreen, stagedRead } from "../core/core.js";
|
|
4
|
+
|
|
5
|
+
import { signalHook } from "../core/identities.js";
|
|
4
6
|
|
|
5
7
|
import { setSlotUnobserved } from "../core/graph.js";
|
|
6
8
|
|
|
@@ -8,10 +10,12 @@ import { getObserver } from "../core/owner.js";
|
|
|
8
10
|
|
|
9
11
|
import { insertIntoHeap } from "../core/heap.js";
|
|
10
12
|
|
|
11
|
-
import { txOf, flushTransaction, holdNode, joinFuture, insertSubs, schedule, passLane, globalQueue, queuePendingNode, GlobalQueue, joinPassTx, dirtyQueue } from "../core/scheduler.js";
|
|
13
|
+
import { txOf, flushTransaction, holdNode, joinFuture, insertSubs, schedule, passLane, globalQueue, queuePendingNode, GlobalQueue, installStoreHooks, joinPassTx, dirtyQueue } from "../core/scheduler.js";
|
|
12
14
|
|
|
13
15
|
import { lookupTarget, setLookup, $OWNER, isOwned, markDescendants, devAssertNeverUserMutation } from "./target.js";
|
|
14
16
|
|
|
17
|
+
import { optHooks, affectsHooks } from "./hooks.js";
|
|
18
|
+
|
|
15
19
|
import { $TARGET, rawValuesUsed, isRawValue, isWrappable, markRawIngest, writeOverride, markRawOne, $RECORD, $PROXY, $TRACK } from "./types.js";
|
|
16
20
|
|
|
17
21
|
export { markRaw } from "./types.js";
|
|
@@ -45,18 +49,7 @@ export { markRaw } from "./types.js";
|
|
|
45
49
|
* Perf contracts (plan sec. 31.4): lazy nodes; one literal per node; O(written) per
|
|
46
50
|
* setter (`wk`); overlay drafts for large plain records; no new named
|
|
47
51
|
* fields on array targets; zero allocation for adopted-but-unread objects.
|
|
48
|
-
*/
|
|
49
|
-
|
|
50
|
-
function installOptHooks(e) {
|
|
51
|
-
optHooks = e;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
let affectsHooks = null;
|
|
55
|
-
|
|
56
|
-
function installAffectsHooks(e) {
|
|
57
|
-
affectsHooks = e;
|
|
58
|
-
}
|
|
59
|
-
|
|
52
|
+
*/
|
|
60
53
|
/** Key of the deep-witness slot node (never a user key). */ const $DEEP = Symbol(0);
|
|
61
54
|
|
|
62
55
|
// ---------------------------------------------------------------------------
|
|
@@ -175,7 +168,7 @@ function wrap(e, n = null, t = null, r = n?.fam ?? null) {
|
|
|
175
168
|
// adoption swap raw identity without changing the logical value — only
|
|
176
169
|
// changed leaves notify, R9).
|
|
177
170
|
const slotNodeEquals = function(e, n) {
|
|
178
|
-
return isEqual(e, n) || sameLogicalSlot(this.
|
|
171
|
+
return isEqual(e, n) || sameLogicalSlot(this.O, e, n);
|
|
179
172
|
};
|
|
180
173
|
|
|
181
174
|
/** The deep witness: the store decides when it notifies. */ const never = false;
|
|
@@ -213,7 +206,7 @@ function sameKey(e, n) {
|
|
|
213
206
|
/** The container node's comparator: a staging is a backing (identity — a
|
|
214
207
|
* pending backing is never "equal" to the committed one); a lane value is
|
|
215
208
|
* an arrangement (`LaneView`), judged by the optimistic machinery. */ const containerEquals = function(e, n) {
|
|
216
|
-
return e === n || optHooks !== null && optHooks.arrangement(this.
|
|
209
|
+
return e === n || optHooks !== null && optHooks.arrangement(this.O, e, n);
|
|
217
210
|
};
|
|
218
211
|
|
|
219
212
|
function sameLogicalSlot(e, n, t) {
|
|
@@ -226,8 +219,8 @@ function sameLogicalSlot(e, n, t) {
|
|
|
226
219
|
* released at the commit that resolves it (`drainFolds`). */ const deferredReleases = new Set;
|
|
227
220
|
|
|
228
221
|
function releaseSlot(e) {
|
|
229
|
-
const n = e.
|
|
230
|
-
const t = e.
|
|
222
|
+
const n = e.O;
|
|
223
|
+
const t = e.ze;
|
|
231
224
|
if (t === $TRACK) {
|
|
232
225
|
if (n.k === e) n.k = null;
|
|
233
226
|
} else if (t === $DEEP) {
|
|
@@ -243,17 +236,21 @@ function releaseSlot(e) {
|
|
|
243
236
|
/** A node was born on `target`: a family target enters the live index. */ function noteNode(e, n, t) {
|
|
244
237
|
if (e.fam !== null) e.fam.live.add(e);
|
|
245
238
|
markDescendants(e);
|
|
246
|
-
if (affectsHooks !== null) affectsHooks.
|
|
239
|
+
if (affectsHooks !== null) affectsHooks.fo(e, n, t);
|
|
247
240
|
}
|
|
248
241
|
|
|
249
242
|
// Shared slot-node release handler: registered once; the core sweep
|
|
250
243
|
// dispatches CONFIG_SLOT_NODE nodes here (graph.ts) instead of holding a
|
|
251
244
|
// per-node closure in a per-node NodeExtension. A dropped node is
|
|
252
245
|
// unreachable through the store — a fresh read makes a fresh node.
|
|
246
|
+
// A staged write — or a lane's value (a guess, S4) — is state only the node
|
|
247
|
+
// holds: its release waits for the commit that resolves it. So does a leaf of
|
|
248
|
+
// a held container (#4146): a mainline write it carried is in the held
|
|
249
|
+
// staging too, where it would read as the hold's until the landing.
|
|
250
|
+
const slotKept = e => e.se !== NOT_PENDING || e.l & CONFIG_OVERRIDE || e.O.k?.l & CONFIG_HELD;
|
|
251
|
+
|
|
253
252
|
setSlotUnobserved(e => {
|
|
254
|
-
|
|
255
|
-
// node holds: defer the release to the commit that resolves it.
|
|
256
|
-
if (e.Ae !== NOT_PENDING || e.u & CONFIG_OVERRIDE) deferredReleases.add(e); else releaseSlot(e);
|
|
253
|
+
if (slotKept(e)) deferredReleases.add(e); else releaseSlot(e);
|
|
257
254
|
});
|
|
258
255
|
|
|
259
256
|
/** A node born while its container has a staging is born from the two
|
|
@@ -261,19 +258,19 @@ setSlotUnobserved(e => {
|
|
|
261
258
|
* pending one — and held by the container's transaction when the container
|
|
262
259
|
* is held (#3706: the unit of the hold is the key — a key the batch left
|
|
263
260
|
* unchanged stages nothing and holds no one). */ function bornStaged(e, n, t, r) {
|
|
264
|
-
if (n.
|
|
261
|
+
if (n.xe && n.xe(n.Ie, t)) return;
|
|
265
262
|
queuePendingNode(n);
|
|
266
|
-
n.
|
|
263
|
+
n.se = t;
|
|
267
264
|
const i = e.k;
|
|
268
265
|
if (i === null) return;
|
|
269
266
|
// a node-less staging holds nothing (`stageOn`)
|
|
270
267
|
// Held by the container's transaction only for a key the HELD staging
|
|
271
268
|
// changed — a mainline layer's change above it stages mainline. (A
|
|
272
269
|
// container carrying a guess is a lane's: the flush's park decides.)
|
|
273
|
-
if ((i.
|
|
270
|
+
if ((i.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && heldKeyChanged(e, r)) holdNode(n, txOf(i));
|
|
274
271
|
// A28 (4): a batch written inside a creation-time pass is promoted — its
|
|
275
272
|
// late-materialized leaf with it.
|
|
276
|
-
if (i.
|
|
273
|
+
if (i.l & CONFIG_PROMOTED) notePromotedWrite(n);
|
|
277
274
|
}
|
|
278
275
|
|
|
279
276
|
function getNode(e, n,
|
|
@@ -323,8 +320,8 @@ function getHasNode(e, n) {
|
|
|
323
320
|
// pending backing staged — so whoever asked for it reads the frame it
|
|
324
321
|
// would have read had the node been there from the write.
|
|
325
322
|
if (e.pb !== null && !directCommit) {
|
|
326
|
-
n.
|
|
327
|
-
n.
|
|
323
|
+
n.Ie = preBatch(e);
|
|
324
|
+
n.se = e.pb;
|
|
328
325
|
queuePendingNode(n);
|
|
329
326
|
}
|
|
330
327
|
}
|
|
@@ -340,11 +337,11 @@ function getHasNode(e, n) {
|
|
|
340
337
|
* staging: nothing allocates, nothing is swept at the commit (dbmon's tick
|
|
341
338
|
* re-created, queued, swept and dropped ~7000 container nodes). If the
|
|
342
339
|
* flush parks, the seam materializes and holds the node then
|
|
343
|
-
* (`
|
|
344
|
-
if (e.k === null && passLane === null && (context === null || globalQueue.
|
|
340
|
+
* (`storeParkHook`) — the one case the staging home exists for. */ function stageOn(e, n) {
|
|
341
|
+
if (e.k === null && passLane === null && (context === null || globalQueue.q) && !deriveHolds(e)) return;
|
|
345
342
|
const t = getContainerNode(e);
|
|
346
|
-
if (t.
|
|
347
|
-
t.
|
|
343
|
+
if (t.se === NOT_PENDING) queuePendingNode(t);
|
|
344
|
+
t.se = n;
|
|
348
345
|
// A28 (4): a write issued inside a pass outside a flush (a creation-time
|
|
349
346
|
// derive) is promoted at the pass's end — the batch with it.
|
|
350
347
|
if (context !== null) notePromotedWrite(t);
|
|
@@ -354,10 +351,10 @@ function getHasNode(e, n) {
|
|
|
354
351
|
/** The projection's derive is held by a transaction and this is its own
|
|
355
352
|
* write (not a user setter's): the staging is the derive's (`holdWithDerive`). */ function deriveHolds(e) {
|
|
356
353
|
const n = e.fam?.node;
|
|
357
|
-
return n != null && !userWrite && (n.
|
|
354
|
+
return n != null && !userWrite && (n.l & CONFIG_HELD) !== 0;
|
|
358
355
|
}
|
|
359
356
|
|
|
360
|
-
|
|
357
|
+
const storePark = e => {
|
|
361
358
|
for (let n = 0; n < foldList.length; n++) {
|
|
362
359
|
const t = foldList[n];
|
|
363
360
|
if (t.k === null && t.pb !== null) holdNode(getContainerNode(t), e);
|
|
@@ -385,8 +382,9 @@ function bumpDeep(e) {
|
|
|
385
382
|
// on the guess (`laneWrite` → `supersede`, judged by `containerEquals`);
|
|
386
383
|
// otherwise the structural readers are walked (the staging is already on
|
|
387
384
|
// the node; the comparator is the store's, done).
|
|
388
|
-
if (e.
|
|
389
|
-
if (e.
|
|
385
|
+
if (e.l & CONFIG_OVERRIDE) setSignal(e, n); else {
|
|
386
|
+
if (e.l & CONFIG_HELD) joinFuture(txOf(e));
|
|
387
|
+
if (snapshotCaptureActive) captureWriteSnapshot(e, e.Ie);
|
|
390
388
|
insertSubs(e);
|
|
391
389
|
schedule();
|
|
392
390
|
}
|
|
@@ -478,7 +476,7 @@ const plainProto = e => {
|
|
|
478
476
|
if (e.kc > OVERLAY_REBUILD_MAX_KEYS) return false;
|
|
479
477
|
if (e.del !== null && e.del.size !== 0) return true;
|
|
480
478
|
const t = e.wk;
|
|
481
|
-
if (t !== null && t !== WK_ALL && t.size <= OVERLAY_REBUILD_MIN_ADDS) return false;
|
|
479
|
+
if (t !== null && t !== WK_ALL && t.u === undefined && t.size <= OVERLAY_REBUILD_MIN_ADDS) return false;
|
|
482
480
|
const r = e.v;
|
|
483
481
|
let i = 0;
|
|
484
482
|
for (const e of Reflect.ownKeys(n)) if (!hasOwn.call(r, e) && ++i > OVERLAY_REBUILD_MIN_ADDS) return true;
|
|
@@ -498,7 +496,7 @@ const plainProto = e => {
|
|
|
498
496
|
e.pb = t;
|
|
499
497
|
e.ovl = false;
|
|
500
498
|
// The staging IS the pending backing: re-point the container node.
|
|
501
|
-
if (e.k !== null && e.k.
|
|
499
|
+
if (e.k !== null && e.k.se === n) e.k.se = t;
|
|
502
500
|
return t;
|
|
503
501
|
}
|
|
504
502
|
|
|
@@ -519,7 +517,7 @@ const plainProto = e => {
|
|
|
519
517
|
// mainline setter mid-hold publishes mainline; the held keys stay held).
|
|
520
518
|
n = e.pb = cloneRaw(n, e);
|
|
521
519
|
const t = e.k;
|
|
522
|
-
if (t !== null && !(t.
|
|
520
|
+
if (t !== null && !(t.l & CONFIG_HELD)) t.se = n;
|
|
523
521
|
} else if (n === null) {
|
|
524
522
|
const t = e.v;
|
|
525
523
|
if (!directCommit) queueFold(e);
|
|
@@ -598,7 +596,7 @@ const plainProto = e => {
|
|
|
598
596
|
} else r = e.v;
|
|
599
597
|
if (directCommit) {
|
|
600
598
|
e.pb = null;
|
|
601
|
-
if (e.k !== null) e.k.
|
|
599
|
+
if (e.k !== null) e.k.Ie = n;
|
|
602
600
|
} else {
|
|
603
601
|
// Eager (INTERNALS §3): the backing swaps now — handlers read the
|
|
604
602
|
// adopted object before the flush — while the container node, when
|
|
@@ -694,7 +692,7 @@ let foldIndex = null;
|
|
|
694
692
|
if (r.pb === null) continue;
|
|
695
693
|
// folded already (a direct commit)
|
|
696
694
|
const i = r.k;
|
|
697
|
-
if (i !== null && i.
|
|
695
|
+
if (i !== null && i.se !== NOT_PENDING) {
|
|
698
696
|
// held: commits with its hold
|
|
699
697
|
foldList.push(r);
|
|
700
698
|
foldOlds.push(n[t]);
|
|
@@ -705,7 +703,7 @@ let foldIndex = null;
|
|
|
705
703
|
}
|
|
706
704
|
if (deferredReleases.size !== 0) {
|
|
707
705
|
for (const e of deferredReleases) {
|
|
708
|
-
if (e.
|
|
706
|
+
if (e.o === null && !slotKept(e)) {
|
|
709
707
|
deferredReleases.delete(e);
|
|
710
708
|
releaseSlot(e);
|
|
711
709
|
}
|
|
@@ -716,28 +714,33 @@ let foldIndex = null;
|
|
|
716
714
|
/** Core asks: is a slot node's truth in flight? Its family's derive is —
|
|
717
715
|
* lanes (A17, #2864: a guess mid-refetch stands in for the flight) and
|
|
718
716
|
* marks (an ambient `affects()` over a declared reload lives to the
|
|
719
|
-
* landing) read it. */ GlobalQueue.
|
|
720
|
-
const n = e.
|
|
721
|
-
return n != null && n.node !== null && (n.node.
|
|
717
|
+
* landing) read it. */ GlobalQueue.k = e => {
|
|
718
|
+
const n = e.O?.fam;
|
|
719
|
+
return n != null && n.node !== null && (n.node.m & STATUS_PENDING) !== 0;
|
|
722
720
|
};
|
|
723
721
|
|
|
724
722
|
/** Core asks (lanes.ts `covered`): the value a slot node's guess covered.
|
|
725
723
|
* A chained link's committed value is the inner store's at the last
|
|
726
724
|
* read-through — refreshed here, since a guess served the reads since
|
|
727
|
-
* (#3672: the revert compares against the base's live value). */ GlobalQueue.
|
|
728
|
-
const n = e.
|
|
725
|
+
* (#3672: the revert compares against the base's live value). */ GlobalQueue.fn = e => {
|
|
726
|
+
const n = e.O;
|
|
729
727
|
if (n.ch) {
|
|
730
|
-
const t = e.
|
|
728
|
+
const t = e.ze;
|
|
731
729
|
const r = n.v;
|
|
732
|
-
if (n.h !== null && n.h[t] === e) e.
|
|
730
|
+
if (n.h !== null && n.h[t] === e) e.Ie = untrack(() => t in r); else if (n.n !== null && n.n[t] === e) e.Ie = untrack(() => unwrapValue(r[t]));
|
|
733
731
|
}
|
|
734
|
-
return e.
|
|
732
|
+
return e.Ie;
|
|
735
733
|
};
|
|
736
734
|
|
|
737
|
-
|
|
735
|
+
installStoreHooks(storePark, () => {
|
|
738
736
|
drainFolds();
|
|
739
737
|
optHooks?.sweep();
|
|
740
|
-
};
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
/** The store half of `affects()` (store/affects.ts) makes its marks' nodes
|
|
741
|
+
* and walks records through these: it never imports the engine. */ GlobalQueue.no = getNode;
|
|
742
|
+
|
|
743
|
+
GlobalQueue.so = isWrappable;
|
|
741
744
|
|
|
742
745
|
/** A projection's creation run commits directly (a memo's first value is
|
|
743
746
|
* its `_value`, not a staging): the draft still writes a clone of the seed,
|
|
@@ -765,7 +768,7 @@ function runDirect(e) {
|
|
|
765
768
|
// the adopted object (shared ownership — never cloned).
|
|
766
769
|
e.pb = null;
|
|
767
770
|
e.wk = null;
|
|
768
|
-
} else if (e.ovl && (e.v !== n || !overlayRebuilds(e, r))) {
|
|
771
|
+
} else if (e.ovl && (e.v !== n || !overlayRebuilds(e, r) && t?.T?.Pe !== n)) {
|
|
769
772
|
// Overlay flatten (#3044): the backing keeps its identity, so the
|
|
770
773
|
// `t.v === old` gate below skips path copying (the parent slot
|
|
771
774
|
// already points here) and the adopted-notify (setter
|
|
@@ -779,8 +782,8 @@ function runDirect(e) {
|
|
|
779
782
|
const t = e.v;
|
|
780
783
|
const i = e.wk;
|
|
781
784
|
if (i !== null && i !== WK_ALL) {
|
|
782
|
-
for (
|
|
783
|
-
if (hasOwn.call(r,
|
|
785
|
+
for (let e = i; e !== undefined; e = e.u) for (const n of e) {
|
|
786
|
+
if (hasOwn.call(r, n)) copyOwn(t, r, n); else delete t[n];
|
|
784
787
|
}
|
|
785
788
|
} else {
|
|
786
789
|
for (const e of Reflect.ownKeys(r)) {
|
|
@@ -807,7 +810,7 @@ function runDirect(e) {
|
|
|
807
810
|
// subscribes to was a transient staging home (INTERNALS §5: an
|
|
808
811
|
// observer-less write holds a transient record until the fold,
|
|
809
812
|
// discarded with it) — gone with the batch.
|
|
810
|
-
if (t.
|
|
813
|
+
if (t.o === null) e.k = null; else if (t.Ie instanceof LaneView) t.Ie.base = e.v; else t.Ie = e.v;
|
|
811
814
|
}
|
|
812
815
|
if (e.v !== n && e.u) {
|
|
813
816
|
const t = parentSlotKey(e, n);
|
|
@@ -834,9 +837,9 @@ function runDirect(e) {
|
|
|
834
837
|
|
|
835
838
|
function heldDerivation(e, n, t) {
|
|
836
839
|
if (e.fam === null || !userWrite) return false;
|
|
837
|
-
if (n !== undefined) return (n.
|
|
840
|
+
if (n !== undefined) return (n.l & (CONFIG_HELD | CONFIG_MANUAL_WRITE)) === CONFIG_HELD && txOf(n) !== flushTransaction;
|
|
838
841
|
const r = e.k;
|
|
839
|
-
return r !== null && (r.
|
|
842
|
+
return r !== null && (r.l & CONFIG_HELD) !== 0 && txOf(r) !== flushTransaction && heldKeyChanged(e, t);
|
|
840
843
|
}
|
|
841
844
|
|
|
842
845
|
/** Setter exit: one node write per changed observed key (the leaves stage
|
|
@@ -866,7 +869,10 @@ function heldDerivation(e, n, t) {
|
|
|
866
869
|
// value can change when ANY key is written), or a non-plain prototype
|
|
867
870
|
// (class instances: prototype getters derive from arbitrary fields).
|
|
868
871
|
// Overlay pbs chain to the COMMITTED object (#3044) — plainness is the
|
|
869
|
-
// committed container's prototype, not the overlay's.
|
|
872
|
+
// committed container's prototype, not the overlay's. THIS setter's keys
|
|
873
|
+
// only (D-8, #3743): a key an earlier setter of the batch wrote and this
|
|
874
|
+
// one did not is no second proposal — its node was told then, and telling
|
|
875
|
+
// a held node its unchanged value again would join this tick to the hold.
|
|
870
876
|
const i = e.wk;
|
|
871
877
|
const o = i === WK_ALL || e.a === true || !plainProto(e.ovl ? e.v : n) ? null : i;
|
|
872
878
|
if (e.fam !== null && !heldDerivationHit && o !== null) for (const n of o) if (heldDerivation(e, r?.[n], n)) {
|
|
@@ -881,15 +887,18 @@ function heldDerivation(e, n, t) {
|
|
|
881
887
|
if (i.acc === true || hasOwn.call(n, o) && lookupGetter.call(n, o) !== undefined) {
|
|
882
888
|
// Accessor keys: the node is linked for shape-change notification,
|
|
883
889
|
// its value is never served (the getter runs with the proxy
|
|
884
|
-
// receiver on read) — FORCE wakes the readers.
|
|
890
|
+
// receiver on read) — FORCE wakes the readers. A data descriptor
|
|
891
|
+
// falls through so the cleared-acc hot read serves it (#3949);
|
|
892
|
+
// an own getter's descriptor has no `value`, so the compare below
|
|
893
|
+
// would otherwise keep the getter's result (or FORCE) for `undefined`.
|
|
885
894
|
i.acc = isOwnAccessor(n, o);
|
|
886
895
|
const e = Object.getOwnPropertyDescriptor(t, o);
|
|
887
896
|
const r = Object.getOwnPropertyDescriptor(n, o);
|
|
888
|
-
if (
|
|
897
|
+
if (!r || r.get || r.set) {
|
|
889
898
|
if (e?.get !== r?.get || e?.set !== r?.set || e?.value !== r?.value) setSignal(i, () => FORCE);
|
|
890
899
|
continue;
|
|
891
900
|
}
|
|
892
|
-
if (!isEqual(e?.value, r
|
|
901
|
+
if (e?.get || e?.set || !isEqual(e?.value, r.value)) setSignal(i, () => r.value);
|
|
893
902
|
continue;
|
|
894
903
|
}
|
|
895
904
|
const l = e.del !== null && e.del.has(o) ? undefined : n[o];
|
|
@@ -898,9 +907,9 @@ function heldDerivation(e, n, t) {
|
|
|
898
907
|
// The derive's write is a derivation; a user setter's is a proposal
|
|
899
908
|
// (A34 (3)'s discriminator).
|
|
900
909
|
if (!userWrite) {
|
|
901
|
-
i.
|
|
910
|
+
i.l &= ~CONFIG_MANUAL_WRITE;
|
|
902
911
|
holdWithDerive(e, i);
|
|
903
|
-
} else i.
|
|
912
|
+
} else i.l |= CONFIG_MANUAL_WRITE;
|
|
904
913
|
}
|
|
905
914
|
}
|
|
906
915
|
}
|
|
@@ -931,7 +940,7 @@ function heldDerivation(e, n, t) {
|
|
|
931
940
|
// container carrying an arrangement guess is told whether or not anyone
|
|
932
941
|
// subscribes: the write is a landing on the guess (`notifyContainer`).
|
|
933
942
|
const u = e.k;
|
|
934
|
-
if (u !== null && (u.
|
|
943
|
+
if (u !== null && (u.o !== null || u.l & CONFIG_OVERRIDE)) {
|
|
935
944
|
let r;
|
|
936
945
|
if (e.ovl) {
|
|
937
946
|
r = e.del !== null && e.del.size !== 0;
|
|
@@ -980,13 +989,14 @@ function membershipChanged(e, n) {
|
|
|
980
989
|
e.acc = isOwnAccessor(r, n);
|
|
981
990
|
const i = Object.getOwnPropertyDescriptor(t, n);
|
|
982
991
|
const o = Object.getOwnPropertyDescriptor(r, n);
|
|
983
|
-
|
|
992
|
+
// Data descriptors fall through, as in notifyWrites (#3949).
|
|
993
|
+
if (!o || o.get || o.set) {
|
|
984
994
|
if (i?.get !== o?.get || i?.set !== o?.set || i?.value !== o?.value) setSignal(e, () => FORCE);
|
|
985
995
|
return;
|
|
986
996
|
}
|
|
987
997
|
const l = i?.value;
|
|
988
998
|
const u = o?.value;
|
|
989
|
-
if (!isEqual(l, u) && !targetsEqual(l, u)) setSignal(e, typeof u === "function" ? () => u : u);
|
|
999
|
+
if (i?.get || i?.set || !isEqual(l, u) && !targetsEqual(l, u)) setSignal(e, typeof u === "function" ? () => u : u);
|
|
990
1000
|
} else {
|
|
991
1001
|
const i = t[n];
|
|
992
1002
|
const o = r[n];
|
|
@@ -1032,7 +1042,7 @@ function hasAccessorFlag(e) {
|
|
|
1032
1042
|
for (const i of Reflect.ownKeys(r)) if (e || i in n !== i in t) setSignal(r[i], i in t);
|
|
1033
1043
|
}
|
|
1034
1044
|
const i = e.k;
|
|
1035
|
-
if (i !== null && (i.
|
|
1045
|
+
if (i !== null && (i.o !== null || i.l & CONFIG_OVERRIDE)) {
|
|
1036
1046
|
const e = Array.isArray(t) && Array.isArray(n) ? arrayStructureChanged(n, t) : membershipChanged(n, t);
|
|
1037
1047
|
if (e) notifyContainer(i, t);
|
|
1038
1048
|
}
|
|
@@ -1089,7 +1099,7 @@ function inDraft(e) {
|
|
|
1089
1099
|
* "adoption is eager by contract" — and the node keeps the frame committed
|
|
1090
1100
|
* readers under a hold still see), `t.v` otherwise. */ function committed(e) {
|
|
1091
1101
|
const n = e.k;
|
|
1092
|
-
if (n !== null) return n.
|
|
1102
|
+
if (n !== null) return n.se !== NOT_PENDING ? asBacking(n.Ie) : e.v;
|
|
1093
1103
|
// A node-less staging (`stageOn`): the fold queue remembers the frame.
|
|
1094
1104
|
return e.pb !== null ? preBatch(e) : e.v;
|
|
1095
1105
|
}
|
|
@@ -1101,8 +1111,8 @@ function inDraft(e) {
|
|
|
1101
1111
|
* committed frame. */ function stagingReader() {
|
|
1102
1112
|
let e = context;
|
|
1103
1113
|
if (e === null) return null;
|
|
1104
|
-
if (e.
|
|
1105
|
-
return e === null || e.
|
|
1114
|
+
if (e.je) e = e.Ye ?? null;
|
|
1115
|
+
return e === null || e.l & CONFIG_CHILDREN_FORBIDDEN ? null : e;
|
|
1106
1116
|
}
|
|
1107
1117
|
|
|
1108
1118
|
/** The backing a read serves (RUL-1, signal parity): the draft and the write
|
|
@@ -1140,10 +1150,10 @@ function inDraft(e) {
|
|
|
1140
1150
|
if (writeOverride && !userWrite) {
|
|
1141
1151
|
const t = n !== undefined ? e.n?.[n] : undefined;
|
|
1142
1152
|
if (t !== undefined) {
|
|
1143
|
-
if ((t.
|
|
1153
|
+
if ((t.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD) joinPassTx(txOf(t));
|
|
1144
1154
|
} else {
|
|
1145
1155
|
const t = e.k;
|
|
1146
|
-
if (t !== null && (t.
|
|
1156
|
+
if (t !== null && (t.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && (n === undefined || heldKeyChanged(e, n))) joinPassTx(txOf(t));
|
|
1147
1157
|
}
|
|
1148
1158
|
}
|
|
1149
1159
|
return r;
|
|
@@ -1155,9 +1165,9 @@ function inDraft(e) {
|
|
|
1155
1165
|
// it).
|
|
1156
1166
|
if (n !== undefined) {
|
|
1157
1167
|
const t = e.n?.[n];
|
|
1158
|
-
if (t !== undefined && t.
|
|
1168
|
+
if (t !== undefined && t.l & CONFIG_OVERRIDE) return committed(e);
|
|
1159
1169
|
}
|
|
1160
|
-
const o = i !== null && (i.
|
|
1170
|
+
const o = i !== null && (i.l & CONFIG_HELD) !== 0;
|
|
1161
1171
|
// A held container: the hold's unit is the key (#3706). A key the HELD
|
|
1162
1172
|
// staging left unchanged reads the ambient view — a mainline layer's
|
|
1163
1173
|
// write above the hold publishes mainline (#3688, #3612); a key it
|
|
@@ -1175,7 +1185,7 @@ function inDraft(e) {
|
|
|
1175
1185
|
// probe) judges the container like any node — for a key the batch
|
|
1176
1186
|
// changed; an unchanged key is committed and final (A22: pending is per
|
|
1177
1187
|
// key). A held container's committed frame is the node's.
|
|
1178
|
-
if (verdict === null || !o && n !== undefined && !keyChanged(e, n, t)) return o ? asBacking(i.
|
|
1188
|
+
if (verdict === null || !o && n !== undefined && !keyChanged(e, n, t)) return o ? asBacking(i.Ie) : e.v;
|
|
1179
1189
|
return i === null ? committed(e) : asBacking(read(i));
|
|
1180
1190
|
}
|
|
1181
1191
|
// A pass reading a key the batch left unchanged: the same value in both
|
|
@@ -1192,13 +1202,13 @@ function inDraft(e) {
|
|
|
1192
1202
|
// frames, a changed one is what the mark already stands for. The
|
|
1193
1203
|
// per-key question is asked once per pass, not once per key (a
|
|
1194
1204
|
// `mapArray` over 1k shifted rows asked it 1k times).
|
|
1195
|
-
if (l.R & REACTIVE_STAGED_READ && !(l.
|
|
1205
|
+
if (l.R & REACTIVE_STAGED_READ && !(l.l & (CONFIG_VERDICT | CONFIG_CHILDREN_FORBIDDEN))) return r;
|
|
1196
1206
|
if (n !== undefined && !keyChanged(e, n, t)) return committed(e);
|
|
1197
1207
|
// A node-less staging (`stageOn`): what `read()` of its node would do
|
|
1198
1208
|
// for this pass — a verdict reader sees the screen, any other derives
|
|
1199
1209
|
// from the staging and is marked as having read it.
|
|
1200
1210
|
if (i === null) {
|
|
1201
|
-
if (l.
|
|
1211
|
+
if (l.l & CONFIG_VERDICT && stagedScreen(l)) return committed(e);
|
|
1202
1212
|
stagedRead(l);
|
|
1203
1213
|
return r;
|
|
1204
1214
|
}
|
|
@@ -1208,11 +1218,52 @@ function inDraft(e) {
|
|
|
1208
1218
|
|
|
1209
1219
|
/** Did the HELD staging (the container node's, not a mainline layer above
|
|
1210
1220
|
* it) change `key`? */ function heldKeyChanged(e, n, t = true) {
|
|
1211
|
-
return changedBetween(e, e.k.
|
|
1221
|
+
return changedBetween(e, e.k.Ie, e.k.se, n, t) && !adoptChild(e, n);
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
/** The values of a held frame, built once per frame: neither the committed
|
|
1225
|
+
* frame nor the held staging changes while the hold stands (a draft writes a
|
|
1226
|
+
* clone), so the set stays true for every first read of the hold. */ const frameRaws = new WeakMap;
|
|
1227
|
+
|
|
1228
|
+
function rawsOf(e) {
|
|
1229
|
+
let n = frameRaws.get(e);
|
|
1230
|
+
if (n === undefined) frameRaws.set(e, n = new Set(Object.values(e)));
|
|
1231
|
+
return n;
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
/** The same row at a slot, as the adoption pairs rows (reconcile.ts): two
|
|
1235
|
+
* objects whose keys agree — positionally for an unkeyed family. */ function sameRow(e, n, t) {
|
|
1236
|
+
return e !== null && typeof e === "object" && n !== null && typeof n === "object" && (t === null || sameKey(t(e), t(n)));
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
/** A key of a held adoption whose old and new values are the same row that
|
|
1240
|
+
* nothing had proxied when the adoption ran, so `descend` never paired
|
|
1241
|
+
* them: paired now, as it would have been (#3712) — one target, the old
|
|
1242
|
+
* raw its committed frame, the new raw its staging, held by the same
|
|
1243
|
+
* transaction. Both raws resolve to it, so the slot reads as unchanged and
|
|
1244
|
+
* the row's own leaves decide: `id` the same in both frames holds no one,
|
|
1245
|
+
* `version` holds. A record's keys pair by name; an array's by the family's
|
|
1246
|
+
* key — without a family the pairing is not known here and the hold stands. */ function adoptChild(e, n) {
|
|
1247
|
+
const t = e.k;
|
|
1248
|
+
const r = asBacking(t.Ie);
|
|
1249
|
+
const i = r[n];
|
|
1250
|
+
const o = t.se[n];
|
|
1251
|
+
const l = Array.isArray(r) ? e.fam?.key : null;
|
|
1252
|
+
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
|
+
// A raw in both frames (the same objects swapped or reordered) is its
|
|
1254
|
+
// own row: pairing it with another would alias the two after the commit.
|
|
1255
|
+
rawsOf(r).has(o) || rawsOf(t.se).has(i)) return false;
|
|
1256
|
+
// A raw-marked value wraps to itself, a proxy to its own target: neither
|
|
1257
|
+
// is a fresh target around `ov`.
|
|
1258
|
+
const u = wrap(i, e, n)[$TARGET];
|
|
1259
|
+
if (u === undefined || u.v !== i) return false;
|
|
1260
|
+
adoptPB(u, o, false);
|
|
1261
|
+
holdNode(getContainerNode(u), txOf(t));
|
|
1262
|
+
return true;
|
|
1212
1263
|
}
|
|
1213
1264
|
|
|
1214
1265
|
/** A family target's unheld staging, read with no pass (a handler). */ function familyAhead(e) {
|
|
1215
|
-
return e.fam !== null && context === null && !(e.k !== null && e.k.
|
|
1266
|
+
return e.fam !== null && context === null && !(e.k !== null && e.k.l & CONFIG_HELD);
|
|
1216
1267
|
}
|
|
1217
1268
|
|
|
1218
1269
|
/** Did this batch change `key` — value (slot equality), presence, and (for
|
|
@@ -1246,7 +1297,7 @@ function changedBetween(e, n, t, r, i) {
|
|
|
1246
1297
|
* tracked or not, applies the hold and A28 rules a signal's read does. A
|
|
1247
1298
|
* projection's unheld staging is ahead for a handler (`familyAhead`). */ function nodeValue(e, n, t) {
|
|
1248
1299
|
let r = read(e);
|
|
1249
|
-
if (e.
|
|
1300
|
+
if (e.se !== NOT_PENDING && !(e.l & CONFIG_HELD) && context === null && t.fam !== null) r = e.se;
|
|
1250
1301
|
return r === FORCE ? n : r;
|
|
1251
1302
|
}
|
|
1252
1303
|
|
|
@@ -1266,8 +1317,8 @@ function changedBetween(e, n, t, r, i) {
|
|
|
1266
1317
|
if (l) {
|
|
1267
1318
|
// The link's committed value follows the inner store's (what a lane
|
|
1268
1319
|
// over it covers — its reveal and revert compare against it, S4).
|
|
1269
|
-
i.
|
|
1270
|
-
if (i.
|
|
1320
|
+
i.Ie = t;
|
|
1321
|
+
if (i.l & CONFIG_OVERRIDE) u = n;
|
|
1271
1322
|
} else u = n;
|
|
1272
1323
|
}
|
|
1273
1324
|
}
|
|
@@ -1288,7 +1339,7 @@ function changedBetween(e, n, t, r, i) {
|
|
|
1288
1339
|
}
|
|
1289
1340
|
|
|
1290
1341
|
/** Is `n` among `c`'s dependencies (any pass's — a kept tail included)? */ function linkedTo(e, n) {
|
|
1291
|
-
for (let t = e.
|
|
1342
|
+
for (let t = e.C; t !== null; t = t.G) if (t.S === n) return true;
|
|
1292
1343
|
return false;
|
|
1293
1344
|
}
|
|
1294
1345
|
|
|
@@ -1307,7 +1358,7 @@ function changedBetween(e, n, t, r, i) {
|
|
|
1307
1358
|
// reader of a memo would — linked, so it re-runs at the landing (the
|
|
1308
1359
|
// next settled pass pulls without linking and the stale link trims), a
|
|
1309
1360
|
// verdict reader registered, a tracked pass suspended.
|
|
1310
|
-
if (n.
|
|
1361
|
+
if (n.m & (STATUS_PENDING | STATUS_ERROR)) {
|
|
1311
1362
|
// A render effect outside the flight's own flush is the frame, not a
|
|
1312
1363
|
// derivation: it keeps what it shows and learns of the landing from the
|
|
1313
1364
|
// leaves the landing changes (unchanged leaves say nothing — it is not a
|
|
@@ -1315,9 +1366,10 @@ function changedBetween(e, n, t, r, i) {
|
|
|
1315
1366
|
// One that already observes the flight (linked by the pass that saw it
|
|
1316
1367
|
// go up — the frame's hold, `blocked`) keeps observing it: a re-run for
|
|
1317
1368
|
// another reason (a guess it read, the seam) that dropped the link would
|
|
1318
|
-
// release the hold with the flight still up.
|
|
1369
|
+
// release the hold with the flight still up. An errored derive has no
|
|
1370
|
+
// landing to learn of: the error is what the frame shows.
|
|
1319
1371
|
const t = context;
|
|
1320
|
-
if (t !== null && t.i === EFFECT_RENDER && n.
|
|
1372
|
+
if (t !== null && t.i === EFFECT_RENDER && !(n.m & STATUS_ERROR) && n.l & CONFIG_HELD && flushTransaction !== txOf(n) && !linkedTo(t, n)) return;
|
|
1321
1373
|
// An optimistic family carrying guesses that stand in for the flight
|
|
1322
1374
|
// (A17): the guesses are the display — a reader is served the leaves
|
|
1323
1375
|
// (committed plus the guesses) and does not suspend on the derive; a
|
|
@@ -1325,7 +1377,7 @@ function changedBetween(e, n, t, r, i) {
|
|
|
1325
1377
|
// up). The family's, not the target's: the edit stands in for the one
|
|
1326
1378
|
// flight the family has.
|
|
1327
1379
|
const r = e.fam.overlaid;
|
|
1328
|
-
if (verdict === null && n.
|
|
1380
|
+
if (verdict === null && n.m & STATUS_PENDING && r !== undefined && r.size !== 0) return;
|
|
1329
1381
|
read(n);
|
|
1330
1382
|
} else untrack(() => read(n));
|
|
1331
1383
|
}
|
|
@@ -1338,12 +1390,12 @@ function changedBetween(e, n, t, r, i) {
|
|
|
1338
1390
|
* classification, as core reads it — `heldNotFinal`). */ function holdWithDerive(e, n) {
|
|
1339
1391
|
const t = e.fam?.node;
|
|
1340
1392
|
if (t !== undefined && t !== null && !userWrite && // the derive's own write (its draft or landing), not a user setter's
|
|
1341
|
-
t.
|
|
1393
|
+
t.l & CONFIG_HELD && n.se !== NOT_PENDING &&
|
|
1342
1394
|
// A node carrying a guess is the lane's: its staging is the truth beneath
|
|
1343
1395
|
// the guess (`setSignal` → `laneWrite`), never re-homed by the store.
|
|
1344
|
-
!(n.
|
|
1396
|
+
!(n.l & (CONFIG_HELD | CONFIG_OVERRIDE))) {
|
|
1345
1397
|
holdNode(n, txOf(t));
|
|
1346
|
-
n.
|
|
1398
|
+
n.T.Ve = t.T.Ve;
|
|
1347
1399
|
}
|
|
1348
1400
|
}
|
|
1349
1401
|
|
|
@@ -1386,6 +1438,22 @@ function changedBetween(e, n, t, r, i) {
|
|
|
1386
1438
|
|
|
1387
1439
|
// ---------------------------------------------------------------------------
|
|
1388
1440
|
// traps
|
|
1441
|
+
/** A trap write: the target is told at the setter's exit, the key recorded
|
|
1442
|
+
* in this setter's `wk`. A setter's first write to a target still staged by
|
|
1443
|
+
* an earlier setter of the batch opens a set of its own (the notify visits
|
|
1444
|
+
* it, D-8) and chains the earlier one behind for the fold's union. */ function touch(e, n) {
|
|
1445
|
+
let t = e.wk;
|
|
1446
|
+
if (!pendingNotify.has(e)) {
|
|
1447
|
+
pendingNotify.add(e);
|
|
1448
|
+
if (t !== null && t !== WK_ALL) {
|
|
1449
|
+
t = new Set;
|
|
1450
|
+
t.u = e.wk;
|
|
1451
|
+
e.wk = t;
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
if (t !== WK_ALL) (e.wk ??= new Set).add(n);
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1389
1457
|
const traps = {
|
|
1390
1458
|
get(e, n, t) {
|
|
1391
1459
|
// One typeof gates every brand-symbol compare off the hot string path.
|
|
@@ -1414,7 +1482,7 @@ const traps = {
|
|
|
1414
1482
|
}
|
|
1415
1483
|
// (The witness before the pull: a mark on an uninitialized derived
|
|
1416
1484
|
// store is witnessed, then the pull throws — loading, declared pending.)
|
|
1417
|
-
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.
|
|
1485
|
+
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.ro(e, n);
|
|
1418
1486
|
if (e.fam !== null) pullFamily(e);
|
|
1419
1487
|
// Hot inline case: existing PLAIN node (non-accessor) read outside any
|
|
1420
1488
|
// draft — the dbmon/uibench effect re-read shape. Core's `read()` serves
|
|
@@ -1429,7 +1497,7 @@ const traps = {
|
|
|
1429
1497
|
const r = e.n?.[n];
|
|
1430
1498
|
if (writeScopes === null && !writeOverride) {
|
|
1431
1499
|
const t = r;
|
|
1432
|
-
if (t !== undefined && t.acc !== true && (!e.ch || t.
|
|
1500
|
+
if (t !== undefined && t.acc !== true && (!e.ch || t.l & CONFIG_OVERRIDE)) {
|
|
1433
1501
|
const r = nodeValue(t, undefined, e);
|
|
1434
1502
|
if (r === null || typeof r !== "object") return r;
|
|
1435
1503
|
if (e.s) return serveShallow(e, n, r);
|
|
@@ -1511,7 +1579,7 @@ const traps = {
|
|
|
1511
1579
|
if (n === $OWNER || n === $RECORD) return false;
|
|
1512
1580
|
// (The witness before the pull: a mark on an uninitialized derived
|
|
1513
1581
|
// store is witnessed, then the pull throws — loading, declared pending.)
|
|
1514
|
-
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.
|
|
1582
|
+
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.ro(e, n);
|
|
1515
1583
|
if (e.fam !== null) pullFamily(e);
|
|
1516
1584
|
const t = readSource(e, n);
|
|
1517
1585
|
// A tracked reader's presence node answers (born from the two frames;
|
|
@@ -1524,9 +1592,9 @@ const traps = {
|
|
|
1524
1592
|
const i = read(r);
|
|
1525
1593
|
if (!e.ch || t !== e.v) return !!i;
|
|
1526
1594
|
const o = n in t;
|
|
1527
|
-
r.
|
|
1595
|
+
r.Ie = o;
|
|
1528
1596
|
// the link follows the inner store (S4)
|
|
1529
|
-
if (r.
|
|
1597
|
+
if (r.l & CONFIG_OVERRIDE) return !!i;
|
|
1530
1598
|
return o;
|
|
1531
1599
|
} else if (optRead(e)) {
|
|
1532
1600
|
const t = optHooks.has(e, n);
|
|
@@ -1536,14 +1604,14 @@ const traps = {
|
|
|
1536
1604
|
return n in t && !(e.del !== null && t === e.pb && e.del.has(n));
|
|
1537
1605
|
},
|
|
1538
1606
|
ownKeys(e) {
|
|
1539
|
-
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.
|
|
1607
|
+
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.ro(e, undefined);
|
|
1540
1608
|
if (e.fam !== null) pullFamily(e);
|
|
1541
1609
|
return visibleKeys(e, enumerationSource(e));
|
|
1542
1610
|
},
|
|
1543
1611
|
getOwnPropertyDescriptor(e, n) {
|
|
1544
1612
|
if (n === $OWNER || n === $RECORD) return undefined;
|
|
1545
1613
|
const t = getObserver();
|
|
1546
|
-
if (verdict !== null && affectsHooks !== null && t === null) affectsHooks.
|
|
1614
|
+
if (verdict !== null && affectsHooks !== null && t === null) affectsHooks.ro(e, n);
|
|
1547
1615
|
if (e.fam !== null) pullFamily(e);
|
|
1548
1616
|
// An enumerator (spread, Object.entries) already holds the container
|
|
1549
1617
|
// node and reads its frame; a descriptor read on its own tracks
|
|
@@ -1561,7 +1629,7 @@ const traps = {
|
|
|
1561
1629
|
const t = getHasNode(e, n);
|
|
1562
1630
|
const r = read(t);
|
|
1563
1631
|
o = readSource(e, n, true);
|
|
1564
|
-
if (!r && (!e.ch || t.
|
|
1632
|
+
if (!r && (!e.ch || t.l & CONFIG_OVERRIDE || !(n in o))) return undefined;
|
|
1565
1633
|
} else o = readSource(e, n, true);
|
|
1566
1634
|
let l;
|
|
1567
1635
|
if (!i && optRead(e)) {
|
|
@@ -1582,15 +1650,22 @@ const traps = {
|
|
|
1582
1650
|
// pollution guard (core R30)
|
|
1583
1651
|
const o = e.s ? t : unwrapValue(t);
|
|
1584
1652
|
const l = ensurePB(e);
|
|
1585
|
-
|
|
1653
|
+
// #4146 (R1: node existence is unobservable; #3706: the hold's unit is
|
|
1654
|
+
// the key): a user setter writing a key a HELD container's staging left
|
|
1655
|
+
// unchanged would write it into the held staging — read as the hold's
|
|
1656
|
+
// until its landing, while the same key with a leaf (a live reader)
|
|
1657
|
+
// publishes at the flush. The key gets its leaf first, born from the two
|
|
1658
|
+
// frames (`bornStaged`: a key the hold left unchanged — or added — is
|
|
1659
|
+
// unstaged and held by no one; a key the hold changed is born held, A34
|
|
1660
|
+
// (1)), and the write stages on it (A28; a write by the holder joins it,
|
|
1661
|
+
// A34) — as it would with a reader's leaf. Kept while the container is
|
|
1662
|
+
// held (`deferredReleases`).
|
|
1663
|
+
if (userWrite && e.k?.l & CONFIG_HELD) deferredReleases.add(getNode(e, n));
|
|
1586
1664
|
if (Array.isArray(l)) {
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
t.add(n);
|
|
1590
|
-
t.add("length");
|
|
1591
|
-
}
|
|
1665
|
+
touch(e, n);
|
|
1666
|
+
if (n === "length") e.wk = WK_ALL; else touch(e, "length");
|
|
1592
1667
|
} else {
|
|
1593
|
-
|
|
1668
|
+
touch(e, n);
|
|
1594
1669
|
if (!(n in l)) e.kc++;
|
|
1595
1670
|
}
|
|
1596
1671
|
if (UNSAFE_KEYS.has(n)) {
|
|
@@ -1628,8 +1703,7 @@ const traps = {
|
|
|
1628
1703
|
};
|
|
1629
1704
|
const o = ensurePB(e);
|
|
1630
1705
|
if (e.a || !(t.enumerable && t.writable && t.configurable)) e.sc = 1;
|
|
1631
|
-
|
|
1632
|
-
if (e.wk !== WK_ALL) (e.wk ??= new Set).add(n);
|
|
1706
|
+
touch(e, n);
|
|
1633
1707
|
Object.defineProperty(o, n, t);
|
|
1634
1708
|
if (e.del !== null) e.del.delete(n);
|
|
1635
1709
|
if (i) notifyWrites(e);
|
|
@@ -1640,10 +1714,22 @@ const traps = {
|
|
|
1640
1714
|
const r = !t && writeOverride;
|
|
1641
1715
|
if (!t && !r) return true;
|
|
1642
1716
|
const i = ensurePB(e);
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
delete
|
|
1646
|
-
|
|
1717
|
+
touch(e, n);
|
|
1718
|
+
// The key count (`kc`, the overlay gate and the rebuild cap) follows a
|
|
1719
|
+
// delete the way it follows an add: an own key of the draft leaving — a
|
|
1720
|
+
// clone's key, or an overlay's own add — is one fewer now. An overlay's
|
|
1721
|
+
// override of a committed key is the exception: the committed key's
|
|
1722
|
+
// deletion is recorded in `del` and counted at the rebuild, as before.
|
|
1723
|
+
// Without the decrement the natural "delete the old key, set the new
|
|
1724
|
+
// one" shape drifted the count up by one per write until a three-key
|
|
1725
|
+
// object crossed the overlay threshold and paid a rebuild on every
|
|
1726
|
+
// commit after.
|
|
1727
|
+
const o = e.ovl && hasOwn.call(e.v, n);
|
|
1728
|
+
if (hasOwn.call(i, n)) {
|
|
1729
|
+
delete i[n];
|
|
1730
|
+
o || e.kc--;
|
|
1731
|
+
}
|
|
1732
|
+
if (o) (e.del ??= new Set).add(n);
|
|
1647
1733
|
if (r) notifyWrites(e);
|
|
1648
1734
|
return true;
|
|
1649
1735
|
}
|
|
@@ -1654,11 +1740,12 @@ const traps = {
|
|
|
1654
1740
|
* to the key's presence node or a later add never re-runs it. */ function observerHoldsContainer(e, n) {
|
|
1655
1741
|
const t = e.k;
|
|
1656
1742
|
if (t === null) return false;
|
|
1657
|
-
const r = t.
|
|
1658
|
-
return r !== null && r.
|
|
1743
|
+
const r = t.ve;
|
|
1744
|
+
return r !== null && r.u === n && (!(n.R & REACTIVE_RECOMPUTING_DEPS) || r.Te === n.Ne);
|
|
1659
1745
|
}
|
|
1660
1746
|
|
|
1661
1747
|
function storeSetter(e, n, t = true) {
|
|
1748
|
+
if (writeHook !== null) writeHook(e);
|
|
1662
1749
|
const r = e[$TARGET];
|
|
1663
1750
|
const i = writeScopes;
|
|
1664
1751
|
writeScopes = new Set;
|
|
@@ -1712,6 +1799,8 @@ function storeSetter(e, n, t = true) {
|
|
|
1712
1799
|
function createStore(e, n) {
|
|
1713
1800
|
const t = n?.shallow === true;
|
|
1714
1801
|
const r = wrap(e);
|
|
1802
|
+
// Dependency edges (store/edges.ts): the root is named like a plain signal.
|
|
1803
|
+
if (signalHook !== null) signalHook(r[$TARGET]);
|
|
1715
1804
|
if (t) {
|
|
1716
1805
|
r[$TARGET].s = true;
|
|
1717
1806
|
markRawIngest(e);
|
|
@@ -1842,7 +1931,7 @@ function snapshotWalk(e, n, t) {
|
|
|
1842
1931
|
// R27: a snapshot sees the batch's pending backing — a held one (another
|
|
1843
1932
|
// transaction's future) only from its own draft.
|
|
1844
1933
|
let o = i.v;
|
|
1845
|
-
if (i.pb !== null && (inDraft(i) || writeOverride || i.k === null || !(i.k.
|
|
1934
|
+
if (i.pb !== null && (inDraft(i) || writeOverride || i.k === null || !(i.k.l & CONFIG_HELD))) {
|
|
1846
1935
|
if (i.ovl) materializePB(i);
|
|
1847
1936
|
o = i.pb;
|
|
1848
1937
|
}
|
|
@@ -1894,4 +1983,4 @@ function snapshotWalk(e, n, t) {
|
|
|
1894
1983
|
return o ?? r;
|
|
1895
1984
|
}
|
|
1896
1985
|
|
|
1897
|
-
export { $PROXY, $TARGET, $TRACK, LaneView, adoptPB,
|
|
1986
|
+
export { $PROXY, $TARGET, $TRACK, LaneView, adoptPB, arrayStructureChanged, asBacking, bumpDeep, cloneRaw, committed, containerEquals, createStore, deep, getContainerNode, getHasNode, getNode, hasAccessorFlag, materializePB, membershipChanged, notifyFold, notifyFoldTail, notifyKeyDiff, notifyKeyValue, resolveChainedRaw, runDirect, sameKey, sameRow, snapshot, storeHasFamily, storeHasOptimisticFamily, storeIsShallow, storeSetter, targetsEqual, unwrapValue, userWriting, wrap };
|