@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
|
@@ -2,20 +2,24 @@ import { attrHooks } from "../core/attribution-hooks.js";
|
|
|
2
2
|
|
|
3
3
|
import { CONFIG_MANUAL_WRITE, CONFIG_OVERRIDE, CONFIG_HELD, $REFRESH, NOT_PENDING, STATUS_PENDING, STATUS_ERROR, EFFECT_RENDER, REACTIVE_RECOMPUTING_DEPS, REACTIVE_STAGED_READ, CONFIG_VERDICT, CONFIG_CHILDREN_FORBIDDEN, CONFIG_PROMOTED, REACTIVE_DIRTY, REACTIVE_CHECK } from "../core/constants.js";
|
|
4
4
|
|
|
5
|
-
import { setSignal, isEqual, read, verdict, context, notePromotedWrite, untrack, slotSignal, stagedScreen, stagedRead } from "../core/core.js";
|
|
5
|
+
import { 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, 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
|
|
|
21
|
+
import { optHooks, affectsHooks } from "./hooks.js";
|
|
22
|
+
|
|
19
23
|
import { $TARGET, rawValuesUsed, isRawValue, isWrappable, markRawIngest, writeOverride, markRawOne, $RECORD, $PROXY, $TRACK } from "./types.js";
|
|
20
24
|
|
|
21
25
|
export { markRaw } from "./types.js";
|
|
@@ -49,18 +53,7 @@ export { markRaw } from "./types.js";
|
|
|
49
53
|
* Perf contracts (plan sec. 31.4): lazy nodes; one literal per node; O(written) per
|
|
50
54
|
* setter (`wk`); overlay drafts for large plain records; no new named
|
|
51
55
|
* fields on array targets; zero allocation for adopted-but-unread objects.
|
|
52
|
-
*/
|
|
53
|
-
|
|
54
|
-
function installOptHooks(e) {
|
|
55
|
-
optHooks = e;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
let affectsHooks = null;
|
|
59
|
-
|
|
60
|
-
function installAffectsHooks(e) {
|
|
61
|
-
affectsHooks = e;
|
|
62
|
-
}
|
|
63
|
-
|
|
56
|
+
*/
|
|
64
57
|
/** Key of the deep-witness slot node (never a user key). */ const $DEEP = Symbol(0);
|
|
65
58
|
|
|
66
59
|
// ---------------------------------------------------------------------------
|
|
@@ -179,7 +172,7 @@ function wrap(e, n = null, t = null, r = n?.fam ?? null) {
|
|
|
179
172
|
// adoption swap raw identity without changing the logical value — only
|
|
180
173
|
// changed leaves notify, R9).
|
|
181
174
|
const slotNodeEquals = function(e, n) {
|
|
182
|
-
return isEqual(e, n) || sameLogicalSlot(this.
|
|
175
|
+
return isEqual(e, n) || sameLogicalSlot(this.O, e, n);
|
|
183
176
|
};
|
|
184
177
|
|
|
185
178
|
/** The deep witness: the store decides when it notifies. */ const never = false;
|
|
@@ -217,7 +210,7 @@ function sameKey(e, n) {
|
|
|
217
210
|
/** The container node's comparator: a staging is a backing (identity — a
|
|
218
211
|
* pending backing is never "equal" to the committed one); a lane value is
|
|
219
212
|
* an arrangement (`LaneView`), judged by the optimistic machinery. */ const containerEquals = function(e, n) {
|
|
220
|
-
return e === n || optHooks !== null && optHooks.arrangement(this.
|
|
213
|
+
return e === n || optHooks !== null && optHooks.arrangement(this.O, e, n);
|
|
221
214
|
};
|
|
222
215
|
|
|
223
216
|
function sameLogicalSlot(e, n, t) {
|
|
@@ -230,8 +223,8 @@ function sameLogicalSlot(e, n, t) {
|
|
|
230
223
|
* released at the commit that resolves it (`drainFolds`). */ const deferredReleases = new Set;
|
|
231
224
|
|
|
232
225
|
function releaseSlot(e) {
|
|
233
|
-
const n = e.
|
|
234
|
-
const t = e.
|
|
226
|
+
const n = e.O;
|
|
227
|
+
const t = e.Vt;
|
|
235
228
|
if (t === $TRACK) {
|
|
236
229
|
if (n.k === e) n.k = null;
|
|
237
230
|
} else if (t === $DEEP) {
|
|
@@ -247,17 +240,21 @@ function releaseSlot(e) {
|
|
|
247
240
|
/** A node was born on `target`: a family target enters the live index. */ function noteNode(e, n, t) {
|
|
248
241
|
if (e.fam !== null) e.fam.live.add(e);
|
|
249
242
|
markDescendants(e);
|
|
250
|
-
if (affectsHooks !== null) affectsHooks.
|
|
243
|
+
if (affectsHooks !== null) affectsHooks.fo(e, n, t);
|
|
251
244
|
}
|
|
252
245
|
|
|
253
246
|
// Shared slot-node release handler: registered once; the core sweep
|
|
254
247
|
// dispatches CONFIG_SLOT_NODE nodes here (graph.ts) instead of holding a
|
|
255
248
|
// per-node closure in a per-node NodeExtension. A dropped node is
|
|
256
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
|
+
|
|
257
256
|
setSlotUnobserved(e => {
|
|
258
|
-
|
|
259
|
-
// node holds: defer the release to the commit that resolves it.
|
|
260
|
-
if (e.Ae !== NOT_PENDING || e.u & CONFIG_OVERRIDE) deferredReleases.add(e); else releaseSlot(e);
|
|
257
|
+
if (slotKept(e)) deferredReleases.add(e); else releaseSlot(e);
|
|
261
258
|
});
|
|
262
259
|
|
|
263
260
|
/** A node born while its container has a staging is born from the two
|
|
@@ -265,19 +262,19 @@ setSlotUnobserved(e => {
|
|
|
265
262
|
* pending one — and held by the container's transaction when the container
|
|
266
263
|
* is held (#3706: the unit of the hold is the key — a key the batch left
|
|
267
264
|
* unchanged stages nothing and holds no one). */ function bornStaged(e, n, t, r) {
|
|
268
|
-
if (n.
|
|
265
|
+
if (n.Ye && n.Ye(n.Ie, t)) return;
|
|
269
266
|
queuePendingNode(n);
|
|
270
|
-
n.
|
|
267
|
+
n.ue = t;
|
|
271
268
|
const o = e.k;
|
|
272
269
|
if (o === null) return;
|
|
273
270
|
// a node-less staging holds nothing (`stageOn`)
|
|
274
271
|
// Held by the container's transaction only for a key the HELD staging
|
|
275
272
|
// changed — a mainline layer's change above it stages mainline. (A
|
|
276
273
|
// container carrying a guess is a lane's: the flush's park decides.)
|
|
277
|
-
if ((o.
|
|
274
|
+
if ((o.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && heldKeyChanged(e, r)) holdNode(n, txOf(o));
|
|
278
275
|
// A28 (4): a batch written inside a creation-time pass is promoted — its
|
|
279
276
|
// late-materialized leaf with it.
|
|
280
|
-
if (o.
|
|
277
|
+
if (o.l & CONFIG_PROMOTED) notePromotedWrite(n);
|
|
281
278
|
}
|
|
282
279
|
|
|
283
280
|
function getNode(e, n,
|
|
@@ -302,7 +299,9 @@ t = -1) {
|
|
|
302
299
|
// the hottest store path, and the disabled cost must stay one null
|
|
303
300
|
// check (nodes created before enable() stay generically named).
|
|
304
301
|
if (attrHooks !== null) {
|
|
305
|
-
|
|
302
|
+
const t = storeRoot(e);
|
|
303
|
+
l._name = (storeNames.get(t) ?? "store") + "." + String(n);
|
|
304
|
+
l._loc = storeLocs.get(t);
|
|
306
305
|
stampNodeOwner(l, e);
|
|
307
306
|
}
|
|
308
307
|
r[n] = o;
|
|
@@ -339,8 +338,8 @@ function getHasNode(e, n) {
|
|
|
339
338
|
// pending backing staged — so whoever asked for it reads the frame it
|
|
340
339
|
// would have read had the node been there from the write.
|
|
341
340
|
if (e.pb !== null && !directCommit) {
|
|
342
|
-
n.
|
|
343
|
-
n.
|
|
341
|
+
n.Ie = preBatch(e);
|
|
342
|
+
n.ue = e.pb;
|
|
344
343
|
queuePendingNode(n);
|
|
345
344
|
}
|
|
346
345
|
}
|
|
@@ -356,11 +355,11 @@ function getHasNode(e, n) {
|
|
|
356
355
|
* staging: nothing allocates, nothing is swept at the commit (dbmon's tick
|
|
357
356
|
* re-created, queued, swept and dropped ~7000 container nodes). If the
|
|
358
357
|
* flush parks, the seam materializes and holds the node then
|
|
359
|
-
* (`
|
|
360
|
-
if (e.k === null && passLane === null && (context === null || globalQueue.
|
|
358
|
+
* (`storeParkHook`) — the one case the staging home exists for. */ function stageOn(e, n) {
|
|
359
|
+
if (e.k === null && passLane === null && (context === null || globalQueue.q) && !deriveHolds(e)) return;
|
|
361
360
|
const t = getContainerNode(e);
|
|
362
|
-
if (t.
|
|
363
|
-
t.
|
|
361
|
+
if (t.ue === NOT_PENDING) queuePendingNode(t);
|
|
362
|
+
t.ue = n;
|
|
364
363
|
// A28 (4): a write issued inside a pass outside a flush (a creation-time
|
|
365
364
|
// derive) is promoted at the pass's end — the batch with it.
|
|
366
365
|
if (context !== null) notePromotedWrite(t);
|
|
@@ -370,10 +369,10 @@ function getHasNode(e, n) {
|
|
|
370
369
|
/** The projection's derive is held by a transaction and this is its own
|
|
371
370
|
* write (not a user setter's): the staging is the derive's (`holdWithDerive`). */ function deriveHolds(e) {
|
|
372
371
|
const n = e.fam?.node;
|
|
373
|
-
return n != null && !userWrite && (n.
|
|
372
|
+
return n != null && !userWrite && (n.l & CONFIG_HELD) !== 0;
|
|
374
373
|
}
|
|
375
374
|
|
|
376
|
-
|
|
375
|
+
const storePark = e => {
|
|
377
376
|
for (let n = 0; n < foldList.length; n++) {
|
|
378
377
|
const t = foldList[n];
|
|
379
378
|
if (t.k === null && t.pb !== null) holdNode(getContainerNode(t), e);
|
|
@@ -402,8 +401,9 @@ function bumpDeep(e) {
|
|
|
402
401
|
// on the guess (`laneWrite` → `supersede`, judged by `containerEquals`);
|
|
403
402
|
// otherwise the structural readers are walked (the staging is already on
|
|
404
403
|
// the node; the comparator is the store's, done).
|
|
405
|
-
if (e.
|
|
406
|
-
if (e.
|
|
404
|
+
if (e.l & CONFIG_OVERRIDE) setSignal(e, n); else {
|
|
405
|
+
if (e.l & CONFIG_HELD) joinFuture(txOf(e));
|
|
406
|
+
if (snapshotCaptureActive) captureWriteSnapshot(e, e.Ie);
|
|
407
407
|
insertSubs(e);
|
|
408
408
|
schedule();
|
|
409
409
|
}
|
|
@@ -426,12 +426,17 @@ function stampNodeOwner(e, n) {
|
|
|
426
426
|
|
|
427
427
|
const storeNames = new WeakMap;
|
|
428
428
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
429
|
+
// The store's `loc` option, carried onto every property node beside its
|
|
430
|
+
// label (the same gate: nodes created before the engine is installed carry
|
|
431
|
+
// neither).
|
|
432
|
+
const storeLocs = new WeakMap;
|
|
432
433
|
|
|
433
|
-
function
|
|
434
|
-
|
|
434
|
+
function nameStore(e, n, t) {
|
|
435
|
+
if (attrHooks !== null) {
|
|
436
|
+
const r = e[$TARGET];
|
|
437
|
+
if (n) storeNames.set(r, n);
|
|
438
|
+
if (t) storeLocs.set(r, t);
|
|
439
|
+
}
|
|
435
440
|
}
|
|
436
441
|
|
|
437
442
|
// ---------------------------------------------------------------------------
|
|
@@ -520,7 +525,7 @@ const plainProto = e => {
|
|
|
520
525
|
if (e.kc > OVERLAY_REBUILD_MAX_KEYS) return false;
|
|
521
526
|
if (e.del !== null && e.del.size !== 0) return true;
|
|
522
527
|
const t = e.wk;
|
|
523
|
-
if (t !== null && t !== WK_ALL && t.size <= OVERLAY_REBUILD_MIN_ADDS) return false;
|
|
528
|
+
if (t !== null && t !== WK_ALL && t.u === undefined && t.size <= OVERLAY_REBUILD_MIN_ADDS) return false;
|
|
524
529
|
const r = e.v;
|
|
525
530
|
let o = 0;
|
|
526
531
|
for (const e of Reflect.ownKeys(n)) if (!hasOwn.call(r, e) && ++o > OVERLAY_REBUILD_MIN_ADDS) return true;
|
|
@@ -540,7 +545,7 @@ const plainProto = e => {
|
|
|
540
545
|
e.pb = t;
|
|
541
546
|
e.ovl = false;
|
|
542
547
|
// The staging IS the pending backing: re-point the container node.
|
|
543
|
-
if (e.k !== null && e.k.
|
|
548
|
+
if (e.k !== null && e.k.ue === n) e.k.ue = t;
|
|
544
549
|
return t;
|
|
545
550
|
}
|
|
546
551
|
|
|
@@ -561,7 +566,7 @@ const plainProto = e => {
|
|
|
561
566
|
// mainline setter mid-hold publishes mainline; the held keys stay held).
|
|
562
567
|
n = e.pb = cloneRaw(n, e);
|
|
563
568
|
const t = e.k;
|
|
564
|
-
if (t !== null && !(t.
|
|
569
|
+
if (t !== null && !(t.l & CONFIG_HELD)) t.ue = n;
|
|
565
570
|
} else if (n === null) {
|
|
566
571
|
const t = e.v;
|
|
567
572
|
if (!directCommit) queueFold(e);
|
|
@@ -640,7 +645,7 @@ const plainProto = e => {
|
|
|
640
645
|
} else r = e.v;
|
|
641
646
|
if (directCommit) {
|
|
642
647
|
e.pb = null;
|
|
643
|
-
if (e.k !== null) e.k.
|
|
648
|
+
if (e.k !== null) e.k.Ie = n;
|
|
644
649
|
} else {
|
|
645
650
|
// Eager (INTERNALS §3): the backing swaps now — handlers read the
|
|
646
651
|
// adopted object before the flush — while the container node, when
|
|
@@ -736,7 +741,7 @@ let foldIndex = null;
|
|
|
736
741
|
if (r.pb === null) continue;
|
|
737
742
|
// folded already (a direct commit)
|
|
738
743
|
const o = r.k;
|
|
739
|
-
if (o !== null && o.
|
|
744
|
+
if (o !== null && o.ue !== NOT_PENDING) {
|
|
740
745
|
// held: commits with its hold
|
|
741
746
|
foldList.push(r);
|
|
742
747
|
foldOlds.push(n[t]);
|
|
@@ -747,7 +752,7 @@ let foldIndex = null;
|
|
|
747
752
|
}
|
|
748
753
|
if (deferredReleases.size !== 0) {
|
|
749
754
|
for (const e of deferredReleases) {
|
|
750
|
-
if (e.
|
|
755
|
+
if (e.o === null && !slotKept(e)) {
|
|
751
756
|
deferredReleases.delete(e);
|
|
752
757
|
releaseSlot(e);
|
|
753
758
|
}
|
|
@@ -758,28 +763,33 @@ let foldIndex = null;
|
|
|
758
763
|
/** Core asks: is a slot node's truth in flight? Its family's derive is —
|
|
759
764
|
* lanes (A17, #2864: a guess mid-refetch stands in for the flight) and
|
|
760
765
|
* marks (an ambient `affects()` over a declared reload lives to the
|
|
761
|
-
* landing) read it. */ GlobalQueue.
|
|
762
|
-
const n = e.
|
|
763
|
-
return n != null && n.node !== null && (n.node.
|
|
766
|
+
* landing) read it. */ GlobalQueue.k = e => {
|
|
767
|
+
const n = e.O?.fam;
|
|
768
|
+
return n != null && n.node !== null && (n.node.m & STATUS_PENDING) !== 0;
|
|
764
769
|
};
|
|
765
770
|
|
|
766
771
|
/** Core asks (lanes.ts `covered`): the value a slot node's guess covered.
|
|
767
772
|
* A chained link's committed value is the inner store's at the last
|
|
768
773
|
* read-through — refreshed here, since a guess served the reads since
|
|
769
|
-
* (#3672: the revert compares against the base's live value). */ GlobalQueue.
|
|
770
|
-
const n = e.
|
|
774
|
+
* (#3672: the revert compares against the base's live value). */ GlobalQueue.fn = e => {
|
|
775
|
+
const n = e.O;
|
|
771
776
|
if (n.ch) {
|
|
772
|
-
const t = e.
|
|
777
|
+
const t = e.Vt;
|
|
773
778
|
const r = n.v;
|
|
774
|
-
if (n.h !== null && n.h[t] === e) e.
|
|
779
|
+
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]));
|
|
775
780
|
}
|
|
776
|
-
return e.
|
|
781
|
+
return e.Ie;
|
|
777
782
|
};
|
|
778
783
|
|
|
779
|
-
|
|
784
|
+
installStoreHooks(storePark, () => {
|
|
780
785
|
drainFolds();
|
|
781
786
|
optHooks?.sweep();
|
|
782
|
-
};
|
|
787
|
+
});
|
|
788
|
+
|
|
789
|
+
/** The store half of `affects()` (store/affects.ts) makes its marks' nodes
|
|
790
|
+
* and walks records through these: it never imports the engine. */ GlobalQueue.no = getNode;
|
|
791
|
+
|
|
792
|
+
GlobalQueue.so = isWrappable;
|
|
783
793
|
|
|
784
794
|
/** A projection's creation run commits directly (a memo's first value is
|
|
785
795
|
* its `_value`, not a staging): the draft still writes a clone of the seed,
|
|
@@ -807,7 +817,7 @@ function runDirect(e) {
|
|
|
807
817
|
// the adopted object (shared ownership — never cloned).
|
|
808
818
|
e.pb = null;
|
|
809
819
|
e.wk = null;
|
|
810
|
-
} else if (e.ovl && (e.v !== n || !overlayRebuilds(e, r))) {
|
|
820
|
+
} else if (e.ovl && (e.v !== n || !overlayRebuilds(e, r) && t?.T?.lt !== n)) {
|
|
811
821
|
// Overlay flatten (#3044): the backing keeps its identity, so the
|
|
812
822
|
// `t.v === old` gate below skips path copying (the parent slot
|
|
813
823
|
// already points here) and the adopted-notify (setter
|
|
@@ -821,8 +831,8 @@ function runDirect(e) {
|
|
|
821
831
|
const t = e.v;
|
|
822
832
|
const o = e.wk;
|
|
823
833
|
if (o !== null && o !== WK_ALL) {
|
|
824
|
-
for (
|
|
825
|
-
if (hasOwn.call(r,
|
|
834
|
+
for (let e = o; e !== undefined; e = e.u) for (const n of e) {
|
|
835
|
+
if (hasOwn.call(r, n)) copyOwn(t, r, n); else delete t[n];
|
|
826
836
|
}
|
|
827
837
|
} else {
|
|
828
838
|
for (const e of Reflect.ownKeys(r)) {
|
|
@@ -849,7 +859,7 @@ function runDirect(e) {
|
|
|
849
859
|
// subscribes to was a transient staging home (INTERNALS §5: an
|
|
850
860
|
// observer-less write holds a transient record until the fold,
|
|
851
861
|
// discarded with it) — gone with the batch.
|
|
852
|
-
if (t.
|
|
862
|
+
if (t.o === null) e.k = null; else if (t.Ie instanceof LaneView) t.Ie.base = e.v; else t.Ie = e.v;
|
|
853
863
|
}
|
|
854
864
|
if (e.v !== n && e.u) {
|
|
855
865
|
const t = parentSlotKey(e, n);
|
|
@@ -922,9 +932,9 @@ function reportReplacedContainers(e, n, t, r) {
|
|
|
922
932
|
|
|
923
933
|
function heldDerivation(e, n, t) {
|
|
924
934
|
if (e.fam === null || !userWrite) return false;
|
|
925
|
-
if (n !== undefined) return (n.
|
|
935
|
+
if (n !== undefined) return (n.l & (CONFIG_HELD | CONFIG_MANUAL_WRITE)) === CONFIG_HELD && txOf(n) !== flushTransaction;
|
|
926
936
|
const r = e.k;
|
|
927
|
-
return r !== null && (r.
|
|
937
|
+
return r !== null && (r.l & CONFIG_HELD) !== 0 && txOf(r) !== flushTransaction && heldKeyChanged(e, t);
|
|
928
938
|
}
|
|
929
939
|
|
|
930
940
|
/** Setter exit: one node write per changed observed key (the leaves stage
|
|
@@ -954,7 +964,10 @@ function heldDerivation(e, n, t) {
|
|
|
954
964
|
// value can change when ANY key is written), or a non-plain prototype
|
|
955
965
|
// (class instances: prototype getters derive from arbitrary fields).
|
|
956
966
|
// Overlay pbs chain to the COMMITTED object (#3044) — plainness is the
|
|
957
|
-
// committed container's prototype, not the overlay's.
|
|
967
|
+
// committed container's prototype, not the overlay's. THIS setter's keys
|
|
968
|
+
// only (D-8, #3743): a key an earlier setter of the batch wrote and this
|
|
969
|
+
// one did not is no second proposal — its node was told then, and telling
|
|
970
|
+
// a held node its unchanged value again would join this tick to the hold.
|
|
958
971
|
const o = e.wk;
|
|
959
972
|
const i = o === WK_ALL || e.a === true || !plainProto(e.ovl ? e.v : n) ? null : o;
|
|
960
973
|
if (attrHooks !== null) reportReplacedContainers(e, t, n, i);
|
|
@@ -970,15 +983,18 @@ function heldDerivation(e, n, t) {
|
|
|
970
983
|
if (o.acc === true || hasOwn.call(n, i) && lookupGetter.call(n, i) !== undefined) {
|
|
971
984
|
// Accessor keys: the node is linked for shape-change notification,
|
|
972
985
|
// its value is never served (the getter runs with the proxy
|
|
973
|
-
// receiver on read) — FORCE wakes the readers.
|
|
986
|
+
// receiver on read) — FORCE wakes the readers. A data descriptor
|
|
987
|
+
// falls through so the cleared-acc hot read serves it (#3949);
|
|
988
|
+
// an own getter's descriptor has no `value`, so the compare below
|
|
989
|
+
// would otherwise keep the getter's result (or FORCE) for `undefined`.
|
|
974
990
|
o.acc = isOwnAccessor(n, i);
|
|
975
991
|
const e = Object.getOwnPropertyDescriptor(t, i);
|
|
976
992
|
const r = Object.getOwnPropertyDescriptor(n, i);
|
|
977
|
-
if (
|
|
993
|
+
if (!r || r.get || r.set) {
|
|
978
994
|
if (e?.get !== r?.get || e?.set !== r?.set || e?.value !== r?.value) setSignal(o, () => FORCE);
|
|
979
995
|
continue;
|
|
980
996
|
}
|
|
981
|
-
if (!isEqual(e?.value, r
|
|
997
|
+
if (e?.get || e?.set || !isEqual(e?.value, r.value)) setSignal(o, () => r.value);
|
|
982
998
|
continue;
|
|
983
999
|
}
|
|
984
1000
|
const l = e.del !== null && e.del.has(i) ? undefined : n[i];
|
|
@@ -987,9 +1003,9 @@ function heldDerivation(e, n, t) {
|
|
|
987
1003
|
// The derive's write is a derivation; a user setter's is a proposal
|
|
988
1004
|
// (A34 (3)'s discriminator).
|
|
989
1005
|
if (!userWrite) {
|
|
990
|
-
o.
|
|
1006
|
+
o.l &= ~CONFIG_MANUAL_WRITE;
|
|
991
1007
|
holdWithDerive(e, o);
|
|
992
|
-
} else o.
|
|
1008
|
+
} else o.l |= CONFIG_MANUAL_WRITE;
|
|
993
1009
|
}
|
|
994
1010
|
}
|
|
995
1011
|
}
|
|
@@ -1020,7 +1036,7 @@ function heldDerivation(e, n, t) {
|
|
|
1020
1036
|
// container carrying an arrangement guess is told whether or not anyone
|
|
1021
1037
|
// subscribes: the write is a landing on the guess (`notifyContainer`).
|
|
1022
1038
|
const u = e.k;
|
|
1023
|
-
if (u !== null && (u.
|
|
1039
|
+
if (u !== null && (u.o !== null || u.l & CONFIG_OVERRIDE)) {
|
|
1024
1040
|
let r;
|
|
1025
1041
|
if (e.ovl) {
|
|
1026
1042
|
r = e.del !== null && e.del.size !== 0;
|
|
@@ -1069,13 +1085,14 @@ function membershipChanged(e, n) {
|
|
|
1069
1085
|
e.acc = isOwnAccessor(r, n);
|
|
1070
1086
|
const o = Object.getOwnPropertyDescriptor(t, n);
|
|
1071
1087
|
const i = Object.getOwnPropertyDescriptor(r, n);
|
|
1072
|
-
|
|
1088
|
+
// Data descriptors fall through, as in notifyWrites (#3949).
|
|
1089
|
+
if (!i || i.get || i.set) {
|
|
1073
1090
|
if (o?.get !== i?.get || o?.set !== i?.set || o?.value !== i?.value) setSignal(e, () => FORCE);
|
|
1074
1091
|
return;
|
|
1075
1092
|
}
|
|
1076
1093
|
const l = o?.value;
|
|
1077
1094
|
const u = i?.value;
|
|
1078
|
-
if (!isEqual(l, u) && !targetsEqual(l, u)) setSignal(e, typeof u === "function" ? () => u : u);
|
|
1095
|
+
if (o?.get || o?.set || !isEqual(l, u) && !targetsEqual(l, u)) setSignal(e, typeof u === "function" ? () => u : u);
|
|
1079
1096
|
} else {
|
|
1080
1097
|
const o = t[n];
|
|
1081
1098
|
const i = r[n];
|
|
@@ -1121,7 +1138,7 @@ function hasAccessorFlag(e) {
|
|
|
1121
1138
|
for (const o of Reflect.ownKeys(r)) if (e || o in n !== o in t) setSignal(r[o], o in t);
|
|
1122
1139
|
}
|
|
1123
1140
|
const o = e.k;
|
|
1124
|
-
if (o !== null && (o.
|
|
1141
|
+
if (o !== null && (o.o !== null || o.l & CONFIG_OVERRIDE)) {
|
|
1125
1142
|
const e = Array.isArray(t) && Array.isArray(n) ? arrayStructureChanged(n, t) : membershipChanged(n, t);
|
|
1126
1143
|
if (e) notifyContainer(o, t);
|
|
1127
1144
|
}
|
|
@@ -1178,7 +1195,7 @@ function inDraft(e) {
|
|
|
1178
1195
|
* "adoption is eager by contract" — and the node keeps the frame committed
|
|
1179
1196
|
* readers under a hold still see), `t.v` otherwise. */ function committed(e) {
|
|
1180
1197
|
const n = e.k;
|
|
1181
|
-
if (n !== null) return n.
|
|
1198
|
+
if (n !== null) return n.ue !== NOT_PENDING ? asBacking(n.Ie) : e.v;
|
|
1182
1199
|
// A node-less staging (`stageOn`): the fold queue remembers the frame.
|
|
1183
1200
|
return e.pb !== null ? preBatch(e) : e.v;
|
|
1184
1201
|
}
|
|
@@ -1190,8 +1207,8 @@ function inDraft(e) {
|
|
|
1190
1207
|
* committed frame. */ function stagingReader() {
|
|
1191
1208
|
let e = context;
|
|
1192
1209
|
if (e === null) return null;
|
|
1193
|
-
if (e.
|
|
1194
|
-
return e === null || e.
|
|
1210
|
+
if (e.Pt) e = e.Ht ?? null;
|
|
1211
|
+
return e === null || e.l & CONFIG_CHILDREN_FORBIDDEN ? null : e;
|
|
1195
1212
|
}
|
|
1196
1213
|
|
|
1197
1214
|
/** The backing a read serves (RUL-1, signal parity): the draft and the write
|
|
@@ -1229,10 +1246,10 @@ function inDraft(e) {
|
|
|
1229
1246
|
if (writeOverride && !userWrite) {
|
|
1230
1247
|
const t = n !== undefined ? e.n?.[n] : undefined;
|
|
1231
1248
|
if (t !== undefined) {
|
|
1232
|
-
if ((t.
|
|
1249
|
+
if ((t.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD) joinPassTx(txOf(t));
|
|
1233
1250
|
} else {
|
|
1234
1251
|
const t = e.k;
|
|
1235
|
-
if (t !== null && (t.
|
|
1252
|
+
if (t !== null && (t.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && (n === undefined || heldKeyChanged(e, n))) joinPassTx(txOf(t));
|
|
1236
1253
|
}
|
|
1237
1254
|
}
|
|
1238
1255
|
return r;
|
|
@@ -1244,9 +1261,9 @@ function inDraft(e) {
|
|
|
1244
1261
|
// it).
|
|
1245
1262
|
if (n !== undefined) {
|
|
1246
1263
|
const t = e.n?.[n];
|
|
1247
|
-
if (t !== undefined && t.
|
|
1264
|
+
if (t !== undefined && t.l & CONFIG_OVERRIDE) return committed(e);
|
|
1248
1265
|
}
|
|
1249
|
-
const i = o !== null && (o.
|
|
1266
|
+
const i = o !== null && (o.l & CONFIG_HELD) !== 0;
|
|
1250
1267
|
// A held container: the hold's unit is the key (#3706). A key the HELD
|
|
1251
1268
|
// staging left unchanged reads the ambient view — a mainline layer's
|
|
1252
1269
|
// write above the hold publishes mainline (#3688, #3612); a key it
|
|
@@ -1264,7 +1281,7 @@ function inDraft(e) {
|
|
|
1264
1281
|
// probe) judges the container like any node — for a key the batch
|
|
1265
1282
|
// changed; an unchanged key is committed and final (A22: pending is per
|
|
1266
1283
|
// key). A held container's committed frame is the node's.
|
|
1267
|
-
if (verdict === null || !i && n !== undefined && !keyChanged(e, n, t)) return i ? asBacking(o.
|
|
1284
|
+
if (verdict === null || !i && n !== undefined && !keyChanged(e, n, t)) return i ? asBacking(o.Ie) : e.v;
|
|
1268
1285
|
return o === null ? committed(e) : asBacking(read(o));
|
|
1269
1286
|
}
|
|
1270
1287
|
// A pass reading a key the batch left unchanged: the same value in both
|
|
@@ -1281,13 +1298,13 @@ function inDraft(e) {
|
|
|
1281
1298
|
// frames, a changed one is what the mark already stands for. The
|
|
1282
1299
|
// per-key question is asked once per pass, not once per key (a
|
|
1283
1300
|
// `mapArray` over 1k shifted rows asked it 1k times).
|
|
1284
|
-
if (l.R & REACTIVE_STAGED_READ && !(l.
|
|
1301
|
+
if (l.R & REACTIVE_STAGED_READ && !(l.l & (CONFIG_VERDICT | CONFIG_CHILDREN_FORBIDDEN))) return r;
|
|
1285
1302
|
if (n !== undefined && !keyChanged(e, n, t)) return committed(e);
|
|
1286
1303
|
// A node-less staging (`stageOn`): what `read()` of its node would do
|
|
1287
1304
|
// for this pass — a verdict reader sees the screen, any other derives
|
|
1288
1305
|
// from the staging and is marked as having read it.
|
|
1289
1306
|
if (o === null) {
|
|
1290
|
-
if (l.
|
|
1307
|
+
if (l.l & CONFIG_VERDICT && stagedScreen(l)) return committed(e);
|
|
1291
1308
|
stagedRead(l);
|
|
1292
1309
|
return r;
|
|
1293
1310
|
}
|
|
@@ -1297,11 +1314,52 @@ function inDraft(e) {
|
|
|
1297
1314
|
|
|
1298
1315
|
/** Did the HELD staging (the container node's, not a mainline layer above
|
|
1299
1316
|
* it) change `key`? */ function heldKeyChanged(e, n, t = true) {
|
|
1300
|
-
return changedBetween(e, e.k.
|
|
1317
|
+
return changedBetween(e, e.k.Ie, e.k.ue, n, t) && !adoptChild(e, n);
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
/** The values of a held frame, built once per frame: neither the committed
|
|
1321
|
+
* frame nor the held staging changes while the hold stands (a draft writes a
|
|
1322
|
+
* clone), so the set stays true for every first read of the hold. */ const frameRaws = new WeakMap;
|
|
1323
|
+
|
|
1324
|
+
function rawsOf(e) {
|
|
1325
|
+
let n = frameRaws.get(e);
|
|
1326
|
+
if (n === undefined) frameRaws.set(e, n = new Set(Object.values(e)));
|
|
1327
|
+
return n;
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
/** The same row at a slot, as the adoption pairs rows (reconcile.ts): two
|
|
1331
|
+
* objects whose keys agree — positionally for an unkeyed family. */ function sameRow(e, n, t) {
|
|
1332
|
+
return e !== null && typeof e === "object" && n !== null && typeof n === "object" && (t === null || sameKey(t(e), t(n)));
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
/** A key of a held adoption whose old and new values are the same row that
|
|
1336
|
+
* nothing had proxied when the adoption ran, so `descend` never paired
|
|
1337
|
+
* them: paired now, as it would have been (#3712) — one target, the old
|
|
1338
|
+
* raw its committed frame, the new raw its staging, held by the same
|
|
1339
|
+
* transaction. Both raws resolve to it, so the slot reads as unchanged and
|
|
1340
|
+
* the row's own leaves decide: `id` the same in both frames holds no one,
|
|
1341
|
+
* `version` holds. A record's keys pair by name; an array's by the family's
|
|
1342
|
+
* key — without a family the pairing is not known here and the hold stands. */ function adoptChild(e, n) {
|
|
1343
|
+
const t = e.k;
|
|
1344
|
+
const r = asBacking(t.Ie);
|
|
1345
|
+
const o = r[n];
|
|
1346
|
+
const i = t.ue[n];
|
|
1347
|
+
const l = Array.isArray(r) ? e.fam?.key : null;
|
|
1348
|
+
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 ||
|
|
1349
|
+
// A raw in both frames (the same objects swapped or reordered) is its
|
|
1350
|
+
// own row: pairing it with another would alias the two after the commit.
|
|
1351
|
+
rawsOf(r).has(i) || rawsOf(t.ue).has(o)) return false;
|
|
1352
|
+
// A raw-marked value wraps to itself, a proxy to its own target: neither
|
|
1353
|
+
// is a fresh target around `ov`.
|
|
1354
|
+
const u = wrap(o, e, n)[$TARGET];
|
|
1355
|
+
if (u === undefined || u.v !== o) return false;
|
|
1356
|
+
adoptPB(u, i, false);
|
|
1357
|
+
holdNode(getContainerNode(u), txOf(t));
|
|
1358
|
+
return true;
|
|
1301
1359
|
}
|
|
1302
1360
|
|
|
1303
1361
|
/** A family target's unheld staging, read with no pass (a handler). */ function familyAhead(e) {
|
|
1304
|
-
return e.fam !== null && context === null && !(e.k !== null && e.k.
|
|
1362
|
+
return e.fam !== null && context === null && !(e.k !== null && e.k.l & CONFIG_HELD);
|
|
1305
1363
|
}
|
|
1306
1364
|
|
|
1307
1365
|
/** Did this batch change `key` — value (slot equality), presence, and (for
|
|
@@ -1335,7 +1393,7 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1335
1393
|
* tracked or not, applies the hold and A28 rules a signal's read does. A
|
|
1336
1394
|
* projection's unheld staging is ahead for a handler (`familyAhead`). */ function nodeValue(e, n, t) {
|
|
1337
1395
|
let r = read(e);
|
|
1338
|
-
if (e.
|
|
1396
|
+
if (e.ue !== NOT_PENDING && !(e.l & CONFIG_HELD) && context === null && t.fam !== null) r = e.ue;
|
|
1339
1397
|
return r === FORCE ? n : r;
|
|
1340
1398
|
}
|
|
1341
1399
|
|
|
@@ -1355,8 +1413,8 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1355
1413
|
if (l) {
|
|
1356
1414
|
// The link's committed value follows the inner store's (what a lane
|
|
1357
1415
|
// over it covers — its reveal and revert compare against it, S4).
|
|
1358
|
-
o.
|
|
1359
|
-
if (o.
|
|
1416
|
+
o.Ie = t;
|
|
1417
|
+
if (o.l & CONFIG_OVERRIDE) u = n;
|
|
1360
1418
|
} else u = n;
|
|
1361
1419
|
}
|
|
1362
1420
|
}
|
|
@@ -1377,7 +1435,7 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1377
1435
|
}
|
|
1378
1436
|
|
|
1379
1437
|
/** Is `n` among `c`'s dependencies (any pass's — a kept tail included)? */ function linkedTo(e, n) {
|
|
1380
|
-
for (let t = e.
|
|
1438
|
+
for (let t = e.C; t !== null; t = t.G) if (t.S === n) return true;
|
|
1381
1439
|
return false;
|
|
1382
1440
|
}
|
|
1383
1441
|
|
|
@@ -1396,7 +1454,7 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1396
1454
|
// reader of a memo would — linked, so it re-runs at the landing (the
|
|
1397
1455
|
// next settled pass pulls without linking and the stale link trims), a
|
|
1398
1456
|
// verdict reader registered, a tracked pass suspended.
|
|
1399
|
-
if (n.
|
|
1457
|
+
if (n.m & (STATUS_PENDING | STATUS_ERROR)) {
|
|
1400
1458
|
// A render effect outside the flight's own flush is the frame, not a
|
|
1401
1459
|
// derivation: it keeps what it shows and learns of the landing from the
|
|
1402
1460
|
// leaves the landing changes (unchanged leaves say nothing — it is not a
|
|
@@ -1404,9 +1462,10 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1404
1462
|
// One that already observes the flight (linked by the pass that saw it
|
|
1405
1463
|
// go up — the frame's hold, `blocked`) keeps observing it: a re-run for
|
|
1406
1464
|
// another reason (a guess it read, the seam) that dropped the link would
|
|
1407
|
-
// release the hold with the flight still up.
|
|
1465
|
+
// release the hold with the flight still up. An errored derive has no
|
|
1466
|
+
// landing to learn of: the error is what the frame shows.
|
|
1408
1467
|
const t = context;
|
|
1409
|
-
if (t !== null && t.i === EFFECT_RENDER && n.
|
|
1468
|
+
if (t !== null && t.i === EFFECT_RENDER && !(n.m & STATUS_ERROR) && n.l & CONFIG_HELD && flushTransaction !== txOf(n) && !linkedTo(t, n)) return;
|
|
1410
1469
|
// An optimistic family carrying guesses that stand in for the flight
|
|
1411
1470
|
// (A17): the guesses are the display — a reader is served the leaves
|
|
1412
1471
|
// (committed plus the guesses) and does not suspend on the derive; a
|
|
@@ -1414,7 +1473,7 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1414
1473
|
// up). The family's, not the target's: the edit stands in for the one
|
|
1415
1474
|
// flight the family has.
|
|
1416
1475
|
const r = e.fam.overlaid;
|
|
1417
|
-
if (verdict === null && n.
|
|
1476
|
+
if (verdict === null && n.m & STATUS_PENDING && r !== undefined && r.size !== 0) return;
|
|
1418
1477
|
read(n);
|
|
1419
1478
|
} else untrack(() => read(n));
|
|
1420
1479
|
}
|
|
@@ -1427,12 +1486,12 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1427
1486
|
* classification, as core reads it — `heldNotFinal`). */ function holdWithDerive(e, n) {
|
|
1428
1487
|
const t = e.fam?.node;
|
|
1429
1488
|
if (t !== undefined && t !== null && !userWrite && // the derive's own write (its draft or landing), not a user setter's
|
|
1430
|
-
t.
|
|
1489
|
+
t.l & CONFIG_HELD && n.ue !== NOT_PENDING &&
|
|
1431
1490
|
// A node carrying a guess is the lane's: its staging is the truth beneath
|
|
1432
1491
|
// the guess (`setSignal` → `laneWrite`), never re-homed by the store.
|
|
1433
|
-
!(n.
|
|
1492
|
+
!(n.l & (CONFIG_HELD | CONFIG_OVERRIDE))) {
|
|
1434
1493
|
holdNode(n, txOf(t));
|
|
1435
|
-
n.
|
|
1494
|
+
n.T.ut = t.T.ut;
|
|
1436
1495
|
}
|
|
1437
1496
|
}
|
|
1438
1497
|
|
|
@@ -1475,6 +1534,22 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1475
1534
|
|
|
1476
1535
|
// ---------------------------------------------------------------------------
|
|
1477
1536
|
// traps
|
|
1537
|
+
/** A trap write: the target is told at the setter's exit, the key recorded
|
|
1538
|
+
* in this setter's `wk`. A setter's first write to a target still staged by
|
|
1539
|
+
* an earlier setter of the batch opens a set of its own (the notify visits
|
|
1540
|
+
* it, D-8) and chains the earlier one behind for the fold's union. */ function touch(e, n) {
|
|
1541
|
+
let t = e.wk;
|
|
1542
|
+
if (!pendingNotify.has(e)) {
|
|
1543
|
+
pendingNotify.add(e);
|
|
1544
|
+
if (t !== null && t !== WK_ALL) {
|
|
1545
|
+
t = new Set;
|
|
1546
|
+
t.u = e.wk;
|
|
1547
|
+
e.wk = t;
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
if (t !== WK_ALL) (e.wk ??= new Set).add(n);
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1478
1553
|
const traps = {
|
|
1479
1554
|
get(e, n, t) {
|
|
1480
1555
|
// One typeof gates every brand-symbol compare off the hot string path.
|
|
@@ -1503,7 +1578,7 @@ const traps = {
|
|
|
1503
1578
|
}
|
|
1504
1579
|
// (The witness before the pull: a mark on an uninitialized derived
|
|
1505
1580
|
// store is witnessed, then the pull throws — loading, declared pending.)
|
|
1506
|
-
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.
|
|
1581
|
+
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.ro(e, n);
|
|
1507
1582
|
if (e.fam !== null) pullFamily(e);
|
|
1508
1583
|
// Hot inline case: existing PLAIN node (non-accessor) read outside any
|
|
1509
1584
|
// draft — the dbmon/uibench effect re-read shape. Core's `read()` serves
|
|
@@ -1518,7 +1593,7 @@ const traps = {
|
|
|
1518
1593
|
const r = e.n?.[n];
|
|
1519
1594
|
if (writeScopes === null && !writeOverride) {
|
|
1520
1595
|
const t = r;
|
|
1521
|
-
if (t !== undefined && t.acc !== true && (!e.ch || t.
|
|
1596
|
+
if (t !== undefined && t.acc !== true && (!e.ch || t.l & CONFIG_OVERRIDE)) {
|
|
1522
1597
|
const r = nodeValue(t, undefined, e);
|
|
1523
1598
|
if (r === null || typeof r !== "object") return r;
|
|
1524
1599
|
if (e.s) return serveShallow(e, n, r);
|
|
@@ -1600,7 +1675,7 @@ const traps = {
|
|
|
1600
1675
|
if (n === $OWNER || n === $RECORD) return false;
|
|
1601
1676
|
// (The witness before the pull: a mark on an uninitialized derived
|
|
1602
1677
|
// store is witnessed, then the pull throws — loading, declared pending.)
|
|
1603
|
-
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.
|
|
1678
|
+
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.ro(e, n);
|
|
1604
1679
|
if (e.fam !== null) pullFamily(e);
|
|
1605
1680
|
const t = readSource(e, n);
|
|
1606
1681
|
// A tracked reader's presence node answers (born from the two frames;
|
|
@@ -1613,9 +1688,9 @@ const traps = {
|
|
|
1613
1688
|
const o = read(r);
|
|
1614
1689
|
if (!e.ch || t !== e.v) return !!o;
|
|
1615
1690
|
const i = n in t;
|
|
1616
|
-
r.
|
|
1691
|
+
r.Ie = i;
|
|
1617
1692
|
// the link follows the inner store (S4)
|
|
1618
|
-
if (r.
|
|
1693
|
+
if (r.l & CONFIG_OVERRIDE) return !!o;
|
|
1619
1694
|
return i;
|
|
1620
1695
|
} else if (optRead(e)) {
|
|
1621
1696
|
const t = optHooks.has(e, n);
|
|
@@ -1625,14 +1700,14 @@ const traps = {
|
|
|
1625
1700
|
return n in t && !(e.del !== null && t === e.pb && e.del.has(n));
|
|
1626
1701
|
},
|
|
1627
1702
|
ownKeys(e) {
|
|
1628
|
-
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.
|
|
1703
|
+
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.ro(e, undefined);
|
|
1629
1704
|
if (e.fam !== null) pullFamily(e);
|
|
1630
1705
|
return visibleKeys(e, enumerationSource(e));
|
|
1631
1706
|
},
|
|
1632
1707
|
getOwnPropertyDescriptor(e, n) {
|
|
1633
1708
|
if (n === $OWNER || n === $RECORD) return undefined;
|
|
1634
1709
|
const t = getObserver();
|
|
1635
|
-
if (verdict !== null && affectsHooks !== null && t === null) affectsHooks.
|
|
1710
|
+
if (verdict !== null && affectsHooks !== null && t === null) affectsHooks.ro(e, n);
|
|
1636
1711
|
if (e.fam !== null) pullFamily(e);
|
|
1637
1712
|
// An enumerator (spread, Object.entries) already holds the container
|
|
1638
1713
|
// node and reads its frame; a descriptor read on its own tracks
|
|
@@ -1650,7 +1725,7 @@ const traps = {
|
|
|
1650
1725
|
const t = getHasNode(e, n);
|
|
1651
1726
|
const r = read(t);
|
|
1652
1727
|
i = readSource(e, n, true);
|
|
1653
|
-
if (!r && (!e.ch || t.
|
|
1728
|
+
if (!r && (!e.ch || t.l & CONFIG_OVERRIDE || !(n in i))) return undefined;
|
|
1654
1729
|
} else i = readSource(e, n, true);
|
|
1655
1730
|
let l;
|
|
1656
1731
|
if (!o && optRead(e)) {
|
|
@@ -1671,15 +1746,22 @@ const traps = {
|
|
|
1671
1746
|
// pollution guard (core R30)
|
|
1672
1747
|
const i = e.s ? t : unwrapValue(t);
|
|
1673
1748
|
const l = ensurePB(e);
|
|
1674
|
-
|
|
1749
|
+
// #4146 (R1: node existence is unobservable; #3706: the hold's unit is
|
|
1750
|
+
// the key): a user setter writing a key a HELD container's staging left
|
|
1751
|
+
// unchanged would write it into the held staging — read as the hold's
|
|
1752
|
+
// until its landing, while the same key with a leaf (a live reader)
|
|
1753
|
+
// publishes at the flush. The key gets its leaf first, born from the two
|
|
1754
|
+
// frames (`bornStaged`: a key the hold left unchanged — or added — is
|
|
1755
|
+
// unstaged and held by no one; a key the hold changed is born held, A34
|
|
1756
|
+
// (1)), and the write stages on it (A28; a write by the holder joins it,
|
|
1757
|
+
// A34) — as it would with a reader's leaf. Kept while the container is
|
|
1758
|
+
// held (`deferredReleases`).
|
|
1759
|
+
if (userWrite && e.k?.l & CONFIG_HELD) deferredReleases.add(getNode(e, n));
|
|
1675
1760
|
if (Array.isArray(l)) {
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
t.add(n);
|
|
1679
|
-
t.add("length");
|
|
1680
|
-
}
|
|
1761
|
+
touch(e, n);
|
|
1762
|
+
if (n === "length") e.wk = WK_ALL; else touch(e, "length");
|
|
1681
1763
|
} else {
|
|
1682
|
-
|
|
1764
|
+
touch(e, n);
|
|
1683
1765
|
if (!(n in l)) e.kc++;
|
|
1684
1766
|
}
|
|
1685
1767
|
if (UNSAFE_KEYS.has(n)) {
|
|
@@ -1717,8 +1799,7 @@ const traps = {
|
|
|
1717
1799
|
};
|
|
1718
1800
|
const i = ensurePB(e);
|
|
1719
1801
|
if (e.a || !(t.enumerable && t.writable && t.configurable)) e.sc = 1;
|
|
1720
|
-
|
|
1721
|
-
if (e.wk !== WK_ALL) (e.wk ??= new Set).add(n);
|
|
1802
|
+
touch(e, n);
|
|
1722
1803
|
Object.defineProperty(i, n, t);
|
|
1723
1804
|
if (e.del !== null) e.del.delete(n);
|
|
1724
1805
|
if (o) notifyWrites(e);
|
|
@@ -1729,10 +1810,22 @@ const traps = {
|
|
|
1729
1810
|
const r = !t && writeOverride;
|
|
1730
1811
|
if (!t && !r) return true;
|
|
1731
1812
|
const o = ensurePB(e);
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
delete
|
|
1735
|
-
|
|
1813
|
+
touch(e, n);
|
|
1814
|
+
// The key count (`kc`, the overlay gate and the rebuild cap) follows a
|
|
1815
|
+
// delete the way it follows an add: an own key of the draft leaving — a
|
|
1816
|
+
// clone's key, or an overlay's own add — is one fewer now. An overlay's
|
|
1817
|
+
// override of a committed key is the exception: the committed key's
|
|
1818
|
+
// deletion is recorded in `del` and counted at the rebuild, as before.
|
|
1819
|
+
// Without the decrement the natural "delete the old key, set the new
|
|
1820
|
+
// one" shape drifted the count up by one per write until a three-key
|
|
1821
|
+
// object crossed the overlay threshold and paid a rebuild on every
|
|
1822
|
+
// commit after.
|
|
1823
|
+
const i = e.ovl && hasOwn.call(e.v, n);
|
|
1824
|
+
if (hasOwn.call(o, n)) {
|
|
1825
|
+
delete o[n];
|
|
1826
|
+
i || e.kc--;
|
|
1827
|
+
}
|
|
1828
|
+
if (i) (e.del ??= new Set).add(n);
|
|
1736
1829
|
if (r) notifyWrites(e);
|
|
1737
1830
|
return true;
|
|
1738
1831
|
}
|
|
@@ -1743,11 +1836,12 @@ const traps = {
|
|
|
1743
1836
|
* to the key's presence node or a later add never re-runs it. */ function observerHoldsContainer(e, n) {
|
|
1744
1837
|
const t = e.k;
|
|
1745
1838
|
if (t === null) return false;
|
|
1746
|
-
const r = t.
|
|
1747
|
-
return r !== null && r.
|
|
1839
|
+
const r = t.dt;
|
|
1840
|
+
return r !== null && r.u === n && (!(n.R & REACTIVE_RECOMPUTING_DEPS) || r.Te === n.Ne);
|
|
1748
1841
|
}
|
|
1749
1842
|
|
|
1750
1843
|
function storeSetter(e, n, t = true) {
|
|
1844
|
+
if (writeHook !== null) writeHook(e);
|
|
1751
1845
|
const r = e[$TARGET];
|
|
1752
1846
|
const o = writeScopes;
|
|
1753
1847
|
writeScopes = new Set;
|
|
@@ -1801,6 +1895,8 @@ function storeSetter(e, n, t = true) {
|
|
|
1801
1895
|
function createStore(e, n) {
|
|
1802
1896
|
const t = n?.shallow === true;
|
|
1803
1897
|
const r = wrap(e);
|
|
1898
|
+
// Dependency edges (store/edges.ts): the root is named like a plain signal.
|
|
1899
|
+
if (signalHook !== null) signalHook(r[$TARGET]);
|
|
1804
1900
|
if (t) {
|
|
1805
1901
|
r[$TARGET].s = true;
|
|
1806
1902
|
markRawIngest(e);
|
|
@@ -1815,7 +1911,7 @@ function createStore(e, n) {
|
|
|
1815
1911
|
// disabled, buys nothing.
|
|
1816
1912
|
if (attrHooks !== null) {
|
|
1817
1913
|
storeOwners.set(r[$TARGET], e);
|
|
1818
|
-
nameStore(r, n?.name);
|
|
1914
|
+
nameStore(r, n?.name, n?.loc);
|
|
1819
1915
|
}
|
|
1820
1916
|
}
|
|
1821
1917
|
const setter = e => storeSetter(r, e);
|
|
@@ -1944,7 +2040,7 @@ function snapshotWalk(e, n, t) {
|
|
|
1944
2040
|
// R27: a snapshot sees the batch's pending backing — a held one (another
|
|
1945
2041
|
// transaction's future) only from its own draft.
|
|
1946
2042
|
let i = o.v;
|
|
1947
|
-
if (o.pb !== null && (inDraft(o) || writeOverride || o.k === null || !(o.k.
|
|
2043
|
+
if (o.pb !== null && (inDraft(o) || writeOverride || o.k === null || !(o.k.l & CONFIG_HELD))) {
|
|
1948
2044
|
if (o.ovl) materializePB(o);
|
|
1949
2045
|
i = o.pb;
|
|
1950
2046
|
}
|
|
@@ -1996,4 +2092,4 @@ function snapshotWalk(e, n, t) {
|
|
|
1996
2092
|
return i ?? r;
|
|
1997
2093
|
}
|
|
1998
2094
|
|
|
1999
|
-
export { $PROXY, $TARGET, $TRACK, LaneView, adoptPB,
|
|
2095
|
+
export { $PROXY, $TARGET, $TRACK, LaneView, adoptPB, arrayStructureChanged, asBacking, bumpDeep, cloneRaw, committed, containerEquals, createStore, deep, getContainerNode, getHasNode, getNode, hasAccessorFlag, materializePB, membershipChanged, nameStore, notifyFold, notifyFoldTail, notifyKeyDiff, notifyKeyValue, resolveChainedRaw, runDirect, sameKey, sameRow, snapshot, storeHasFamily, storeHasOptimisticFamily, storeIsShallow, storeSetter, targetsEqual, unwrapValue, userWriting, wrap };
|