@solidjs/signals 2.0.0-rc.15 → 2.0.0-rc.17
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 +2268 -1734
- package/dist/dev.attribution.js +75 -52
- package/dist/dev.js +1348 -658
- package/dist/observe/affects-marks.js +86 -0
- package/dist/observe/affects.js +22 -73
- package/dist/observe/boundaries.js +192 -190
- package/dist/observe/core/async.js +177 -131
- package/dist/observe/core/attribution-queries.js +1 -1
- package/dist/observe/core/attribution.js +176 -159
- package/dist/observe/core/constants.js +17 -3
- package/dist/observe/core/context.js +5 -3
- package/dist/observe/core/core.js +520 -364
- package/dist/observe/core/dev.js +66 -38
- package/dist/observe/core/effect.js +50 -48
- package/dist/observe/core/error.js +5 -3
- package/dist/observe/core/external.js +5 -6
- package/dist/observe/core/graph.js +60 -44
- package/dist/observe/core/heap.js +33 -33
- package/dist/observe/core/identities.js +62 -0
- package/dist/observe/core/lanes.js +238 -260
- package/dist/observe/core/owner.js +117 -79
- package/dist/observe/core/scheduler.js +381 -217
- package/dist/observe/core/truth.js +45 -0
- package/dist/observe/core/verdict.js +91 -50
- package/dist/observe/deferred.js +184 -0
- package/dist/observe/index.js +7 -1
- package/dist/observe/map.js +345 -260
- package/dist/observe/reveal.js +76 -76
- package/dist/observe/signals.js +47 -31
- package/dist/observe/store/affects.js +18 -18
- package/dist/observe/store/edges.js +39 -0
- package/dist/observe/store/optimistic.js +146 -100
- package/dist/observe/store/projection.js +68 -55
- package/dist/observe/store/store.js +180 -118
- package/dist/prod/affects-marks.js +86 -0
- package/dist/prod/affects.js +23 -72
- package/dist/prod/boundaries.js +156 -154
- package/dist/prod/core/async.js +173 -127
- package/dist/prod/core/constants.js +17 -3
- package/dist/prod/core/context.js +5 -3
- package/dist/prod/core/core.js +495 -343
- package/dist/prod/core/effect.js +70 -68
- package/dist/prod/core/error.js +5 -3
- package/dist/prod/core/external.js +5 -6
- package/dist/prod/core/graph.js +60 -44
- package/dist/prod/core/heap.js +38 -38
- package/dist/prod/core/identities.js +62 -0
- package/dist/prod/core/lanes.js +230 -252
- package/dist/prod/core/owner.js +114 -77
- package/dist/prod/core/scheduler.js +327 -163
- package/dist/prod/core/truth.js +45 -0
- package/dist/prod/core/verdict.js +90 -49
- package/dist/prod/deferred.js +182 -0
- package/dist/prod/index.js +7 -1
- package/dist/prod/map.js +360 -275
- package/dist/prod/reveal.js +76 -76
- package/dist/prod/signals.js +42 -26
- package/dist/prod/store/affects.js +6 -6
- package/dist/prod/store/edges.js +39 -0
- package/dist/prod/store/optimistic.js +143 -98
- package/dist/prod/store/projection.js +25 -19
- package/dist/prod/store/store.js +157 -100
- package/dist/types/affects-marks.d.ts +14 -0
- package/dist/types/boundaries.d.ts +1 -1
- package/dist/types/core/async.d.ts +10 -0
- package/dist/types/core/constants.d.ts +13 -1
- package/dist/types/core/core.d.ts +54 -5
- package/dist/types/core/dev.d.ts +51 -13
- package/dist/types/core/error.d.ts +9 -1
- 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 -14
- package/dist/types/core/owner.d.ts +9 -3
- package/dist/types/core/scheduler.d.ts +70 -32
- package/dist/types/core/truth.d.ts +16 -0
- package/dist/types/core/types.d.ts +21 -0
- 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/edges.d.ts +9 -0
- package/dist/types/store/index.d.ts +10 -3
- 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 +1 -1
- package/dist/types/store/target.d.ts +16 -8
- package/dist/types/store/types.d.ts +14 -0
- package/package.json +1 -1
|
@@ -2,17 +2,19 @@ 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, snapshotCaptureActive, captureWriteSnapshot, context, notePromotedWrite, untrack, slotSignal, stagedScreen, stagedRead } from "../core/core.js";
|
|
5
|
+
import { writeHook, 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
|
|
|
9
|
+
import { signalHook } from "../core/identities.js";
|
|
10
|
+
|
|
9
11
|
import { setSlotUnobserved } from "../core/graph.js";
|
|
10
12
|
|
|
11
13
|
import { getOwner, getObserver } from "../core/owner.js";
|
|
12
14
|
|
|
13
15
|
import { insertIntoHeap } from "../core/heap.js";
|
|
14
16
|
|
|
15
|
-
import { txOf, flushTransaction, holdNode, joinFuture, insertSubs, schedule, passLane, globalQueue, queuePendingNode, GlobalQueue, joinPassTx, dirtyQueue } from "../core/scheduler.js";
|
|
17
|
+
import { txOf, flushTransaction, holdNode, joinFuture, insertSubs, schedule, passLane, globalQueue, queuePendingNode, resolveTx, GlobalQueue, installStoreHooks, joinPassTx, dirtyQueue } from "../core/scheduler.js";
|
|
16
18
|
|
|
17
19
|
import { lookupTarget, storeOwners, setLookup, $OWNER, isOwned, markDescendants, devAssertNeverUserMutation } from "./target.js";
|
|
18
20
|
|
|
@@ -170,7 +172,7 @@ function wrap(e, n = null, t = null, r = n?.fam ?? null) {
|
|
|
170
172
|
// adoption swap raw identity without changing the logical value — only
|
|
171
173
|
// changed leaves notify, R9).
|
|
172
174
|
const slotNodeEquals = function(e, n) {
|
|
173
|
-
return isEqual(e, n) || sameLogicalSlot(this.
|
|
175
|
+
return isEqual(e, n) || sameLogicalSlot(this.O, e, n);
|
|
174
176
|
};
|
|
175
177
|
|
|
176
178
|
/** The deep witness: the store decides when it notifies. */ const never = false;
|
|
@@ -208,7 +210,7 @@ function sameKey(e, n) {
|
|
|
208
210
|
/** The container node's comparator: a staging is a backing (identity — a
|
|
209
211
|
* pending backing is never "equal" to the committed one); a lane value is
|
|
210
212
|
* an arrangement (`LaneView`), judged by the optimistic machinery. */ const containerEquals = function(e, n) {
|
|
211
|
-
return e === n || optHooks !== null && optHooks.arrangement(this.
|
|
213
|
+
return e === n || optHooks !== null && optHooks.arrangement(this.O, e, n);
|
|
212
214
|
};
|
|
213
215
|
|
|
214
216
|
function sameLogicalSlot(e, n, t) {
|
|
@@ -221,8 +223,8 @@ function sameLogicalSlot(e, n, t) {
|
|
|
221
223
|
* released at the commit that resolves it (`drainFolds`). */ const deferredReleases = new Set;
|
|
222
224
|
|
|
223
225
|
function releaseSlot(e) {
|
|
224
|
-
const n = e.
|
|
225
|
-
const t = e.
|
|
226
|
+
const n = e.O;
|
|
227
|
+
const t = e.Vt;
|
|
226
228
|
if (t === $TRACK) {
|
|
227
229
|
if (n.k === e) n.k = null;
|
|
228
230
|
} else if (t === $DEEP) {
|
|
@@ -238,17 +240,21 @@ function releaseSlot(e) {
|
|
|
238
240
|
/** A node was born on `target`: a family target enters the live index. */ function noteNode(e, n, t) {
|
|
239
241
|
if (e.fam !== null) e.fam.live.add(e);
|
|
240
242
|
markDescendants(e);
|
|
241
|
-
if (affectsHooks !== null) affectsHooks.
|
|
243
|
+
if (affectsHooks !== null) affectsHooks.ro(e, n, t);
|
|
242
244
|
}
|
|
243
245
|
|
|
244
246
|
// Shared slot-node release handler: registered once; the core sweep
|
|
245
247
|
// dispatches CONFIG_SLOT_NODE nodes here (graph.ts) instead of holding a
|
|
246
248
|
// per-node closure in a per-node NodeExtension. A dropped node is
|
|
247
249
|
// unreachable through the store — a fresh read makes a fresh node.
|
|
250
|
+
// A staged write — or a lane's value (a guess, S4) — is state only the node
|
|
251
|
+
// holds: its release waits for the commit that resolves it. So does a leaf of
|
|
252
|
+
// a held container (#4146): a mainline write it carried is in the held
|
|
253
|
+
// staging too, where it would read as the hold's until the landing.
|
|
254
|
+
const slotKept = e => e.ue !== NOT_PENDING || e.l & CONFIG_OVERRIDE || e.O.k?.l & CONFIG_HELD;
|
|
255
|
+
|
|
248
256
|
setSlotUnobserved(e => {
|
|
249
|
-
|
|
250
|
-
// node holds: defer the release to the commit that resolves it.
|
|
251
|
-
if (e.Oe !== NOT_PENDING || e.i & CONFIG_OVERRIDE) deferredReleases.add(e); else releaseSlot(e);
|
|
257
|
+
if (slotKept(e)) deferredReleases.add(e); else releaseSlot(e);
|
|
252
258
|
});
|
|
253
259
|
|
|
254
260
|
/** A node born while its container has a staging is born from the two
|
|
@@ -256,19 +262,24 @@ setSlotUnobserved(e => {
|
|
|
256
262
|
* pending one — and held by the container's transaction when the container
|
|
257
263
|
* is held (#3706: the unit of the hold is the key — a key the batch left
|
|
258
264
|
* unchanged stages nothing and holds no one). */ function bornStaged(e, n, t, r) {
|
|
259
|
-
if (n.
|
|
265
|
+
if (n.Ye && n.Ye(n.Ie, t)) return;
|
|
260
266
|
queuePendingNode(n);
|
|
261
|
-
n.
|
|
267
|
+
n.ue = t;
|
|
262
268
|
const o = e.k;
|
|
263
269
|
if (o === null) return;
|
|
264
270
|
// a node-less staging holds nothing (`stageOn`)
|
|
265
271
|
// Held by the container's transaction only for a key the HELD staging
|
|
266
272
|
// changed — a mainline layer's change above it stages mainline. (A
|
|
267
|
-
// container carrying a guess is a lane's: the flush's park decides
|
|
268
|
-
|
|
273
|
+
// container carrying a guess is a lane's: the flush's park decides —
|
|
274
|
+
// unless a correction folded under the guess, D-34: the key is that
|
|
275
|
+
// correction's hold's.)
|
|
276
|
+
if (o.l & CONFIG_HELD && heldKeyChanged(e, r)) {
|
|
277
|
+
const e = txOf(o);
|
|
278
|
+
if (!(o.l & CONFIG_OVERRIDE) || e.folded) holdNode(n, resolveTx(e._parent ?? e));
|
|
279
|
+
}
|
|
269
280
|
// A28 (4): a batch written inside a creation-time pass is promoted — its
|
|
270
281
|
// late-materialized leaf with it.
|
|
271
|
-
if (o.
|
|
282
|
+
if (o.l & CONFIG_PROMOTED) notePromotedWrite(n);
|
|
272
283
|
}
|
|
273
284
|
|
|
274
285
|
function getNode(e, n,
|
|
@@ -293,7 +304,9 @@ t = -1) {
|
|
|
293
304
|
// the hottest store path, and the disabled cost must stay one null
|
|
294
305
|
// check (nodes created before enable() stay generically named).
|
|
295
306
|
if (attrHooks !== null) {
|
|
296
|
-
|
|
307
|
+
const t = storeRoot(e);
|
|
308
|
+
l._name = (storeNames.get(t) ?? "store") + "." + String(n);
|
|
309
|
+
l._loc = storeLocs.get(t);
|
|
297
310
|
stampNodeOwner(l, e);
|
|
298
311
|
}
|
|
299
312
|
r[n] = o;
|
|
@@ -330,8 +343,8 @@ function getHasNode(e, n) {
|
|
|
330
343
|
// pending backing staged — so whoever asked for it reads the frame it
|
|
331
344
|
// would have read had the node been there from the write.
|
|
332
345
|
if (e.pb !== null && !directCommit) {
|
|
333
|
-
n.
|
|
334
|
-
n.
|
|
346
|
+
n.Ie = preBatch(e);
|
|
347
|
+
n.ue = e.pb;
|
|
335
348
|
queuePendingNode(n);
|
|
336
349
|
}
|
|
337
350
|
}
|
|
@@ -347,11 +360,11 @@ function getHasNode(e, n) {
|
|
|
347
360
|
* staging: nothing allocates, nothing is swept at the commit (dbmon's tick
|
|
348
361
|
* re-created, queued, swept and dropped ~7000 container nodes). If the
|
|
349
362
|
* flush parks, the seam materializes and holds the node then
|
|
350
|
-
* (`
|
|
351
|
-
if (e.k === null && passLane === null && (context === null || globalQueue.
|
|
363
|
+
* (`storeParkHook`) — the one case the staging home exists for. */ function stageOn(e, n) {
|
|
364
|
+
if (e.k === null && passLane === null && (context === null || globalQueue.q) && !deriveHolds(e)) return;
|
|
352
365
|
const t = getContainerNode(e);
|
|
353
|
-
if (t.
|
|
354
|
-
t.
|
|
366
|
+
if (t.ue === NOT_PENDING) queuePendingNode(t);
|
|
367
|
+
t.ue = n;
|
|
355
368
|
// A28 (4): a write issued inside a pass outside a flush (a creation-time
|
|
356
369
|
// derive) is promoted at the pass's end — the batch with it.
|
|
357
370
|
if (context !== null) notePromotedWrite(t);
|
|
@@ -361,10 +374,10 @@ function getHasNode(e, n) {
|
|
|
361
374
|
/** The projection's derive is held by a transaction and this is its own
|
|
362
375
|
* write (not a user setter's): the staging is the derive's (`holdWithDerive`). */ function deriveHolds(e) {
|
|
363
376
|
const n = e.fam?.node;
|
|
364
|
-
return n != null && !userWrite && (n.
|
|
377
|
+
return n != null && !userWrite && (n.l & CONFIG_HELD) !== 0;
|
|
365
378
|
}
|
|
366
379
|
|
|
367
|
-
|
|
380
|
+
const storePark = e => {
|
|
368
381
|
for (let n = 0; n < foldList.length; n++) {
|
|
369
382
|
const t = foldList[n];
|
|
370
383
|
if (t.k === null && t.pb !== null) holdNode(getContainerNode(t), e);
|
|
@@ -393,9 +406,9 @@ function bumpDeep(e) {
|
|
|
393
406
|
// on the guess (`laneWrite` → `supersede`, judged by `containerEquals`);
|
|
394
407
|
// otherwise the structural readers are walked (the staging is already on
|
|
395
408
|
// the node; the comparator is the store's, done).
|
|
396
|
-
if (e.
|
|
397
|
-
if (e.
|
|
398
|
-
if (snapshotCaptureActive) captureWriteSnapshot(e, e.
|
|
409
|
+
if (e.l & CONFIG_OVERRIDE) setSignal(e, n); else {
|
|
410
|
+
if (e.l & CONFIG_HELD) joinFuture(txOf(e));
|
|
411
|
+
if (snapshotCaptureActive) captureWriteSnapshot(e, e.Ie);
|
|
399
412
|
insertSubs(e);
|
|
400
413
|
schedule();
|
|
401
414
|
}
|
|
@@ -413,17 +426,22 @@ function storeRootOwner(e) {
|
|
|
413
426
|
}
|
|
414
427
|
|
|
415
428
|
function stampNodeOwner(e, n) {
|
|
416
|
-
e.
|
|
429
|
+
e.H = storeRootOwner(n) ?? null;
|
|
417
430
|
}
|
|
418
431
|
|
|
419
432
|
const storeNames = new WeakMap;
|
|
420
433
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
434
|
+
// The store's `loc` option, carried onto every property node beside its
|
|
435
|
+
// label (the same gate: nodes created before the engine is installed carry
|
|
436
|
+
// neither).
|
|
437
|
+
const storeLocs = new WeakMap;
|
|
424
438
|
|
|
425
|
-
function
|
|
426
|
-
|
|
439
|
+
function nameStore(e, n, t) {
|
|
440
|
+
if (attrHooks !== null) {
|
|
441
|
+
const r = e[$TARGET];
|
|
442
|
+
if (n) storeNames.set(r, n);
|
|
443
|
+
if (t) storeLocs.set(r, t);
|
|
444
|
+
}
|
|
427
445
|
}
|
|
428
446
|
|
|
429
447
|
// ---------------------------------------------------------------------------
|
|
@@ -512,7 +530,7 @@ const plainProto = e => {
|
|
|
512
530
|
if (e.kc > OVERLAY_REBUILD_MAX_KEYS) return false;
|
|
513
531
|
if (e.del !== null && e.del.size !== 0) return true;
|
|
514
532
|
const t = e.wk;
|
|
515
|
-
if (t !== null && t !== WK_ALL && t.size <= OVERLAY_REBUILD_MIN_ADDS) return false;
|
|
533
|
+
if (t !== null && t !== WK_ALL && t.u === undefined && t.size <= OVERLAY_REBUILD_MIN_ADDS) return false;
|
|
516
534
|
const r = e.v;
|
|
517
535
|
let o = 0;
|
|
518
536
|
for (const e of Reflect.ownKeys(n)) if (!hasOwn.call(r, e) && ++o > OVERLAY_REBUILD_MIN_ADDS) return true;
|
|
@@ -532,7 +550,7 @@ const plainProto = e => {
|
|
|
532
550
|
e.pb = t;
|
|
533
551
|
e.ovl = false;
|
|
534
552
|
// The staging IS the pending backing: re-point the container node.
|
|
535
|
-
if (e.k !== null && e.k.
|
|
553
|
+
if (e.k !== null && e.k.ue === n) e.k.ue = t;
|
|
536
554
|
return t;
|
|
537
555
|
}
|
|
538
556
|
|
|
@@ -553,7 +571,7 @@ const plainProto = e => {
|
|
|
553
571
|
// mainline setter mid-hold publishes mainline; the held keys stay held).
|
|
554
572
|
n = e.pb = cloneRaw(n, e);
|
|
555
573
|
const t = e.k;
|
|
556
|
-
if (t !== null && !(t.
|
|
574
|
+
if (t !== null && !(t.l & CONFIG_HELD)) t.ue = n;
|
|
557
575
|
} else if (n === null) {
|
|
558
576
|
const t = e.v;
|
|
559
577
|
if (!directCommit) queueFold(e);
|
|
@@ -632,7 +650,7 @@ const plainProto = e => {
|
|
|
632
650
|
} else r = e.v;
|
|
633
651
|
if (directCommit) {
|
|
634
652
|
e.pb = null;
|
|
635
|
-
if (e.k !== null) e.k.
|
|
653
|
+
if (e.k !== null) e.k.Ie = n;
|
|
636
654
|
} else {
|
|
637
655
|
// Eager (INTERNALS §3): the backing swaps now — handlers read the
|
|
638
656
|
// adopted object before the flush — while the container node, when
|
|
@@ -728,7 +746,7 @@ let foldIndex = null;
|
|
|
728
746
|
if (r.pb === null) continue;
|
|
729
747
|
// folded already (a direct commit)
|
|
730
748
|
const o = r.k;
|
|
731
|
-
if (o !== null && o.
|
|
749
|
+
if (o !== null && o.ue !== NOT_PENDING) {
|
|
732
750
|
// held: commits with its hold
|
|
733
751
|
foldList.push(r);
|
|
734
752
|
foldOlds.push(n[t]);
|
|
@@ -739,7 +757,7 @@ let foldIndex = null;
|
|
|
739
757
|
}
|
|
740
758
|
if (deferredReleases.size !== 0) {
|
|
741
759
|
for (const e of deferredReleases) {
|
|
742
|
-
if (e.
|
|
760
|
+
if (e.o === null && !slotKept(e)) {
|
|
743
761
|
deferredReleases.delete(e);
|
|
744
762
|
releaseSlot(e);
|
|
745
763
|
}
|
|
@@ -750,33 +768,33 @@ let foldIndex = null;
|
|
|
750
768
|
/** Core asks: is a slot node's truth in flight? Its family's derive is —
|
|
751
769
|
* lanes (A17, #2864: a guess mid-refetch stands in for the flight) and
|
|
752
770
|
* marks (an ambient `affects()` over a declared reload lives to the
|
|
753
|
-
* landing) read it. */ GlobalQueue.
|
|
754
|
-
const n = e.
|
|
755
|
-
return n != null && n.node !== null && (n.node.
|
|
771
|
+
* landing) read it. */ GlobalQueue.k = e => {
|
|
772
|
+
const n = e.O?.fam;
|
|
773
|
+
return n != null && n.node !== null && (n.node.m & STATUS_PENDING) !== 0;
|
|
756
774
|
};
|
|
757
775
|
|
|
758
776
|
/** Core asks (lanes.ts `covered`): the value a slot node's guess covered.
|
|
759
777
|
* A chained link's committed value is the inner store's at the last
|
|
760
778
|
* read-through — refreshed here, since a guess served the reads since
|
|
761
|
-
* (#3672: the revert compares against the base's live value). */ GlobalQueue.
|
|
762
|
-
const n = e.
|
|
779
|
+
* (#3672: the revert compares against the base's live value). */ GlobalQueue.fn = e => {
|
|
780
|
+
const n = e.O;
|
|
763
781
|
if (n.ch) {
|
|
764
|
-
const t = e.
|
|
782
|
+
const t = e.Vt;
|
|
765
783
|
const r = n.v;
|
|
766
|
-
if (n.h !== null && n.h[t] === e) e.
|
|
784
|
+
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]));
|
|
767
785
|
}
|
|
768
|
-
return e.
|
|
786
|
+
return e.Ie;
|
|
769
787
|
};
|
|
770
788
|
|
|
771
|
-
|
|
789
|
+
installStoreHooks(storePark, () => {
|
|
772
790
|
drainFolds();
|
|
773
791
|
optHooks?.sweep();
|
|
774
|
-
};
|
|
792
|
+
});
|
|
775
793
|
|
|
776
794
|
/** The store half of `affects()` (store/affects.ts) makes its marks' nodes
|
|
777
|
-
* and walks records through these: it never imports the engine. */ GlobalQueue.
|
|
795
|
+
* and walks records through these: it never imports the engine. */ GlobalQueue.so = getNode;
|
|
778
796
|
|
|
779
|
-
GlobalQueue.
|
|
797
|
+
GlobalQueue.fo = isWrappable;
|
|
780
798
|
|
|
781
799
|
/** A projection's creation run commits directly (a memo's first value is
|
|
782
800
|
* its `_value`, not a staging): the draft still writes a clone of the seed,
|
|
@@ -804,7 +822,7 @@ function runDirect(e) {
|
|
|
804
822
|
// the adopted object (shared ownership — never cloned).
|
|
805
823
|
e.pb = null;
|
|
806
824
|
e.wk = null;
|
|
807
|
-
} else if (e.ovl && (e.v !== n || !overlayRebuilds(e, r) && t?.
|
|
825
|
+
} else if (e.ovl && (e.v !== n || !overlayRebuilds(e, r) && t?.T?.lt !== n)) {
|
|
808
826
|
// Overlay flatten (#3044): the backing keeps its identity, so the
|
|
809
827
|
// `t.v === old` gate below skips path copying (the parent slot
|
|
810
828
|
// already points here) and the adopted-notify (setter
|
|
@@ -818,8 +836,8 @@ function runDirect(e) {
|
|
|
818
836
|
const t = e.v;
|
|
819
837
|
const o = e.wk;
|
|
820
838
|
if (o !== null && o !== WK_ALL) {
|
|
821
|
-
for (
|
|
822
|
-
if (hasOwn.call(r,
|
|
839
|
+
for (let e = o; e !== undefined; e = e.u) for (const n of e) {
|
|
840
|
+
if (hasOwn.call(r, n)) copyOwn(t, r, n); else delete t[n];
|
|
823
841
|
}
|
|
824
842
|
} else {
|
|
825
843
|
for (const e of Reflect.ownKeys(r)) {
|
|
@@ -846,7 +864,7 @@ function runDirect(e) {
|
|
|
846
864
|
// subscribes to was a transient staging home (INTERNALS §5: an
|
|
847
865
|
// observer-less write holds a transient record until the fold,
|
|
848
866
|
// discarded with it) — gone with the batch.
|
|
849
|
-
if (t.
|
|
867
|
+
if (t.o === null) e.k = null; else if (t.Ie instanceof LaneView) t.Ie.base = e.v; else t.Ie = e.v;
|
|
850
868
|
}
|
|
851
869
|
if (e.v !== n && e.u) {
|
|
852
870
|
const t = parentSlotKey(e, n);
|
|
@@ -919,9 +937,9 @@ function reportReplacedContainers(e, n, t, r) {
|
|
|
919
937
|
|
|
920
938
|
function heldDerivation(e, n, t) {
|
|
921
939
|
if (e.fam === null || !userWrite) return false;
|
|
922
|
-
if (n !== undefined) return (n.
|
|
940
|
+
if (n !== undefined) return (n.l & (CONFIG_HELD | CONFIG_MANUAL_WRITE)) === CONFIG_HELD && txOf(n) !== flushTransaction;
|
|
923
941
|
const r = e.k;
|
|
924
|
-
return r !== null && (r.
|
|
942
|
+
return r !== null && (r.l & CONFIG_HELD) !== 0 && txOf(r) !== flushTransaction && heldKeyChanged(e, t);
|
|
925
943
|
}
|
|
926
944
|
|
|
927
945
|
/** Setter exit: one node write per changed observed key (the leaves stage
|
|
@@ -951,7 +969,10 @@ function heldDerivation(e, n, t) {
|
|
|
951
969
|
// value can change when ANY key is written), or a non-plain prototype
|
|
952
970
|
// (class instances: prototype getters derive from arbitrary fields).
|
|
953
971
|
// Overlay pbs chain to the COMMITTED object (#3044) — plainness is the
|
|
954
|
-
// committed container's prototype, not the overlay's.
|
|
972
|
+
// committed container's prototype, not the overlay's. THIS setter's keys
|
|
973
|
+
// only (D-8, #3743): a key an earlier setter of the batch wrote and this
|
|
974
|
+
// one did not is no second proposal — its node was told then, and telling
|
|
975
|
+
// a held node its unchanged value again would join this tick to the hold.
|
|
955
976
|
const o = e.wk;
|
|
956
977
|
const i = o === WK_ALL || e.a === true || !plainProto(e.ovl ? e.v : n) ? null : o;
|
|
957
978
|
if (attrHooks !== null) reportReplacedContainers(e, t, n, i);
|
|
@@ -987,9 +1008,9 @@ function heldDerivation(e, n, t) {
|
|
|
987
1008
|
// The derive's write is a derivation; a user setter's is a proposal
|
|
988
1009
|
// (A34 (3)'s discriminator).
|
|
989
1010
|
if (!userWrite) {
|
|
990
|
-
o.
|
|
1011
|
+
o.l &= ~CONFIG_MANUAL_WRITE;
|
|
991
1012
|
holdWithDerive(e, o);
|
|
992
|
-
} else o.
|
|
1013
|
+
} else o.l |= CONFIG_MANUAL_WRITE;
|
|
993
1014
|
}
|
|
994
1015
|
}
|
|
995
1016
|
}
|
|
@@ -1020,7 +1041,7 @@ function heldDerivation(e, n, t) {
|
|
|
1020
1041
|
// container carrying an arrangement guess is told whether or not anyone
|
|
1021
1042
|
// subscribes: the write is a landing on the guess (`notifyContainer`).
|
|
1022
1043
|
const u = e.k;
|
|
1023
|
-
if (u !== null && (u.
|
|
1044
|
+
if (u !== null && (u.o !== null || u.l & CONFIG_OVERRIDE)) {
|
|
1024
1045
|
let r;
|
|
1025
1046
|
if (e.ovl) {
|
|
1026
1047
|
r = e.del !== null && e.del.size !== 0;
|
|
@@ -1122,7 +1143,7 @@ function hasAccessorFlag(e) {
|
|
|
1122
1143
|
for (const o of Reflect.ownKeys(r)) if (e || o in n !== o in t) setSignal(r[o], o in t);
|
|
1123
1144
|
}
|
|
1124
1145
|
const o = e.k;
|
|
1125
|
-
if (o !== null && (o.
|
|
1146
|
+
if (o !== null && (o.o !== null || o.l & CONFIG_OVERRIDE)) {
|
|
1126
1147
|
const e = Array.isArray(t) && Array.isArray(n) ? arrayStructureChanged(n, t) : membershipChanged(n, t);
|
|
1127
1148
|
if (e) notifyContainer(o, t);
|
|
1128
1149
|
}
|
|
@@ -1179,7 +1200,7 @@ function inDraft(e) {
|
|
|
1179
1200
|
* "adoption is eager by contract" — and the node keeps the frame committed
|
|
1180
1201
|
* readers under a hold still see), `t.v` otherwise. */ function committed(e) {
|
|
1181
1202
|
const n = e.k;
|
|
1182
|
-
if (n !== null) return n.
|
|
1203
|
+
if (n !== null) return n.ue !== NOT_PENDING ? asBacking(n.Ie) : e.v;
|
|
1183
1204
|
// A node-less staging (`stageOn`): the fold queue remembers the frame.
|
|
1184
1205
|
return e.pb !== null ? preBatch(e) : e.v;
|
|
1185
1206
|
}
|
|
@@ -1191,8 +1212,8 @@ function inDraft(e) {
|
|
|
1191
1212
|
* committed frame. */ function stagingReader() {
|
|
1192
1213
|
let e = context;
|
|
1193
1214
|
if (e === null) return null;
|
|
1194
|
-
if (e.
|
|
1195
|
-
return e === null || e.
|
|
1215
|
+
if (e.Pt) e = e.Ht ?? null;
|
|
1216
|
+
return e === null || e.l & CONFIG_CHILDREN_FORBIDDEN ? null : e;
|
|
1196
1217
|
}
|
|
1197
1218
|
|
|
1198
1219
|
/** The backing a read serves (RUL-1, signal parity): the draft and the write
|
|
@@ -1230,10 +1251,10 @@ function inDraft(e) {
|
|
|
1230
1251
|
if (writeOverride && !userWrite) {
|
|
1231
1252
|
const t = n !== undefined ? e.n?.[n] : undefined;
|
|
1232
1253
|
if (t !== undefined) {
|
|
1233
|
-
if ((t.
|
|
1254
|
+
if ((t.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD) joinPassTx(txOf(t));
|
|
1234
1255
|
} else {
|
|
1235
1256
|
const t = e.k;
|
|
1236
|
-
if (t !== null && (t.
|
|
1257
|
+
if (t !== null && (t.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && (n === undefined || heldKeyChanged(e, n))) joinPassTx(txOf(t));
|
|
1237
1258
|
}
|
|
1238
1259
|
}
|
|
1239
1260
|
return r;
|
|
@@ -1245,9 +1266,9 @@ function inDraft(e) {
|
|
|
1245
1266
|
// it).
|
|
1246
1267
|
if (n !== undefined) {
|
|
1247
1268
|
const t = e.n?.[n];
|
|
1248
|
-
if (t !== undefined && t.
|
|
1269
|
+
if (t !== undefined && t.l & CONFIG_OVERRIDE) return committed(e);
|
|
1249
1270
|
}
|
|
1250
|
-
const i = o !== null && (o.
|
|
1271
|
+
const i = o !== null && (o.l & CONFIG_HELD) !== 0;
|
|
1251
1272
|
// A held container: the hold's unit is the key (#3706). A key the HELD
|
|
1252
1273
|
// staging left unchanged reads the ambient view — a mainline layer's
|
|
1253
1274
|
// write above the hold publishes mainline (#3688, #3612); a key it
|
|
@@ -1265,7 +1286,7 @@ function inDraft(e) {
|
|
|
1265
1286
|
// probe) judges the container like any node — for a key the batch
|
|
1266
1287
|
// changed; an unchanged key is committed and final (A22: pending is per
|
|
1267
1288
|
// key). A held container's committed frame is the node's.
|
|
1268
|
-
if (verdict === null || !i && n !== undefined && !keyChanged(e, n, t)) return i ? asBacking(o.
|
|
1289
|
+
if (verdict === null || !i && n !== undefined && !keyChanged(e, n, t)) return i ? asBacking(o.Ie) : e.v;
|
|
1269
1290
|
return o === null ? committed(e) : asBacking(read(o));
|
|
1270
1291
|
}
|
|
1271
1292
|
// A pass reading a key the batch left unchanged: the same value in both
|
|
@@ -1282,13 +1303,13 @@ function inDraft(e) {
|
|
|
1282
1303
|
// frames, a changed one is what the mark already stands for. The
|
|
1283
1304
|
// per-key question is asked once per pass, not once per key (a
|
|
1284
1305
|
// `mapArray` over 1k shifted rows asked it 1k times).
|
|
1285
|
-
if (l.R & REACTIVE_STAGED_READ && !(l.
|
|
1306
|
+
if (l.R & REACTIVE_STAGED_READ && !(l.l & (CONFIG_VERDICT | CONFIG_CHILDREN_FORBIDDEN))) return r;
|
|
1286
1307
|
if (n !== undefined && !keyChanged(e, n, t)) return committed(e);
|
|
1287
1308
|
// A node-less staging (`stageOn`): what `read()` of its node would do
|
|
1288
1309
|
// for this pass — a verdict reader sees the screen, any other derives
|
|
1289
1310
|
// from the staging and is marked as having read it.
|
|
1290
1311
|
if (o === null) {
|
|
1291
|
-
if (l.
|
|
1312
|
+
if (l.l & CONFIG_VERDICT && stagedScreen(l)) return committed(e);
|
|
1292
1313
|
stagedRead(l);
|
|
1293
1314
|
return r;
|
|
1294
1315
|
}
|
|
@@ -1298,7 +1319,7 @@ function inDraft(e) {
|
|
|
1298
1319
|
|
|
1299
1320
|
/** Did the HELD staging (the container node's, not a mainline layer above
|
|
1300
1321
|
* it) change `key`? */ function heldKeyChanged(e, n, t = true) {
|
|
1301
|
-
return changedBetween(e, e.k.
|
|
1322
|
+
return changedBetween(e, e.k.Ie, e.k.ue, n, t) && !adoptChild(e, n);
|
|
1302
1323
|
}
|
|
1303
1324
|
|
|
1304
1325
|
/** The values of a held frame, built once per frame: neither the committed
|
|
@@ -1325,14 +1346,14 @@ function rawsOf(e) {
|
|
|
1325
1346
|
* `version` holds. A record's keys pair by name; an array's by the family's
|
|
1326
1347
|
* key — without a family the pairing is not known here and the hold stands. */ function adoptChild(e, n) {
|
|
1327
1348
|
const t = e.k;
|
|
1328
|
-
const r = asBacking(t.
|
|
1349
|
+
const r = asBacking(t.Ie);
|
|
1329
1350
|
const o = r[n];
|
|
1330
|
-
const i = t.
|
|
1351
|
+
const i = t.ue[n];
|
|
1331
1352
|
const l = Array.isArray(r) ? e.fam?.key : null;
|
|
1332
1353
|
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
1354
|
// A raw in both frames (the same objects swapped or reordered) is its
|
|
1334
1355
|
// own row: pairing it with another would alias the two after the commit.
|
|
1335
|
-
rawsOf(r).has(i) || rawsOf(t.
|
|
1356
|
+
rawsOf(r).has(i) || rawsOf(t.ue).has(o)) return false;
|
|
1336
1357
|
// A raw-marked value wraps to itself, a proxy to its own target: neither
|
|
1337
1358
|
// is a fresh target around `ov`.
|
|
1338
1359
|
const u = wrap(o, e, n)[$TARGET];
|
|
@@ -1343,7 +1364,7 @@ function rawsOf(e) {
|
|
|
1343
1364
|
}
|
|
1344
1365
|
|
|
1345
1366
|
/** A family target's unheld staging, read with no pass (a handler). */ function familyAhead(e) {
|
|
1346
|
-
return e.fam !== null && context === null && !(e.k !== null && e.k.
|
|
1367
|
+
return e.fam !== null && context === null && !(e.k !== null && e.k.l & CONFIG_HELD);
|
|
1347
1368
|
}
|
|
1348
1369
|
|
|
1349
1370
|
/** Did this batch change `key` — value (slot equality), presence, and (for
|
|
@@ -1377,7 +1398,7 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1377
1398
|
* tracked or not, applies the hold and A28 rules a signal's read does. A
|
|
1378
1399
|
* projection's unheld staging is ahead for a handler (`familyAhead`). */ function nodeValue(e, n, t) {
|
|
1379
1400
|
let r = read(e);
|
|
1380
|
-
if (e.
|
|
1401
|
+
if (e.ue !== NOT_PENDING && !(e.l & CONFIG_HELD) && context === null && t.fam !== null) r = e.ue;
|
|
1381
1402
|
return r === FORCE ? n : r;
|
|
1382
1403
|
}
|
|
1383
1404
|
|
|
@@ -1397,8 +1418,8 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1397
1418
|
if (l) {
|
|
1398
1419
|
// The link's committed value follows the inner store's (what a lane
|
|
1399
1420
|
// over it covers — its reveal and revert compare against it, S4).
|
|
1400
|
-
o.
|
|
1401
|
-
if (o.
|
|
1421
|
+
o.Ie = t;
|
|
1422
|
+
if (o.l & CONFIG_OVERRIDE) u = n;
|
|
1402
1423
|
} else u = n;
|
|
1403
1424
|
}
|
|
1404
1425
|
}
|
|
@@ -1419,7 +1440,7 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1419
1440
|
}
|
|
1420
1441
|
|
|
1421
1442
|
/** Is `n` among `c`'s dependencies (any pass's — a kept tail included)? */ function linkedTo(e, n) {
|
|
1422
|
-
for (let t = e.
|
|
1443
|
+
for (let t = e.C; t !== null; t = t.G) if (t.S === n) return true;
|
|
1423
1444
|
return false;
|
|
1424
1445
|
}
|
|
1425
1446
|
|
|
@@ -1438,7 +1459,7 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1438
1459
|
// reader of a memo would — linked, so it re-runs at the landing (the
|
|
1439
1460
|
// next settled pass pulls without linking and the stale link trims), a
|
|
1440
1461
|
// verdict reader registered, a tracked pass suspended.
|
|
1441
|
-
if (n.
|
|
1462
|
+
if (n.m & (STATUS_PENDING | STATUS_ERROR)) {
|
|
1442
1463
|
// A render effect outside the flight's own flush is the frame, not a
|
|
1443
1464
|
// derivation: it keeps what it shows and learns of the landing from the
|
|
1444
1465
|
// leaves the landing changes (unchanged leaves say nothing — it is not a
|
|
@@ -1449,7 +1470,7 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1449
1470
|
// release the hold with the flight still up. An errored derive has no
|
|
1450
1471
|
// landing to learn of: the error is what the frame shows.
|
|
1451
1472
|
const t = context;
|
|
1452
|
-
if (t !== null && t.
|
|
1473
|
+
if (t !== null && t.i === EFFECT_RENDER && !(n.m & STATUS_ERROR) && n.l & CONFIG_HELD && flushTransaction !== txOf(n) && !linkedTo(t, n)) return;
|
|
1453
1474
|
// An optimistic family carrying guesses that stand in for the flight
|
|
1454
1475
|
// (A17): the guesses are the display — a reader is served the leaves
|
|
1455
1476
|
// (committed plus the guesses) and does not suspend on the derive; a
|
|
@@ -1457,7 +1478,7 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1457
1478
|
// up). The family's, not the target's: the edit stands in for the one
|
|
1458
1479
|
// flight the family has.
|
|
1459
1480
|
const r = e.fam.overlaid;
|
|
1460
|
-
if (verdict === null && n.
|
|
1481
|
+
if (verdict === null && n.m & STATUS_PENDING && r !== undefined && r.size !== 0) return;
|
|
1461
1482
|
read(n);
|
|
1462
1483
|
} else untrack(() => read(n));
|
|
1463
1484
|
}
|
|
@@ -1470,12 +1491,12 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1470
1491
|
* classification, as core reads it — `heldNotFinal`). */ function holdWithDerive(e, n) {
|
|
1471
1492
|
const t = e.fam?.node;
|
|
1472
1493
|
if (t !== undefined && t !== null && !userWrite && // the derive's own write (its draft or landing), not a user setter's
|
|
1473
|
-
t.
|
|
1494
|
+
t.l & CONFIG_HELD && n.ue !== NOT_PENDING &&
|
|
1474
1495
|
// A node carrying a guess is the lane's: its staging is the truth beneath
|
|
1475
1496
|
// the guess (`setSignal` → `laneWrite`), never re-homed by the store.
|
|
1476
|
-
!(n.
|
|
1497
|
+
!(n.l & (CONFIG_HELD | CONFIG_OVERRIDE))) {
|
|
1477
1498
|
holdNode(n, txOf(t));
|
|
1478
|
-
n.
|
|
1499
|
+
n.T.ut = t.T.ut;
|
|
1479
1500
|
}
|
|
1480
1501
|
}
|
|
1481
1502
|
|
|
@@ -1518,6 +1539,22 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1518
1539
|
|
|
1519
1540
|
// ---------------------------------------------------------------------------
|
|
1520
1541
|
// traps
|
|
1542
|
+
/** A trap write: the target is told at the setter's exit, the key recorded
|
|
1543
|
+
* in this setter's `wk`. A setter's first write to a target still staged by
|
|
1544
|
+
* an earlier setter of the batch opens a set of its own (the notify visits
|
|
1545
|
+
* it, D-8) and chains the earlier one behind for the fold's union. */ function touch(e, n) {
|
|
1546
|
+
let t = e.wk;
|
|
1547
|
+
if (!pendingNotify.has(e)) {
|
|
1548
|
+
pendingNotify.add(e);
|
|
1549
|
+
if (t !== null && t !== WK_ALL) {
|
|
1550
|
+
t = new Set;
|
|
1551
|
+
t.u = e.wk;
|
|
1552
|
+
e.wk = t;
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
if (t !== WK_ALL) (e.wk ??= new Set).add(n);
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1521
1558
|
const traps = {
|
|
1522
1559
|
get(e, n, t) {
|
|
1523
1560
|
// One typeof gates every brand-symbol compare off the hot string path.
|
|
@@ -1546,7 +1583,7 @@ const traps = {
|
|
|
1546
1583
|
}
|
|
1547
1584
|
// (The witness before the pull: a mark on an uninitialized derived
|
|
1548
1585
|
// store is witnessed, then the pull throws — loading, declared pending.)
|
|
1549
|
-
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.
|
|
1586
|
+
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.io(e, n);
|
|
1550
1587
|
if (e.fam !== null) pullFamily(e);
|
|
1551
1588
|
// Hot inline case: existing PLAIN node (non-accessor) read outside any
|
|
1552
1589
|
// draft — the dbmon/uibench effect re-read shape. Core's `read()` serves
|
|
@@ -1561,7 +1598,7 @@ const traps = {
|
|
|
1561
1598
|
const r = e.n?.[n];
|
|
1562
1599
|
if (writeScopes === null && !writeOverride) {
|
|
1563
1600
|
const t = r;
|
|
1564
|
-
if (t !== undefined && t.acc !== true && (!e.ch || t.
|
|
1601
|
+
if (t !== undefined && t.acc !== true && (!e.ch || t.l & CONFIG_OVERRIDE)) {
|
|
1565
1602
|
const r = nodeValue(t, undefined, e);
|
|
1566
1603
|
if (r === null || typeof r !== "object") return r;
|
|
1567
1604
|
if (e.s) return serveShallow(e, n, r);
|
|
@@ -1643,7 +1680,7 @@ const traps = {
|
|
|
1643
1680
|
if (n === $OWNER || n === $RECORD) return false;
|
|
1644
1681
|
// (The witness before the pull: a mark on an uninitialized derived
|
|
1645
1682
|
// store is witnessed, then the pull throws — loading, declared pending.)
|
|
1646
|
-
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.
|
|
1683
|
+
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.io(e, n);
|
|
1647
1684
|
if (e.fam !== null) pullFamily(e);
|
|
1648
1685
|
const t = readSource(e, n);
|
|
1649
1686
|
// A tracked reader's presence node answers (born from the two frames;
|
|
@@ -1656,9 +1693,9 @@ const traps = {
|
|
|
1656
1693
|
const o = read(r);
|
|
1657
1694
|
if (!e.ch || t !== e.v) return !!o;
|
|
1658
1695
|
const i = n in t;
|
|
1659
|
-
r.
|
|
1696
|
+
r.Ie = i;
|
|
1660
1697
|
// the link follows the inner store (S4)
|
|
1661
|
-
if (r.
|
|
1698
|
+
if (r.l & CONFIG_OVERRIDE) return !!o;
|
|
1662
1699
|
return i;
|
|
1663
1700
|
} else if (optRead(e)) {
|
|
1664
1701
|
const t = optHooks.has(e, n);
|
|
@@ -1668,14 +1705,14 @@ const traps = {
|
|
|
1668
1705
|
return n in t && !(e.del !== null && t === e.pb && e.del.has(n));
|
|
1669
1706
|
},
|
|
1670
1707
|
ownKeys(e) {
|
|
1671
|
-
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.
|
|
1708
|
+
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.io(e, undefined);
|
|
1672
1709
|
if (e.fam !== null) pullFamily(e);
|
|
1673
1710
|
return visibleKeys(e, enumerationSource(e));
|
|
1674
1711
|
},
|
|
1675
1712
|
getOwnPropertyDescriptor(e, n) {
|
|
1676
1713
|
if (n === $OWNER || n === $RECORD) return undefined;
|
|
1677
1714
|
const t = getObserver();
|
|
1678
|
-
if (verdict !== null && affectsHooks !== null && t === null) affectsHooks.
|
|
1715
|
+
if (verdict !== null && affectsHooks !== null && t === null) affectsHooks.io(e, n);
|
|
1679
1716
|
if (e.fam !== null) pullFamily(e);
|
|
1680
1717
|
// An enumerator (spread, Object.entries) already holds the container
|
|
1681
1718
|
// node and reads its frame; a descriptor read on its own tracks
|
|
@@ -1693,7 +1730,7 @@ const traps = {
|
|
|
1693
1730
|
const t = getHasNode(e, n);
|
|
1694
1731
|
const r = read(t);
|
|
1695
1732
|
i = readSource(e, n, true);
|
|
1696
|
-
if (!r && (!e.ch || t.
|
|
1733
|
+
if (!r && (!e.ch || t.l & CONFIG_OVERRIDE || !(n in i))) return undefined;
|
|
1697
1734
|
} else i = readSource(e, n, true);
|
|
1698
1735
|
let l;
|
|
1699
1736
|
if (!o && optRead(e)) {
|
|
@@ -1709,20 +1746,29 @@ const traps = {
|
|
|
1709
1746
|
set(e, n, t) {
|
|
1710
1747
|
const r = inDraft(e);
|
|
1711
1748
|
const o = !r && writeOverride;
|
|
1712
|
-
if (!r && !o)
|
|
1749
|
+
if (!r && !o) {
|
|
1750
|
+
return true;
|
|
1751
|
+
}
|
|
1713
1752
|
if (n === "__proto__") return true;
|
|
1714
1753
|
// pollution guard (core R30)
|
|
1715
1754
|
const i = e.s ? t : unwrapValue(t);
|
|
1716
1755
|
const l = ensurePB(e);
|
|
1717
|
-
|
|
1756
|
+
// #4146 (R1: node existence is unobservable; #3706: the hold's unit is
|
|
1757
|
+
// the key): a user setter writing a key a HELD container's staging left
|
|
1758
|
+
// unchanged would write it into the held staging — read as the hold's
|
|
1759
|
+
// until its landing, while the same key with a leaf (a live reader)
|
|
1760
|
+
// publishes at the flush. The key gets its leaf first, born from the two
|
|
1761
|
+
// frames (`bornStaged`: a key the hold left unchanged — or added — is
|
|
1762
|
+
// unstaged and held by no one; a key the hold changed is born held, A34
|
|
1763
|
+
// (1)), and the write stages on it (A28; a write by the holder joins it,
|
|
1764
|
+
// A34) — as it would with a reader's leaf. Kept while the container is
|
|
1765
|
+
// held (`deferredReleases`).
|
|
1766
|
+
if (userWrite && e.k?.l & CONFIG_HELD) deferredReleases.add(getNode(e, n));
|
|
1718
1767
|
if (Array.isArray(l)) {
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
t.add(n);
|
|
1722
|
-
t.add("length");
|
|
1723
|
-
}
|
|
1768
|
+
touch(e, n);
|
|
1769
|
+
if (n === "length") e.wk = WK_ALL; else touch(e, "length");
|
|
1724
1770
|
} else {
|
|
1725
|
-
|
|
1771
|
+
touch(e, n);
|
|
1726
1772
|
if (!(n in l)) e.kc++;
|
|
1727
1773
|
}
|
|
1728
1774
|
if (UNSAFE_KEYS.has(n)) {
|
|
@@ -1751,7 +1797,9 @@ const traps = {
|
|
|
1751
1797
|
defineProperty(e, n, t) {
|
|
1752
1798
|
const r = inDraft(e);
|
|
1753
1799
|
const o = !r && writeOverride;
|
|
1754
|
-
if (!r && !o)
|
|
1800
|
+
if (!r && !o) {
|
|
1801
|
+
return true;
|
|
1802
|
+
}
|
|
1755
1803
|
if (n === "__proto__") return true;
|
|
1756
1804
|
if (t.get || t.set) e.a = true;
|
|
1757
1805
|
if ("value" in t) t = {
|
|
@@ -1760,8 +1808,7 @@ const traps = {
|
|
|
1760
1808
|
};
|
|
1761
1809
|
const i = ensurePB(e);
|
|
1762
1810
|
if (e.a || !(t.enumerable && t.writable && t.configurable)) e.sc = 1;
|
|
1763
|
-
|
|
1764
|
-
if (e.wk !== WK_ALL) (e.wk ??= new Set).add(n);
|
|
1811
|
+
touch(e, n);
|
|
1765
1812
|
Object.defineProperty(i, n, t);
|
|
1766
1813
|
if (e.del !== null) e.del.delete(n);
|
|
1767
1814
|
if (o) notifyWrites(e);
|
|
@@ -1770,12 +1817,26 @@ const traps = {
|
|
|
1770
1817
|
deleteProperty(e, n) {
|
|
1771
1818
|
const t = inDraft(e);
|
|
1772
1819
|
const r = !t && writeOverride;
|
|
1773
|
-
if (!t && !r)
|
|
1820
|
+
if (!t && !r) {
|
|
1821
|
+
return true;
|
|
1822
|
+
}
|
|
1774
1823
|
const o = ensurePB(e);
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
delete
|
|
1778
|
-
|
|
1824
|
+
touch(e, n);
|
|
1825
|
+
// The key count (`kc`, the overlay gate and the rebuild cap) follows a
|
|
1826
|
+
// delete the way it follows an add: an own key of the draft leaving — a
|
|
1827
|
+
// clone's key, or an overlay's own add — is one fewer now. An overlay's
|
|
1828
|
+
// override of a committed key is the exception: the committed key's
|
|
1829
|
+
// deletion is recorded in `del` and counted at the rebuild, as before.
|
|
1830
|
+
// Without the decrement the natural "delete the old key, set the new
|
|
1831
|
+
// one" shape drifted the count up by one per write until a three-key
|
|
1832
|
+
// object crossed the overlay threshold and paid a rebuild on every
|
|
1833
|
+
// commit after.
|
|
1834
|
+
const i = e.ovl && hasOwn.call(e.v, n);
|
|
1835
|
+
if (hasOwn.call(o, n)) {
|
|
1836
|
+
delete o[n];
|
|
1837
|
+
i || e.kc--;
|
|
1838
|
+
}
|
|
1839
|
+
if (i) (e.del ??= new Set).add(n);
|
|
1779
1840
|
if (r) notifyWrites(e);
|
|
1780
1841
|
return true;
|
|
1781
1842
|
}
|
|
@@ -1786,11 +1847,12 @@ const traps = {
|
|
|
1786
1847
|
* to the key's presence node or a later add never re-runs it. */ function observerHoldsContainer(e, n) {
|
|
1787
1848
|
const t = e.k;
|
|
1788
1849
|
if (t === null) return false;
|
|
1789
|
-
const r = t.
|
|
1790
|
-
return r !== null && r.
|
|
1850
|
+
const r = t.dt;
|
|
1851
|
+
return r !== null && r.u === n && (!(n.R & REACTIVE_RECOMPUTING_DEPS) || r.Te === n.Ne);
|
|
1791
1852
|
}
|
|
1792
1853
|
|
|
1793
1854
|
function storeSetter(e, n, t = true) {
|
|
1855
|
+
if (writeHook !== null) writeHook(e);
|
|
1794
1856
|
const r = e[$TARGET];
|
|
1795
1857
|
const o = writeScopes;
|
|
1796
1858
|
writeScopes = new Set;
|
|
@@ -1844,21 +1906,21 @@ function storeSetter(e, n, t = true) {
|
|
|
1844
1906
|
function createStore(e, n) {
|
|
1845
1907
|
const t = n?.shallow === true;
|
|
1846
1908
|
const r = wrap(e);
|
|
1909
|
+
// Dependency edges (store/edges.ts): the root is named like a plain signal.
|
|
1910
|
+
if (signalHook !== null) signalHook(r[$TARGET]);
|
|
1847
1911
|
if (t) {
|
|
1848
1912
|
r[$TARGET].s = true;
|
|
1849
1913
|
markRawIngest(e);
|
|
1850
1914
|
}
|
|
1851
1915
|
{
|
|
1852
1916
|
const e = getOwner();
|
|
1853
|
-
|
|
1854
|
-
// `_owner` write itself never reaches the proxy, see storeOwners.
|
|
1855
|
-
registerGraph(r, e);
|
|
1917
|
+
registerGraph(r, e);
|
|
1856
1918
|
// Only once the engine is installed, like the node stamping it feeds: a
|
|
1857
1919
|
// WeakMap.set per fresh store is a growing ephemeron table and,
|
|
1858
1920
|
// disabled, buys nothing.
|
|
1859
1921
|
if (attrHooks !== null) {
|
|
1860
1922
|
storeOwners.set(r[$TARGET], e);
|
|
1861
|
-
nameStore(r, n?.name);
|
|
1923
|
+
nameStore(r, n?.name, n?.loc);
|
|
1862
1924
|
}
|
|
1863
1925
|
}
|
|
1864
1926
|
const setter = e => storeSetter(r, e);
|
|
@@ -1987,7 +2049,7 @@ function snapshotWalk(e, n, t) {
|
|
|
1987
2049
|
// R27: a snapshot sees the batch's pending backing — a held one (another
|
|
1988
2050
|
// transaction's future) only from its own draft.
|
|
1989
2051
|
let i = o.v;
|
|
1990
|
-
if (o.pb !== null && (inDraft(o) || writeOverride || o.k === null || !(o.k.
|
|
2052
|
+
if (o.pb !== null && (inDraft(o) || writeOverride || o.k === null || !(o.k.l & CONFIG_HELD))) {
|
|
1991
2053
|
if (o.ovl) materializePB(o);
|
|
1992
2054
|
i = o.pb;
|
|
1993
2055
|
}
|