@solidjs/signals 2.0.0-rc.13 → 2.0.0-rc.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dev-shared.js +2059 -3768
- package/dist/dev.attribution.js +237 -101
- package/dist/dev.js +7466 -8497
- package/dist/observe/affects.js +128 -104
- package/dist/observe/boundaries.js +492 -665
- package/dist/observe/core/action.js +63 -66
- package/dist/observe/core/async.js +243 -379
- package/dist/observe/core/attribution-queries.js +1 -1
- package/dist/observe/core/attribution.js +394 -253
- package/dist/observe/core/constants.js +120 -187
- package/dist/observe/core/context.js +3 -3
- package/dist/observe/core/core.js +874 -1440
- package/dist/observe/core/dev.js +3 -3
- package/dist/observe/core/effect.js +73 -84
- package/dist/observe/core/external.js +5 -5
- package/dist/observe/core/graph.js +62 -71
- package/dist/observe/core/heap.js +75 -98
- package/dist/observe/core/invariants.js +1 -34
- package/dist/observe/core/lanes.js +750 -181
- package/dist/observe/core/owner.js +80 -101
- package/dist/observe/core/scheduler.js +719 -1356
- package/dist/observe/core/verdict.js +314 -621
- package/dist/observe/flatten.js +79 -0
- package/dist/observe/index.js +15 -13
- package/dist/observe/map.js +232 -235
- package/dist/observe/reveal.js +212 -0
- package/dist/observe/signals.js +144 -216
- package/dist/observe/store/affects.js +142 -0
- package/dist/observe/store/hooks.js +13 -0
- package/dist/observe/store/index.js +27 -24
- package/dist/observe/store/optimistic.js +571 -0
- package/dist/observe/store/projection.js +308 -0
- package/dist/observe/store/reconcile.js +259 -0
- package/dist/observe/store/store.js +1988 -277
- package/dist/observe/store/storePath.js +1 -1
- package/dist/{prod/store/next → observe/store}/target.js +17 -18
- package/dist/observe/store/types.js +73 -0
- package/dist/observe/store/utils.js +1 -13
- package/dist/prod/affects.js +130 -106
- package/dist/prod/boundaries.js +476 -654
- package/dist/prod/core/action.js +71 -74
- package/dist/prod/core/async.js +306 -444
- package/dist/prod/core/constants.js +117 -186
- package/dist/prod/core/context.js +3 -3
- package/dist/prod/core/core.js +888 -1462
- package/dist/prod/core/effect.js +88 -99
- package/dist/prod/core/external.js +5 -5
- package/dist/prod/core/graph.js +62 -71
- package/dist/prod/core/heap.js +75 -98
- package/dist/prod/core/invariants.js +1 -32
- package/dist/prod/core/lanes.js +712 -181
- package/dist/prod/core/owner.js +75 -96
- package/dist/prod/core/scheduler.js +686 -1335
- package/dist/prod/core/verdict.js +309 -621
- package/dist/prod/flatten.js +77 -0
- package/dist/prod/index.js +15 -13
- package/dist/prod/map.js +142 -145
- package/dist/prod/reveal.js +212 -0
- package/dist/prod/signals.js +155 -227
- package/dist/prod/store/affects.js +142 -0
- package/dist/prod/store/hooks.js +13 -0
- package/dist/prod/store/index.js +26 -19
- package/dist/prod/store/optimistic.js +566 -0
- package/dist/prod/store/projection.js +301 -0
- package/dist/prod/store/reconcile.js +259 -0
- package/dist/prod/store/store.js +1883 -272
- package/dist/prod/store/storePath.js +1 -1
- package/dist/{observe/store/next → prod/store}/target.js +13 -17
- package/dist/prod/store/types.js +73 -0
- package/dist/prod/store/utils.js +1 -11
- package/dist/types/affects.d.ts +11 -30
- package/dist/types/boundaries.d.ts +66 -176
- package/dist/types/carved.d.ts +1 -0
- package/dist/types/core/action.d.ts +5 -6
- package/dist/types/core/async.d.ts +10 -5
- package/dist/types/core/attribution-hooks.d.ts +20 -1
- package/dist/types/core/attribution.d.ts +26 -11
- package/dist/types/core/constants.d.ts +130 -180
- package/dist/types/core/core.d.ts +134 -264
- package/dist/types/core/dev.d.ts +2 -2
- package/dist/types/core/effect.d.ts +0 -7
- package/dist/types/core/graph.d.ts +7 -1
- package/dist/types/core/heap.d.ts +5 -7
- package/dist/types/core/index.d.ts +5 -5
- package/dist/types/core/invariants.d.ts +0 -29
- package/dist/types/core/lanes.d.ts +74 -86
- package/dist/types/core/owner.d.ts +3 -3
- package/dist/types/core/scheduler.d.ts +255 -331
- package/dist/types/core/types.d.ts +45 -85
- package/dist/types/core/verdict.d.ts +3 -0
- package/dist/types/flatten.d.ts +8 -0
- package/dist/types/index.d.ts +6 -4
- package/dist/types/reveal.d.ts +19 -0
- package/dist/types/signals.d.ts +35 -91
- package/dist/types/store/affects.d.ts +12 -0
- package/dist/types/store/hooks.d.ts +48 -0
- package/dist/types/store/index.d.ts +34 -15
- package/dist/types/store/optimistic.d.ts +23 -0
- package/dist/types/store/projection.d.ts +18 -0
- package/dist/types/store/reconcile.d.ts +3 -0
- package/dist/types/store/store.d.ts +132 -145
- package/dist/types/store/storePath.d.ts +1 -1
- package/dist/types/store/target.d.ts +151 -0
- package/dist/types/store/types.d.ts +84 -0
- package/package.json +1 -1
- package/dist/observe/core/optimistic.js +0 -669
- package/dist/observe/store/next/optimistic.js +0 -717
- package/dist/observe/store/next/projection.js +0 -266
- package/dist/observe/store/next/reconcile.js +0 -375
- package/dist/observe/store/next/store.js +0 -2608
- package/dist/prod/core/optimistic.js +0 -658
- package/dist/prod/store/next/optimistic.js +0 -710
- package/dist/prod/store/next/projection.js +0 -257
- package/dist/prod/store/next/reconcile.js +0 -373
- package/dist/prod/store/next/store.js +0 -2484
- package/dist/types/core/optimistic.d.ts +0 -6
- package/dist/types/store/next/optimistic.d.ts +0 -25
- package/dist/types/store/next/projection.d.ts +0 -27
- package/dist/types/store/next/reconcile.d.ts +0 -10
- package/dist/types/store/next/store.d.ts +0 -145
- package/dist/types/store/next/target.d.ts +0 -196
|
@@ -1,331 +1,2042 @@
|
|
|
1
|
-
import "../core/
|
|
1
|
+
import { attrHooks } from "../core/attribution-hooks.js";
|
|
2
2
|
|
|
3
|
-
import "../core/
|
|
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 {
|
|
5
|
+
import { setSignal, isEqual, read, verdict, snapshotCaptureActive, captureWriteSnapshot, context, notePromotedWrite, untrack, slotSignal, stagedScreen, stagedRead } from "../core/core.js";
|
|
6
6
|
|
|
7
|
-
import "../core/
|
|
7
|
+
import { registerGraph } from "../core/dev.js";
|
|
8
8
|
|
|
9
|
-
import "../core/
|
|
9
|
+
import { setSlotUnobserved } from "../core/graph.js";
|
|
10
10
|
|
|
11
|
-
import "../core/
|
|
11
|
+
import { getOwner, getObserver } from "../core/owner.js";
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import { insertIntoHeap } from "../core/heap.js";
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
// foreign — where asking `$TARGET` first and then each view kind was two
|
|
25
|
-
// to four trap hops (see `viewOf`).
|
|
26
|
-
$RECORD = Symbol(0), // Node-map slot carrying a record-level `affects()` mark: any read through
|
|
27
|
-
// the record witnesses it into the active isPending() probe.
|
|
28
|
-
$AFFECTS = Symbol(0);
|
|
29
|
-
|
|
30
|
-
// Structural field names of store targets (StoreNextTarget aliases these, so
|
|
31
|
-
// shared machinery — affects walks, tests — reads targets via the consts).
|
|
32
|
-
const STORE_VALUE = "v", STORE_NODE = "n", STORE_HAS = "h";
|
|
33
|
-
|
|
34
|
-
function lookupTarget(e, t) {
|
|
35
|
-
// Family registrations (projections/optimistic) first, then the global
|
|
36
|
-
// next lookup. Proxies resolve through $TARGET directly.
|
|
37
|
-
return (t ? lookupTarget$1(e, t) : undefined) ?? lookupTarget$1(e, null);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Values marked raw never acquire a proxy identity: wrap() serves them as-is
|
|
41
|
-
// everywhere — deep stores hold them as leaf values replaced by reference.
|
|
42
|
-
// Once raw, always raw (identity stays single, just unwrapped). Consulted
|
|
43
|
-
// only on wrap-creation and ingest paths; reads never touch it.
|
|
44
|
-
const rawValues = new WeakSet;
|
|
15
|
+
import { txOf, flushTransaction, holdNode, joinFuture, insertSubs, schedule, passLane, globalQueue, queuePendingNode, GlobalQueue, joinPassTx, dirtyQueue } from "../core/scheduler.js";
|
|
16
|
+
|
|
17
|
+
import { lookupTarget, storeOwners, setLookup, $OWNER, isOwned, markDescendants, devAssertNeverUserMutation } from "./target.js";
|
|
18
|
+
|
|
19
|
+
import { optHooks, affectsHooks } from "./hooks.js";
|
|
20
|
+
|
|
21
|
+
import { $TARGET, rawValuesUsed, isRawValue, isWrappable, markRawIngest, writeOverride, markRawOne, $RECORD, $PROXY, $TRACK } from "./types.js";
|
|
22
|
+
|
|
23
|
+
export { markRaw } from "./types.js";
|
|
45
24
|
|
|
46
25
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
26
|
+
* Store — plain stores on L2 (plan sec. 31, S1).
|
|
27
|
+
*
|
|
28
|
+
* A store is a tree of L2 nodes and nothing else carries reactive state:
|
|
29
|
+
*
|
|
30
|
+
* - **Leaf nodes** (one per tracked-or-written key, lazy; `slotSignal`, one
|
|
31
|
+
* literal): `_value` committed, `_pendingValue` this batch's staging.
|
|
32
|
+
* - **Presence nodes** (`in` tracks presence, not value — R13) and the
|
|
33
|
+
* **deep witness** (`deep()` subscribes one node per record).
|
|
34
|
+
* - The **container node** `k` (plan sec. 31.3, Q-A): the `$TRACK` node given a
|
|
35
|
+
* value. `_value` is the committed backing, `_pendingValue` the pending
|
|
36
|
+
* backing the draft mutates — so a write to a key nobody reads has a
|
|
37
|
+
* staging home the scheduler owns, and park/commit/revert of the backing
|
|
38
|
+
* is the hold model's, not a fold ledger of the store's. Structural
|
|
39
|
+
* readers (`ownKeys`, iteration, `$TRACK`, `length`, `deep`) subscribe to
|
|
40
|
+
* it; value readers never do — a key with no leaf reads through the
|
|
41
|
+
* container's visible backing without subscribing (R1: node existence is
|
|
42
|
+
* unobservable).
|
|
43
|
+
*
|
|
44
|
+
* The committed backing (`t.v`) is owned raw (CoW: a user object is shared
|
|
45
|
+
* until the first write privatizes it and its ancestor chain). It mutates at
|
|
46
|
+
* exactly one moment — the flush's commit (`_storeCommit`), after the
|
|
47
|
+
* container node committed its staging. Reads: context-free readers see the
|
|
48
|
+
* committed backing until the flush; a pass sees the staging (signal
|
|
49
|
+
* parity, RUL-1); the draft reads its own staging.
|
|
50
|
+
*
|
|
51
|
+
* Perf contracts (plan sec. 31.4): lazy nodes; one literal per node; O(written) per
|
|
52
|
+
* setter (`wk`); overlay drafts for large plain records; no new named
|
|
53
|
+
* fields on array targets; zero allocation for adopted-but-unread objects.
|
|
52
54
|
*/
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
// proxy
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
55
|
+
/** Key of the deep-witness slot node (never a user key). */ const $DEEP = Symbol(0);
|
|
56
|
+
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// wrap / dedupe
|
|
59
|
+
/** Pre-shaped constructor for OBJECT proxy targets: V8 tips a bare `{}` into
|
|
60
|
+
* dictionary mode once ~19 named properties are assigned onto it (the #3044
|
|
61
|
+
* `ovl`/`del` fields crossed that line — every trap's field read became a
|
|
62
|
+
* hash lookup, a 15% deep-dbmon tick regression). Declaring every field in a
|
|
63
|
+
* constructor pre-allocates in-object slots so the map stays fast, with
|
|
64
|
+
* headroom for future fields. The prototype is reset to `Object.prototype`
|
|
65
|
+
* so proxy-forwarded semantics (getPrototypeOf, constructor) are exactly a
|
|
66
|
+
* plain object's. Array targets keep the bare-`[]` path — they must carry
|
|
67
|
+
* the array exotic class for `Array.isArray(proxy)`.
|
|
68
|
+
*
|
|
69
|
+
* ARRAY SHAPE RULE: arrays normalize their named properties to dictionary
|
|
70
|
+
* mode as the count grows (V8 13.x: counts ≡ 0 mod 3 from 18 up), so the
|
|
71
|
+
* target's named field count is capped at 20 — any future write-side state
|
|
72
|
+
* beyond `wk` must ride an extension object (see target.ts), never new
|
|
73
|
+
* named fields here. */ function TargetShape() {
|
|
74
|
+
this.v = undefined;
|
|
75
|
+
this.ch = undefined;
|
|
76
|
+
this.pb = undefined;
|
|
77
|
+
this.n = undefined;
|
|
78
|
+
this.h = undefined;
|
|
79
|
+
this.k = undefined;
|
|
80
|
+
this.dk = undefined;
|
|
81
|
+
this.u = undefined;
|
|
82
|
+
this.pk = undefined;
|
|
83
|
+
this.px = undefined;
|
|
84
|
+
this.d = undefined;
|
|
85
|
+
this.a = undefined;
|
|
86
|
+
this.sc = undefined;
|
|
87
|
+
this.kc = undefined;
|
|
88
|
+
this.nc = undefined;
|
|
89
|
+
this.fam = undefined;
|
|
90
|
+
this.s = undefined;
|
|
91
|
+
this.ovl = undefined;
|
|
92
|
+
this.del = undefined;
|
|
93
|
+
this.wk = undefined;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
TargetShape.prototype = Object.prototype;
|
|
97
|
+
|
|
98
|
+
function createTarget(e, n, t, r = n?.fam ?? null) {
|
|
99
|
+
// The proxy target carries the array exotic class when the value is an
|
|
100
|
+
// array, so Array.isArray(proxy) is true; the fields live on it directly.
|
|
101
|
+
// Direct field assignment in one fixed order (no Object.assign literal
|
|
102
|
+
// copy): every target shares a hidden-class transition chain — createTarget
|
|
103
|
+
// was the #2 store cost in the uibench creation profile.
|
|
104
|
+
const o = Array.isArray(e) ? [] : new TargetShape;
|
|
105
|
+
o.v = e;
|
|
106
|
+
// Chained-backing flag (backing IS another store's proxy, §7b) — cached so
|
|
107
|
+
// the hot read path never does a per-read symbol lookup on the backing.
|
|
108
|
+
o.ch = e[$TARGET] !== undefined;
|
|
109
|
+
o.pb = null;
|
|
110
|
+
o.n = null;
|
|
111
|
+
o.h = null;
|
|
112
|
+
o.k = null;
|
|
113
|
+
o.dk = null;
|
|
114
|
+
o.wk = null;
|
|
115
|
+
o.u = n;
|
|
116
|
+
o.pk = t;
|
|
117
|
+
o.px = null;
|
|
118
|
+
o.d = false;
|
|
119
|
+
o.a = false;
|
|
120
|
+
o.sc = 0;
|
|
121
|
+
o.kc = 0;
|
|
122
|
+
o.nc = 0;
|
|
123
|
+
o.fam = r;
|
|
124
|
+
o.s = false;
|
|
125
|
+
o.ovl = false;
|
|
126
|
+
o.del = null;
|
|
127
|
+
o.px = new Proxy(o, traps);
|
|
128
|
+
setLookup(e, o);
|
|
129
|
+
return o;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function wrap(e, n = null, t = null, r = n?.fam ?? null) {
|
|
133
|
+
// markRaw'd values never wrap through ANY store (R42; sticky raw-marking
|
|
134
|
+
// is one half of the never-both-wrapped-and-raw invariant, RUL-12).
|
|
135
|
+
if (rawValuesUsed && isRawValue(e)) return e;
|
|
136
|
+
const o = lookupTarget(e, r);
|
|
137
|
+
if (o !== undefined) return o.px;
|
|
138
|
+
const i = e[$TARGET];
|
|
139
|
+
if (i !== undefined && i.px === e) {
|
|
140
|
+
// Foreign-family proxies re-wrap into THIS family (writes stay isolated);
|
|
141
|
+
// same-family and plain-store proxies pass through.
|
|
142
|
+
if (r === null || i.fam === r) return e;
|
|
143
|
+
return createTarget(e, n, t, r).px;
|
|
144
|
+
}
|
|
145
|
+
return createTarget(e, n, t, r).px;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Unwrap our own proxies to their current backing; leave everything else.
|
|
149
|
+
* A projection draft wrapper is registered as its target's alias
|
|
150
|
+
* (projection.ts `wrapDraft`) — adopted as a backing, it would forward to
|
|
151
|
+
* itself (#3767). */ function unwrapValue(e) {
|
|
152
|
+
if (e == null || typeof e !== "object") return e;
|
|
153
|
+
const n = e[$TARGET];
|
|
154
|
+
if (n !== undefined && (n.px === e || lookupTarget(e, n.fam) === n) && n.v !== undefined) {
|
|
155
|
+
// A draft escaping into other storage must be a REAL container that
|
|
156
|
+
// becomes this target's committed backing at fold (the shared-raw
|
|
157
|
+
// contract) — a prototype overlay is neither.
|
|
158
|
+
if (n.ovl) materializePB(n);
|
|
159
|
+
return n.pb ?? n.v;
|
|
160
|
+
}
|
|
161
|
+
return e;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ---------------------------------------------------------------------------
|
|
165
|
+
// nodes: pure subscription points (values used only for equality gating)
|
|
166
|
+
// Shared slot-node equality (create-floor diet): ONE function for every
|
|
167
|
+
// leaf — `this` is the node (method-call convention at every _equals site),
|
|
168
|
+
// `_host` is the baked-in target backref. Logical-slot equality: values
|
|
169
|
+
// resolving to the same child target are the same slot (privatization/
|
|
170
|
+
// adoption swap raw identity without changing the logical value — only
|
|
171
|
+
// changed leaves notify, R9).
|
|
172
|
+
const slotNodeEquals = function(e, n) {
|
|
173
|
+
return isEqual(e, n) || sameLogicalSlot(this.yt, e, n);
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/** The deep witness: the store decides when it notifies. */ const never = false;
|
|
177
|
+
|
|
178
|
+
function sameKey(e, n) {
|
|
179
|
+
return e === n || e !== e && n !== n;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** The container node's arrangement guess: the rows (or keys) the setter
|
|
183
|
+
* left the container with, in order. Its comparator judges a landing or a
|
|
184
|
+
* revert against it by row identity — by key when the family has one
|
|
185
|
+
* (the server's object for a guessed row is the same row). */ class LaneView {
|
|
186
|
+
rows;
|
|
187
|
+
base;
|
|
188
|
+
removed;
|
|
189
|
+
constructor(e,
|
|
190
|
+
/** The backing the guess is shown over: the committed one at the
|
|
191
|
+
* guess, the composed arrangement after a re-base (optimistic.ts). */
|
|
192
|
+
n,
|
|
193
|
+
/** Keys the setter REMOVED from the view it saw (a deliberate delete,
|
|
194
|
+
* not a row the truth has yet to show): a re-base does not restore
|
|
195
|
+
* them. `null` when none. */
|
|
196
|
+
t = null) {
|
|
197
|
+
this.rows = e;
|
|
198
|
+
this.base = n;
|
|
199
|
+
this.removed = t;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** A container node's value as a backing: the base an arrangement guess is
|
|
204
|
+
* shown over, else the value itself. */ function asBacking(e) {
|
|
205
|
+
return e instanceof LaneView ? e.base : e;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** The container node's comparator: a staging is a backing (identity — a
|
|
209
|
+
* pending backing is never "equal" to the committed one); a lane value is
|
|
210
|
+
* an arrangement (`LaneView`), judged by the optimistic machinery. */ const containerEquals = function(e, n) {
|
|
211
|
+
return e === n || optHooks !== null && optHooks.arrangement(this.yt, e, n);
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
function sameLogicalSlot(e, n, t) {
|
|
215
|
+
if (n === null || typeof n !== "object" || t === null || typeof t !== "object") return false;
|
|
216
|
+
const r = lookupTarget(n, e.fam);
|
|
217
|
+
return r !== undefined && r === lookupTarget(t, e.fam);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** Slot nodes whose last subscriber left while they still held a staging:
|
|
221
|
+
* released at the commit that resolves it (`drainFolds`). */ const deferredReleases = new Set;
|
|
222
|
+
|
|
223
|
+
function releaseSlot(e) {
|
|
224
|
+
const n = e.yt;
|
|
225
|
+
const t = e.bt;
|
|
226
|
+
if (t === $TRACK) {
|
|
227
|
+
if (n.k === e) n.k = null;
|
|
228
|
+
} else if (t === $DEEP) {
|
|
229
|
+
if (n.dk === e) n.dk = null;
|
|
230
|
+
} else if (n.n !== null && n.n[t] === e) {
|
|
231
|
+
delete n.n[t];
|
|
232
|
+
n.nc--;
|
|
233
|
+
} else if (n.h !== null && n.h[t] === e) delete n.h[t];
|
|
234
|
+
// The family's index keeps only targets that still carry a node.
|
|
235
|
+
if (n.fam !== null && n.nc === 0 && n.k === null && n.dk === null && (n.h === null || Reflect.ownKeys(n.h).length === 0)) n.fam.live.delete(n);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** A node was born on `target`: a family target enters the live index. */ function noteNode(e, n, t) {
|
|
239
|
+
if (e.fam !== null) e.fam.live.add(e);
|
|
240
|
+
markDescendants(e);
|
|
241
|
+
if (affectsHooks !== null) affectsHooks.fo(e, n, t);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Shared slot-node release handler: registered once; the core sweep
|
|
245
|
+
// dispatches CONFIG_SLOT_NODE nodes here (graph.ts) instead of holding a
|
|
246
|
+
// per-node closure in a per-node NodeExtension. A dropped node is
|
|
247
|
+
// unreachable through the store — a fresh read makes a fresh node.
|
|
248
|
+
setSlotUnobserved(e => {
|
|
249
|
+
// A staged write — or a lane's value (a guess, S4) — is state only the
|
|
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);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
/** A node born while its container has a staging is born from the two
|
|
255
|
+
* frames (A29): committed from the committed backing, the staging from the
|
|
256
|
+
* pending one — and held by the container's transaction when the container
|
|
257
|
+
* is held (#3706: the unit of the hold is the key — a key the batch left
|
|
258
|
+
* unchanged stages nothing and holds no one). */ function bornStaged(e, n, t, r) {
|
|
259
|
+
if (n.en && n.en(n.Ae, t)) return;
|
|
260
|
+
queuePendingNode(n);
|
|
261
|
+
n.Oe = t;
|
|
262
|
+
const o = e.k;
|
|
263
|
+
if (o === null) return;
|
|
264
|
+
// a node-less staging holds nothing (`stageOn`)
|
|
265
|
+
// Held by the container's transaction only for a key the HELD staging
|
|
266
|
+
// 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
|
+
if ((o.i & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && heldKeyChanged(e, r)) holdNode(n, txOf(o));
|
|
269
|
+
// A28 (4): a batch written inside a creation-time pass is promoted — its
|
|
270
|
+
// late-materialized leaf with it.
|
|
271
|
+
if (o.i & CONFIG_PROMOTED) notePromotedWrite(n);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function getNode(e, n,
|
|
275
|
+
// First-read dedupe (create-floor slice 2): the get trap probes
|
|
276
|
+
// accessor-ness right before creating the node — pass the verdict through
|
|
277
|
+
// so creation skips the second descriptor scan. -1 = unknown (other
|
|
278
|
+
// callers), 0/1 = probed.
|
|
279
|
+
t = -1) {
|
|
280
|
+
const r = e.n ??= Object.create(null);
|
|
281
|
+
let o = r[n];
|
|
110
282
|
if (o === undefined) {
|
|
111
|
-
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
283
|
+
// Accessor-ness resolved ONCE per node (no per-object descriptor scan
|
|
284
|
+
// on reads): accessor keys serve through Reflect.get with the proxy
|
|
285
|
+
// receiver.
|
|
286
|
+
const i = e.pb;
|
|
287
|
+
const l = o = slotSignal(committed(e)[n], slotNodeEquals, e, n, t === -1 ? isOwnAccessor(i ?? e.v, n) : t === 1);
|
|
288
|
+
if (i !== null && !l.acc) bornStaged(e, l, e.del !== null && e.del.has(n) ? undefined : i[n], n);
|
|
289
|
+
// Attribution-only: name store property nodes by path segment so
|
|
290
|
+
// attribution chains and wide-scope warnings read "store.todos" (or
|
|
291
|
+
// "todos.title" when the store was declared with a name), not
|
|
292
|
+
// "signal". Gated on the engine being installed — node creation is
|
|
293
|
+
// the hottest store path, and the disabled cost must stay one null
|
|
294
|
+
// check (nodes created before enable() stay generically named).
|
|
295
|
+
if (attrHooks !== null) {
|
|
296
|
+
l._name = storeLabel(e) + "." + String(n);
|
|
297
|
+
stampNodeOwner(l, e);
|
|
298
|
+
}
|
|
299
|
+
r[n] = o;
|
|
300
|
+
e.nc++;
|
|
301
|
+
noteNode(e, l, n);
|
|
117
302
|
}
|
|
118
303
|
return o;
|
|
119
304
|
}
|
|
120
305
|
|
|
121
|
-
|
|
306
|
+
function getHasNode(e, n) {
|
|
307
|
+
const t = e.h ??= Object.create(null);
|
|
308
|
+
let r = t[n];
|
|
309
|
+
if (r === undefined) {
|
|
310
|
+
const o = r = slotSignal(n in committed(e), isEqual, e, n, false);
|
|
311
|
+
const i = e.pb;
|
|
312
|
+
if (i !== null) bornStaged(e, o, n in i && !(e.del !== null && e.del.has(n)), n);
|
|
313
|
+
if (attrHooks !== null) stampNodeOwner(o, e);
|
|
314
|
+
t[n] = r;
|
|
315
|
+
noteNode(e, o, n);
|
|
316
|
+
}
|
|
317
|
+
return r;
|
|
318
|
+
}
|
|
122
319
|
|
|
123
|
-
|
|
124
|
-
|
|
320
|
+
/** The container node (plan sec. 31.3): created by the first structural subscription
|
|
321
|
+
* or the first write that needs to stage. Its committed value is the
|
|
322
|
+
* committed backing. */ function getContainerNode(e) {
|
|
323
|
+
let n = e.k;
|
|
324
|
+
if (n === null) {
|
|
325
|
+
n = e.k = slotSignal(e.v, containerEquals, e, $TRACK, false);
|
|
326
|
+
if (attrHooks !== null) stampNodeOwner(n, e);
|
|
327
|
+
noteNode(e, n, $TRACK);
|
|
328
|
+
// Born while a batch is staged without a node (`stageOn`): the node
|
|
329
|
+
// takes the staging as its own — the pre-batch backing committed, the
|
|
330
|
+
// pending backing staged — so whoever asked for it reads the frame it
|
|
331
|
+
// would have read had the node been there from the write.
|
|
332
|
+
if (e.pb !== null && !directCommit) {
|
|
333
|
+
n.Ae = preBatch(e);
|
|
334
|
+
n.Oe = e.pb;
|
|
335
|
+
queuePendingNode(n);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return n;
|
|
125
339
|
}
|
|
126
340
|
|
|
127
|
-
|
|
128
|
-
|
|
341
|
+
/** Q-A's escape hatch (plan sec. 33; measured plan sec. 41.4): a staging needs the container
|
|
342
|
+
* node as its home only when something can read the frame through it — a
|
|
343
|
+
* structural subscriber (the node exists), lane work (the staging is the
|
|
344
|
+
* lane's), a pass outside a flush (its write is promoted at the pass's end,
|
|
345
|
+
* A28 (4)), or a derive a transaction holds (the staging is the derive's,
|
|
346
|
+
* `holdWithDerive`). Otherwise the backing and the fold queue are the whole
|
|
347
|
+
* staging: nothing allocates, nothing is swept at the commit (dbmon's tick
|
|
348
|
+
* re-created, queued, swept and dropped ~7000 container nodes). If the
|
|
349
|
+
* flush parks, the seam materializes and holds the node then
|
|
350
|
+
* (`_storePark`) — the one case the staging home exists for. */ function stageOn(e, n) {
|
|
351
|
+
if (e.k === null && passLane === null && (context === null || globalQueue.X) && !deriveHolds(e)) return;
|
|
352
|
+
const t = getContainerNode(e);
|
|
353
|
+
if (t.Oe === NOT_PENDING) queuePendingNode(t);
|
|
354
|
+
t.Oe = n;
|
|
355
|
+
// A28 (4): a write issued inside a pass outside a flush (a creation-time
|
|
356
|
+
// derive) is promoted at the pass's end — the batch with it.
|
|
357
|
+
if (context !== null) notePromotedWrite(t);
|
|
358
|
+
if (e.fam !== null) holdWithDerive(e, t);
|
|
129
359
|
}
|
|
130
360
|
|
|
131
|
-
|
|
132
|
-
function
|
|
133
|
-
|
|
361
|
+
/** The projection's derive is held by a transaction and this is its own
|
|
362
|
+
* write (not a user setter's): the staging is the derive's (`holdWithDerive`). */ function deriveHolds(e) {
|
|
363
|
+
const n = e.fam?.node;
|
|
364
|
+
return n != null && !userWrite && (n.i & CONFIG_HELD) !== 0;
|
|
134
365
|
}
|
|
135
366
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
367
|
+
GlobalQueue.An = e => {
|
|
368
|
+
for (let n = 0; n < foldList.length; n++) {
|
|
369
|
+
const t = foldList[n];
|
|
370
|
+
if (t.k === null && t.pb !== null) holdNode(getContainerNode(t), e);
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
function getDeepNode(e) {
|
|
375
|
+
let n = e.dk;
|
|
376
|
+
if (n === null) {
|
|
377
|
+
n = e.dk = slotSignal(0, never, e, $DEEP, false);
|
|
378
|
+
if (attrHooks !== null) stampNodeOwner(n, e);
|
|
379
|
+
noteNode(e, n, $DEEP);
|
|
380
|
+
}
|
|
381
|
+
return n;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function bumpDeep(e) {
|
|
385
|
+
if (e.dk !== null) setSignal(e.dk, 1);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/** The structural readers' one notification: a membership/arrangement
|
|
389
|
+
* change on the container (the staging is already on the node; `setSignal`
|
|
390
|
+
* re-stages the same backing and walks the subscribers — and, on later
|
|
391
|
+
* steps, routes a held or lane-owned container the way any node is). */ function notifyContainer(e, n) {
|
|
392
|
+
// A container carrying an arrangement guess takes the write as a landing
|
|
393
|
+
// on the guess (`laneWrite` → `supersede`, judged by `containerEquals`);
|
|
394
|
+
// otherwise the structural readers are walked (the staging is already on
|
|
395
|
+
// the node; the comparator is the store's, done).
|
|
396
|
+
if (e.i & CONFIG_OVERRIDE) setSignal(e, n); else {
|
|
397
|
+
if (e.i & CONFIG_HELD) joinFuture(txOf(e));
|
|
398
|
+
if (snapshotCaptureActive) captureWriteSnapshot(e, e.Ae);
|
|
399
|
+
insertSubs(e);
|
|
400
|
+
schedule();
|
|
149
401
|
}
|
|
150
402
|
}
|
|
151
403
|
|
|
152
|
-
|
|
404
|
+
// Observe-tier labels and owners (attribution).
|
|
405
|
+
function storeRoot(e) {
|
|
406
|
+
let n = e;
|
|
407
|
+
while (n.u !== null) n = n.u;
|
|
408
|
+
return n;
|
|
409
|
+
}
|
|
153
410
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
411
|
+
function storeRootOwner(e) {
|
|
412
|
+
return storeOwners.get(storeRoot(e));
|
|
413
|
+
}
|
|
157
414
|
|
|
158
|
-
function
|
|
159
|
-
|
|
415
|
+
function stampNodeOwner(e, n) {
|
|
416
|
+
e.L = storeRootOwner(n) ?? null;
|
|
160
417
|
}
|
|
161
418
|
|
|
162
|
-
|
|
163
|
-
* pushed before the declaration are in motion too — legacy reads its write
|
|
164
|
-
* overlays; next composes armed-node overrides). */ let nextOptimisticViewResolver = null;
|
|
419
|
+
const storeNames = new WeakMap;
|
|
165
420
|
|
|
166
|
-
function
|
|
167
|
-
|
|
421
|
+
function nameStore(e, n) {
|
|
422
|
+
if (attrHooks !== null && n) storeNames.set(e[$TARGET], n);
|
|
168
423
|
}
|
|
169
424
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return affectsScopes.size > 0;
|
|
425
|
+
function storeLabel(e) {
|
|
426
|
+
return storeNames.get(storeRoot(e)) ?? "store";
|
|
173
427
|
}
|
|
174
428
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
429
|
+
// ---------------------------------------------------------------------------
|
|
430
|
+
// clones (CoW privatization)
|
|
431
|
+
const hasOwn = Object.prototype.hasOwnProperty;
|
|
432
|
+
|
|
433
|
+
const lookupGetter = Object.prototype.__lookupGetter__;
|
|
434
|
+
|
|
435
|
+
const lookupSetter = Object.prototype.__lookupSetter__;
|
|
436
|
+
|
|
437
|
+
const propertyIsEnumerable = Object.prototype.propertyIsEnumerable;
|
|
438
|
+
|
|
439
|
+
function isOwnAccessor(e, n) {
|
|
440
|
+
return hasOwn.call(e, n) && (lookupGetter.call(e, n) !== undefined || lookupSetter.call(e, n) !== undefined);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/** Shallow clone into store ownership. Plain data (grade 2) spreads; anything
|
|
444
|
+
* else copies descriptors (accessors kept, everything made configurable). */ function cloneRaw(e, n) {
|
|
445
|
+
n.sc || scanAccessorsOnce(n);
|
|
446
|
+
if (n.sc === 2) {
|
|
447
|
+
const t = {
|
|
448
|
+
...e
|
|
449
|
+
};
|
|
450
|
+
t[$OWNER] = n;
|
|
451
|
+
return t;
|
|
452
|
+
}
|
|
453
|
+
const t = Object.getOwnPropertyDescriptors(e);
|
|
454
|
+
for (const r of Reflect.ownKeys(t)) {
|
|
455
|
+
const o = t[r];
|
|
456
|
+
if (r === "length" && Array.isArray(e)) continue;
|
|
457
|
+
o.configurable = true;
|
|
458
|
+
if (!o.get && !o.set) o.writable = true; else n.a = true;
|
|
459
|
+
}
|
|
460
|
+
const r = Array.isArray(e) ? Object.defineProperties([], t) : Object.create(Object.getPrototypeOf(e), t);
|
|
461
|
+
r[$OWNER] = n;
|
|
462
|
+
return r;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/** Wide plain clone (many keys): null-prototype build, then reset. */ function wideClone(e, n) {
|
|
466
|
+
const t = Object.create(null);
|
|
467
|
+
for (const n of Reflect.ownKeys(e)) t[n] = e[n];
|
|
468
|
+
Object.setPrototypeOf(t, Object.prototype);
|
|
469
|
+
t[$OWNER] = n;
|
|
470
|
+
return t;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function copyOwn(e, n, t) {
|
|
474
|
+
const r = Object.getOwnPropertyDescriptor(n, t);
|
|
475
|
+
if (r.get || r.set || !r.enumerable || !r.writable || !r.configurable) Object.defineProperty(e, t, r); else e[t] = r.value;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/** One descriptor scan per container (grade 1/2, `a`, `kc`). */ function scanAccessorsOnce(e) {
|
|
479
|
+
const n = e.v;
|
|
480
|
+
const t = Reflect.ownKeys(n);
|
|
481
|
+
let r = Object.getPrototypeOf(n) === Object.prototype;
|
|
482
|
+
for (const o of t) {
|
|
483
|
+
if (lookupGetter.call(n, o) !== undefined || lookupSetter.call(n, o) !== undefined) {
|
|
484
|
+
e.a = true;
|
|
485
|
+
r = false;
|
|
486
|
+
break;
|
|
225
487
|
}
|
|
488
|
+
if (r && !propertyIsEnumerable.call(n, o)) r = false;
|
|
489
|
+
}
|
|
490
|
+
e.sc = r ? 2 : 1;
|
|
491
|
+
e.kc = t.length;
|
|
492
|
+
return !e.a;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// ---------------------------------------------------------------------------
|
|
496
|
+
// the pending backing
|
|
497
|
+
const OVERLAY_MIN_KEYS = 32;
|
|
498
|
+
|
|
499
|
+
const OVERLAY_REBUILD_MAX_KEYS = 1024;
|
|
500
|
+
|
|
501
|
+
const OVERLAY_REBUILD_MIN_ADDS = 16;
|
|
502
|
+
|
|
503
|
+
const WK_ALL = new Set;
|
|
504
|
+
|
|
505
|
+
const plainProto = e => {
|
|
506
|
+
const n = Object.getPrototypeOf(e);
|
|
507
|
+
return n === Object.prototype || n === Array.prototype || n === null;
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
/** An overlay with many adds (or any delete) rebuilds the committed
|
|
511
|
+
* container at flatten so its shape stays fast (#3689). */ function overlayRebuilds(e, n) {
|
|
512
|
+
if (e.kc > OVERLAY_REBUILD_MAX_KEYS) return false;
|
|
513
|
+
if (e.del !== null && e.del.size !== 0) return true;
|
|
514
|
+
const t = e.wk;
|
|
515
|
+
if (t !== null && t !== WK_ALL && t.size <= OVERLAY_REBUILD_MIN_ADDS) return false;
|
|
516
|
+
const r = e.v;
|
|
517
|
+
let o = 0;
|
|
518
|
+
for (const e of Reflect.ownKeys(n)) if (!hasOwn.call(r, e) && ++o > OVERLAY_REBUILD_MIN_ADDS) return true;
|
|
519
|
+
return false;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/** Downgrade an overlay to a real clone (a consumer needs a container). */ function materializePB(e) {
|
|
523
|
+
const n = e.pb;
|
|
524
|
+
if (!e.ovl) return n;
|
|
525
|
+
const t = e.sc === 2 ? wideClone(e.v, e) : cloneRaw(e.v, e);
|
|
526
|
+
if (e.del !== null) {
|
|
527
|
+
for (const n of e.del) delete t[n];
|
|
528
|
+
e.kc -= e.del.size;
|
|
529
|
+
e.del = null;
|
|
530
|
+
}
|
|
531
|
+
for (const r of Reflect.ownKeys(n)) e.sc === 2 ? t[r] = n[r] : copyOwn(t, n, r);
|
|
532
|
+
e.pb = t;
|
|
533
|
+
e.ovl = false;
|
|
534
|
+
// The staging IS the pending backing: re-point the container node.
|
|
535
|
+
if (e.k !== null && e.k.Oe === n) e.k.Oe = t;
|
|
536
|
+
return t;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/** The batch's pending backing, created on the first draft write: an overlay
|
|
540
|
+
* of an owned plain-data record above the key threshold, a clone otherwise.
|
|
541
|
+
* Staged on the container node (the scheduler owns its lifetime) and queued
|
|
542
|
+
* for the fold at the flush's commit. */ function ensurePB(e) {
|
|
543
|
+
let n = e.pb;
|
|
544
|
+
// An optimistic edit composes on the view the user saw and becomes
|
|
545
|
+
// guesses at the setter's exit: nothing is staged, nothing folds
|
|
546
|
+
// (optimistic.ts).
|
|
547
|
+
if (userWrite && e.fam?.opt === true && !pendingNotify.has(e)) return e.pb = optHooks.draft(e);
|
|
548
|
+
if (n !== null && !isOwned(n)) {
|
|
549
|
+
// An adopted object awaiting its fold is the user's: the draft writes a
|
|
550
|
+
// clone of it. The clone is the batch's staging — unless the adoption is
|
|
551
|
+
// HELD (another transaction's): then the held staging stays the
|
|
552
|
+
// adoption and the draft is a mainline layer above it (#3612/#3688: a
|
|
553
|
+
// mainline setter mid-hold publishes mainline; the held keys stay held).
|
|
554
|
+
n = e.pb = cloneRaw(n, e);
|
|
555
|
+
const t = e.k;
|
|
556
|
+
if (t !== null && !(t.i & CONFIG_HELD)) t.Oe = n;
|
|
557
|
+
} else if (n === null) {
|
|
558
|
+
const t = e.v;
|
|
559
|
+
if (!directCommit) queueFold(e);
|
|
560
|
+
if (!e.ch && !Array.isArray(t) && (e.sc !== 0 ? !e.a : scanAccessorsOnce(e)) && e.kc > OVERLAY_MIN_KEYS && isOwned(t)) {
|
|
561
|
+
n = e.pb = Object.create(t);
|
|
562
|
+
e.ovl = true;
|
|
563
|
+
} else n = e.pb = cloneRaw(t, e);
|
|
564
|
+
if (!directCommit) stageOn(e, n);
|
|
565
|
+
}
|
|
566
|
+
return n;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/** Privatize `target`'s committed backing (and its ancestor chain — a shared
|
|
570
|
+
* parent cannot point at an owned child). Not a reactive event. */ function privatizeCommitted(e) {
|
|
571
|
+
if (isOwned(e.v)) return;
|
|
572
|
+
const n = e.v;
|
|
573
|
+
const t = cloneRaw(n, e);
|
|
574
|
+
// Mid-batch (a staging is open on this target) the fold still sees the
|
|
575
|
+
// backing the batch started from: the queue recorded it (`foldOlds`).
|
|
576
|
+
e.v = t;
|
|
577
|
+
e.ch = false;
|
|
578
|
+
if (e.u) {
|
|
579
|
+
privatizeCommitted(e.u);
|
|
580
|
+
devAssertNeverUserMutation(e.u.v);
|
|
581
|
+
const t = e.u.v;
|
|
582
|
+
const r = parentSlotKey(e, n);
|
|
583
|
+
if (t[r] === n) t[r] = e.v;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/** The key this target sits under in its parent — re-resolved by identity
|
|
588
|
+
* when an array parent moved it (#3282). Not searched when the slot already
|
|
589
|
+
* holds this target's backing (a parent adopted whole carries its children's
|
|
590
|
+
* new raws before the children fold — the miss was O(rows) per changed row
|
|
591
|
+
* of a keyed reconcile). */ function parentSlotKey(e, n) {
|
|
592
|
+
const t = e.pk;
|
|
593
|
+
const r = e.u.v;
|
|
594
|
+
if (r[t] === n || r[t] === e.v || !Array.isArray(r)) return t;
|
|
595
|
+
const o = r.indexOf(n);
|
|
596
|
+
if (o === -1) return t;
|
|
597
|
+
e.pk = o;
|
|
598
|
+
return o;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/** Overlay flatten (#3044): own keys onto the owned committed backing in
|
|
602
|
+
* place — the backing keeps its identity. */ function flattenOverlay(e, n) {
|
|
603
|
+
privatizeCommitted(e);
|
|
604
|
+
const t = e.v;
|
|
605
|
+
for (const r of Reflect.ownKeys(n)) e.sc === 2 ? t[r] = n[r] : copyOwn(t, n, r);
|
|
606
|
+
if (e.del !== null) {
|
|
607
|
+
for (const n of e.del) delete t[n];
|
|
608
|
+
e.kc -= e.del.size;
|
|
609
|
+
e.del = null;
|
|
610
|
+
}
|
|
611
|
+
e.pb = null;
|
|
612
|
+
e.ovl = false;
|
|
613
|
+
e.wk = null;
|
|
614
|
+
// written-keys window closes with the commit
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/** Adoption (a setter's returned replacement; reconcile and projection
|
|
618
|
+
* landings in S3): the incoming object becomes the backing by reference at
|
|
619
|
+
* the fold — no clones, no user-object mutation, ownership resets to
|
|
620
|
+
* shared. It is the container's staging until then (signal parity: a
|
|
621
|
+
* handler reads the committed frame until the flush; a hold keeps it
|
|
622
|
+
* staged with the rest). The nodes are told now, diffed against the view
|
|
623
|
+
* they were LAST told (#3296): the draft's pending backing when one
|
|
624
|
+
* preceded the adoption this batch — its node writes were real, and the
|
|
625
|
+
* adoption's re-stage cancels or confirms each — else the committed
|
|
626
|
+
* backing. */ function adoptPB(e, n, t = true) {
|
|
627
|
+
if (!directCommit) queueFold(e);
|
|
628
|
+
let r;
|
|
629
|
+
if (e.pb !== null) {
|
|
630
|
+
if (e.ovl) materializePB(e);
|
|
631
|
+
r = e.pb;
|
|
632
|
+
} else r = e.v;
|
|
633
|
+
if (directCommit) {
|
|
634
|
+
e.pb = null;
|
|
635
|
+
if (e.k !== null) e.k.Ae = n;
|
|
226
636
|
} else {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
637
|
+
// Eager (INTERNALS §3): the backing swaps now — handlers read the
|
|
638
|
+
// adopted object before the flush — while the container node, when
|
|
639
|
+
// there is one (`stageOn`), keeps the committed frame (`_value`) for
|
|
640
|
+
// readers a hold keeps on it, and stages the adoption for the fold's
|
|
641
|
+
// path copy.
|
|
642
|
+
e.pb = n;
|
|
643
|
+
stageOn(e, n);
|
|
644
|
+
}
|
|
645
|
+
e.v = n;
|
|
646
|
+
e.ch = n[$TARGET] !== undefined;
|
|
647
|
+
e.ovl = false;
|
|
648
|
+
e.del = null;
|
|
649
|
+
e.sc = 0;
|
|
650
|
+
e.a = false;
|
|
651
|
+
e.wk = null;
|
|
652
|
+
// adoption supersedes staged trap writes
|
|
653
|
+
const o = n[$OWNER];
|
|
654
|
+
if (o !== undefined && o.fam === e.fam) n[$OWNER] = e; else setLookup(n, e);
|
|
655
|
+
if (t && r !== n) notifyFold(e, r, n);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// ---------------------------------------------------------------------------
|
|
659
|
+
// the fold: owned raw's one mutation point, at the flush's commit
|
|
660
|
+
/** The fold queue: the targets with a batch open (`pb !== null` IS the
|
|
661
|
+
* membership — a target is queued exactly while it carries a staging; the
|
|
662
|
+
* drain skips one already folded), in two reusable arrays. Nothing here
|
|
663
|
+
* allocates per batch once warm: a dbmon tick queues ~7000 containers, and
|
|
664
|
+
* a map built and drained per tick cost ~540 KB of table allocation — most
|
|
665
|
+
* of the tick's young-generation GC, and the whole of its gap to `next`
|
|
666
|
+
* (plan sec. 41.5).
|
|
667
|
+
*
|
|
668
|
+
* Beside each target, its PRE-BATCH committed backing — `t.v` as the batch
|
|
669
|
+
* opened on it (`foldOlds`, parallel to `foldList`). That one record is the
|
|
670
|
+
* fold's base in every case: a draft's backing does not move until the fold
|
|
671
|
+
* (so the record is `t.v` still), an adoption swaps it eagerly (the record
|
|
672
|
+
* is the frame committed readers keep), a mid-batch privatization or a
|
|
673
|
+
* draft over an adopted raw moves it again (the record is what the batch
|
|
674
|
+
* started from, which is all the fold wants to know). The record lives
|
|
675
|
+
* exactly as long as the batch: a weak map keyed by target kept every
|
|
676
|
+
* container's LAST adopted-away backing alive until its next adoption —
|
|
677
|
+
* for a keyed reconcile, the whole previous tree promoted out of the
|
|
678
|
+
* nursery every tick (the saturated listened-paths shape ran 15–25% over
|
|
679
|
+
* the fork, all of it this; plan sec. 43.2) — and for a store adopted
|
|
680
|
+
* once, for its lifetime.
|
|
681
|
+
*
|
|
682
|
+
* The target carries no slot for its record (ARRAY SHAPE RULE, target.ts),
|
|
683
|
+
* so a lookup by target (`preBatch`: a container node born onto an open
|
|
684
|
+
* staging, a committed-frame read of a node-less staging, a park) indexes
|
|
685
|
+
* the list through a map built on first use per batch — the drain itself
|
|
686
|
+
* walks the two lists in step and never builds it. */ let foldList = [];
|
|
687
|
+
|
|
688
|
+
let foldOlds = [];
|
|
689
|
+
|
|
690
|
+
let foldSpare = [];
|
|
691
|
+
|
|
692
|
+
let foldOldsSpare = [];
|
|
693
|
+
|
|
694
|
+
let foldIndex = null;
|
|
695
|
+
|
|
696
|
+
/** Before the staging is set and before an adoption swaps the backing. */ function queueFold(e) {
|
|
697
|
+
if (e.pb !== null) return;
|
|
698
|
+
schedule();
|
|
699
|
+
if (foldIndex !== null) foldIndex.set(e, foldList.length);
|
|
700
|
+
foldList.push(e);
|
|
701
|
+
foldOlds.push(e.v);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/** The committed backing before this batch (valid while `pb !== null`). */ function preBatch(e) {
|
|
705
|
+
if (foldIndex === null) {
|
|
706
|
+
foldIndex = new Map;
|
|
707
|
+
for (let e = 0; e < foldList.length; e++) foldIndex.set(foldList[e], e);
|
|
708
|
+
}
|
|
709
|
+
const n = foldIndex.get(e);
|
|
710
|
+
return n === undefined ? e.v : foldOlds[n];
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
/** The flush committed its pending nodes: fold every pending backing whose
|
|
714
|
+
* container node committed (its staging is gone — `_value` is the backing
|
|
715
|
+
* now) into the committed home; one a later step holds (its node still
|
|
716
|
+
* staged) waits for the flush that commits it. Then the deferred slot
|
|
717
|
+
* releases. */ function drainFolds() {
|
|
718
|
+
if (foldList.length !== 0) {
|
|
719
|
+
const e = foldList;
|
|
720
|
+
const n = foldOlds;
|
|
721
|
+
foldList = foldSpare;
|
|
722
|
+
foldOlds = foldOldsSpare;
|
|
723
|
+
foldSpare = e;
|
|
724
|
+
foldOldsSpare = n;
|
|
725
|
+
foldIndex = null;
|
|
726
|
+
for (let t = 0; t < e.length; t++) {
|
|
727
|
+
const r = e[t];
|
|
728
|
+
if (r.pb === null) continue;
|
|
729
|
+
// folded already (a direct commit)
|
|
730
|
+
const o = r.k;
|
|
731
|
+
if (o !== null && o.Oe !== NOT_PENDING) {
|
|
732
|
+
// held: commits with its hold
|
|
733
|
+
foldList.push(r);
|
|
734
|
+
foldOlds.push(n[t]);
|
|
735
|
+
} else foldTarget(r, n[t]);
|
|
736
|
+
}
|
|
737
|
+
e.length = 0;
|
|
738
|
+
n.length = 0;
|
|
739
|
+
}
|
|
740
|
+
if (deferredReleases.size !== 0) {
|
|
741
|
+
for (const e of deferredReleases) {
|
|
742
|
+
if (e.Oe === NOT_PENDING && !(e.i & CONFIG_OVERRIDE) && e.t === null) {
|
|
743
|
+
deferredReleases.delete(e);
|
|
744
|
+
releaseSlot(e);
|
|
745
|
+
}
|
|
233
746
|
}
|
|
234
747
|
}
|
|
235
748
|
}
|
|
236
749
|
|
|
237
|
-
/**
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
750
|
+
/** Core asks: is a slot node's truth in flight? Its family's derive is —
|
|
751
|
+
* lanes (A17, #2864: a guess mid-refetch stands in for the flight) and
|
|
752
|
+
* marks (an ambient `affects()` over a declared reload lives to the
|
|
753
|
+
* landing) read it. */ GlobalQueue.C = e => {
|
|
754
|
+
const n = e.yt?.fam;
|
|
755
|
+
return n != null && n.node !== null && (n.node.S & STATUS_PENDING) !== 0;
|
|
756
|
+
};
|
|
757
|
+
|
|
758
|
+
/** Core asks (lanes.ts `covered`): the value a slot node's guess covered.
|
|
759
|
+
* A chained link's committed value is the inner store's at the last
|
|
760
|
+
* read-through — refreshed here, since a guess served the reads since
|
|
761
|
+
* (#3672: the revert compares against the base's live value). */ GlobalQueue.In = e => {
|
|
762
|
+
const n = e.yt;
|
|
763
|
+
if (n.ch) {
|
|
764
|
+
const t = e.bt;
|
|
765
|
+
const r = n.v;
|
|
766
|
+
if (n.h !== null && n.h[t] === e) e.Ae = untrack(() => t in r); else if (n.n !== null && n.n[t] === e) e.Ae = untrack(() => unwrapValue(r[t]));
|
|
767
|
+
}
|
|
768
|
+
return e.Ae;
|
|
769
|
+
};
|
|
770
|
+
|
|
771
|
+
GlobalQueue.Rn = () => {
|
|
772
|
+
drainFolds();
|
|
773
|
+
optHooks?.sweep();
|
|
774
|
+
};
|
|
775
|
+
|
|
776
|
+
/** The store half of `affects()` (store/affects.ts) makes its marks' nodes
|
|
777
|
+
* and walks records through these: it never imports the engine. */ GlobalQueue.no = getNode;
|
|
778
|
+
|
|
779
|
+
GlobalQueue.so = isWrappable;
|
|
780
|
+
|
|
781
|
+
/** A projection's creation run commits directly (a memo's first value is
|
|
782
|
+
* its `_value`, not a staging): the draft still writes a clone of the seed,
|
|
783
|
+
* nothing is staged on the nodes, and the setter's exit folds the touched
|
|
784
|
+
* containers at once. Only the synchronous first run. */ let directCommit = false;
|
|
785
|
+
|
|
786
|
+
function runDirect(e) {
|
|
787
|
+
const n = directCommit;
|
|
788
|
+
directCommit = true;
|
|
789
|
+
try {
|
|
790
|
+
e();
|
|
791
|
+
} finally {
|
|
792
|
+
directCommit = n;
|
|
245
793
|
}
|
|
246
794
|
}
|
|
247
795
|
|
|
248
|
-
/**
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
796
|
+
/** Fold one target's pending backing into its committed home. */ function foldTarget(e, n) {
|
|
797
|
+
{
|
|
798
|
+
{
|
|
799
|
+
const t = e.k;
|
|
800
|
+
const r = e.pb;
|
|
801
|
+
if (r !== null) {
|
|
802
|
+
if (r === e.v) {
|
|
803
|
+
// An eager adoption nothing wrote after: the backing is already
|
|
804
|
+
// the adopted object (shared ownership — never cloned).
|
|
805
|
+
e.pb = null;
|
|
806
|
+
e.wk = null;
|
|
807
|
+
} else if (e.ovl && (e.v !== n || !overlayRebuilds(e, r) && t?.m?.ot !== n)) {
|
|
808
|
+
// Overlay flatten (#3044): the backing keeps its identity, so the
|
|
809
|
+
// `t.v === old` gate below skips path copying (the parent slot
|
|
810
|
+
// already points here) and the adopted-notify (setter
|
|
811
|
+
// notifications happened at write time). A backing privatized
|
|
812
|
+
// mid-batch is a fresh clone the parent already points at.
|
|
813
|
+
flattenOverlay(e, r);
|
|
814
|
+
} else if (e.v !== n) {
|
|
815
|
+
// Privatized mid-batch (a child's path copy repointed us): the
|
|
816
|
+
// owned backing takes the written keys.
|
|
817
|
+
privatizeCommitted(e);
|
|
818
|
+
const t = e.v;
|
|
819
|
+
const o = e.wk;
|
|
820
|
+
if (o !== null && o !== WK_ALL) {
|
|
821
|
+
for (const e of o) {
|
|
822
|
+
if (hasOwn.call(r, e)) copyOwn(t, r, e); else delete t[e];
|
|
823
|
+
}
|
|
824
|
+
} else {
|
|
825
|
+
for (const e of Reflect.ownKeys(r)) {
|
|
826
|
+
const o = Object.getOwnPropertyDescriptor(r, e);
|
|
827
|
+
if (o.get || o.set || !o.enumerable || !o.writable || !o.configurable) Object.defineProperty(t, e, o); else if (o.value !== n[e] || !hasOwn.call(n, e)) t[e] = o.value;
|
|
828
|
+
}
|
|
829
|
+
for (const e of Reflect.ownKeys(n)) {
|
|
830
|
+
if (!hasOwn.call(r, e)) delete t[e];
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
e.pb = null;
|
|
834
|
+
e.wk = null;
|
|
835
|
+
} else {
|
|
836
|
+
// The clone (or adopted object) becomes the committed backing
|
|
837
|
+
// (path copy below).
|
|
838
|
+
e.v = e.ovl ? materializePB(e) : r;
|
|
839
|
+
e.ch = e.v[$TARGET] !== undefined;
|
|
840
|
+
e.pb = null;
|
|
841
|
+
e.wk = null;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
if (t !== null) {
|
|
845
|
+
// The node's committed value is the backing. A container nobody
|
|
846
|
+
// subscribes to was a transient staging home (INTERNALS §5: an
|
|
847
|
+
// observer-less write holds a transient record until the fold,
|
|
848
|
+
// discarded with it) — gone with the batch.
|
|
849
|
+
if (t.t === null) e.k = null; else if (t.Ae instanceof LaneView) t.Ae.base = e.v; else t.Ae = e.v;
|
|
850
|
+
}
|
|
851
|
+
if (e.v !== n && e.u) {
|
|
852
|
+
const t = parentSlotKey(e, n);
|
|
853
|
+
if (e.u.v[t] === n) {
|
|
854
|
+
privatizeCommitted(e.u);
|
|
855
|
+
devAssertNeverUserMutation(e.u.v);
|
|
856
|
+
e.u.v[t] = e.v;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
// ---------------------------------------------------------------------------
|
|
864
|
+
// write-time notification (setter exit)
|
|
865
|
+
/** Devtools and attribution: the containers a setter replaced wholesale. */ function storePathOf(e) {
|
|
866
|
+
let n = "";
|
|
867
|
+
for (let t = e; t !== null; t = t.u) n = t.pk === null ? "store" + n : "." + String(t.pk) + n;
|
|
868
|
+
return n;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
const REPLACED_CENSUS_MAX = 64;
|
|
872
|
+
|
|
873
|
+
function sameLeaf(e, n) {
|
|
874
|
+
if (e === n) return true;
|
|
875
|
+
if (e === null || n === null || typeof e !== "object" || typeof n !== "object") return false;
|
|
876
|
+
return unwrapValue(e) === unwrapValue(n) || targetsEqual(e, n);
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
function reportReplacedContainers(e, n, t, r) {
|
|
880
|
+
const o = r ?? Reflect.ownKeys(t);
|
|
881
|
+
const i = Array.isArray(t);
|
|
882
|
+
const l = storeRootOwner(e);
|
|
883
|
+
for (const r of o) {
|
|
884
|
+
if (i && r === "length" || r === $OWNER) continue;
|
|
885
|
+
if (e.del !== null && e.del.has(r)) continue;
|
|
886
|
+
const o = unwrapValue(n[r]);
|
|
887
|
+
const u = unwrapValue(t[r]);
|
|
888
|
+
if (o === null || u === null || typeof o !== "object" || typeof u !== "object" || o === u || targetsEqual(o, u)) continue;
|
|
889
|
+
const f = Array.isArray(u);
|
|
890
|
+
if (f !== Array.isArray(o)) continue;
|
|
891
|
+
let s;
|
|
892
|
+
let a = 0;
|
|
893
|
+
if (f) {
|
|
894
|
+
s = u.length;
|
|
895
|
+
if (s > REPLACED_CENSUS_MAX) continue;
|
|
896
|
+
const e = new Set;
|
|
897
|
+
for (const n of o) e.add(unwrapValue(n));
|
|
898
|
+
for (const n of u) if (e.has(unwrapValue(n))) a++;
|
|
899
|
+
} else {
|
|
900
|
+
const e = Object.keys(u);
|
|
901
|
+
s = e.length;
|
|
902
|
+
if (s > REPLACED_CENSUS_MAX) continue;
|
|
903
|
+
for (const n of e) if (sameLeaf(o[n], u[n])) a++;
|
|
904
|
+
}
|
|
905
|
+
attrHooks.storeReplaced(storePathOf(e) + "." + String(r), f, s, a, f ? o.length : Object.keys(o).length, l);
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
/** A user setter wrote a key whose staging is a derivation another
|
|
910
|
+
* transaction holds (A34 (3), #3612): the hold re-derives over the write. */ let heldDerivationHit = false;
|
|
911
|
+
|
|
912
|
+
/** The open setter is a USER'S (`storeSetter(…, guard = true)`), not the
|
|
913
|
+
* derive's draft or landing. */ let userWrite = false;
|
|
914
|
+
|
|
915
|
+
/** Is the open setter a user's? (reconcile.ts: a user's reconcile on an
|
|
916
|
+
* optimistic family writes the draft.) */ function userWriting() {
|
|
917
|
+
return userWrite;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
function heldDerivation(e, n, t) {
|
|
921
|
+
if (e.fam === null || !userWrite) return false;
|
|
922
|
+
if (n !== undefined) return (n.i & (CONFIG_HELD | CONFIG_MANUAL_WRITE)) === CONFIG_HELD && txOf(n) !== flushTransaction;
|
|
923
|
+
const r = e.k;
|
|
924
|
+
return r !== null && (r.i & CONFIG_HELD) !== 0 && txOf(r) !== flushTransaction && heldKeyChanged(e, t);
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
/** Setter exit: one node write per changed observed key (the leaves stage
|
|
928
|
+
* like signals — holds and lanes engage here on later steps), presence and
|
|
929
|
+
* deep witnesses, and the container when membership changed. */ function notifyWrites(e) {
|
|
930
|
+
let n = e.pb;
|
|
931
|
+
if (n === null) return;
|
|
932
|
+
// Optimistic channel: a user's writes on an optimistic family become
|
|
933
|
+
// guesses on the nodes (lanes); the draft is discarded, the committed
|
|
934
|
+
// backing never touched.
|
|
935
|
+
if (userWrite && e.fam?.opt === true) {
|
|
936
|
+
// The draft is discarded first: the diff reads the target (its nodes
|
|
937
|
+
// are born from the committed frame, not the draft).
|
|
938
|
+
e.pb = null;
|
|
939
|
+
e.wk = null;
|
|
940
|
+
e.pb = optHooks.writes(e, n);
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
943
|
+
const t = e.v;
|
|
944
|
+
const r = e.n;
|
|
945
|
+
// Written-keys bound: trap writes record their keys, so the notify visits
|
|
946
|
+
// O(written) nodes instead of every subscription on the record (a selection
|
|
947
|
+
// map with thousands of per-key subscribers pays two visits per select,
|
|
948
|
+
// not a full scan). Falls back to the full node scan when the bound can't
|
|
949
|
+
// hold: no trap granularity (wk null), an array length write (WK_ALL —
|
|
950
|
+
// implicit index deletes), accessors on the record (t.a — a getter node's
|
|
951
|
+
// value can change when ANY key is written), or a non-plain prototype
|
|
952
|
+
// (class instances: prototype getters derive from arbitrary fields).
|
|
953
|
+
// Overlay pbs chain to the COMMITTED object (#3044) — plainness is the
|
|
954
|
+
// committed container's prototype, not the overlay's.
|
|
955
|
+
const o = e.wk;
|
|
956
|
+
const i = o === WK_ALL || e.a === true || !plainProto(e.ovl ? e.v : n) ? null : o;
|
|
957
|
+
if (attrHooks !== null) reportReplacedContainers(e, t, n, i);
|
|
958
|
+
if (e.fam !== null && !heldDerivationHit && i !== null) for (const n of i) if (heldDerivation(e, r?.[n], n)) {
|
|
959
|
+
heldDerivationHit = true;
|
|
960
|
+
break;
|
|
961
|
+
}
|
|
962
|
+
if (r !== null) {
|
|
963
|
+
const o = i ?? Reflect.ownKeys(r);
|
|
964
|
+
for (const i of o) {
|
|
965
|
+
const o = r[i];
|
|
966
|
+
if (o === undefined) continue;
|
|
967
|
+
if (o.acc === true || hasOwn.call(n, i) && lookupGetter.call(n, i) !== undefined) {
|
|
968
|
+
// Accessor keys: the node is linked for shape-change notification,
|
|
969
|
+
// its value is never served (the getter runs with the proxy
|
|
970
|
+
// receiver on read) — FORCE wakes the readers. A data descriptor
|
|
971
|
+
// falls through so the cleared-acc hot read serves it (#3949);
|
|
972
|
+
// an own getter's descriptor has no `value`, so the compare below
|
|
973
|
+
// would otherwise keep the getter's result (or FORCE) for `undefined`.
|
|
974
|
+
o.acc = isOwnAccessor(n, i);
|
|
975
|
+
const e = Object.getOwnPropertyDescriptor(t, i);
|
|
976
|
+
const r = Object.getOwnPropertyDescriptor(n, i);
|
|
977
|
+
if (!r || r.get || r.set) {
|
|
978
|
+
if (e?.get !== r?.get || e?.set !== r?.set || e?.value !== r?.value) setSignal(o, () => FORCE);
|
|
979
|
+
continue;
|
|
980
|
+
}
|
|
981
|
+
if (e?.get || e?.set || !isEqual(e?.value, r.value)) setSignal(o, () => r.value);
|
|
982
|
+
continue;
|
|
983
|
+
}
|
|
984
|
+
const l = e.del !== null && e.del.has(i) ? undefined : n[i];
|
|
985
|
+
setSignal(o, () => l);
|
|
986
|
+
if (e.fam !== null) {
|
|
987
|
+
// The derive's write is a derivation; a user setter's is a proposal
|
|
988
|
+
// (A34 (3)'s discriminator).
|
|
989
|
+
if (!userWrite) {
|
|
990
|
+
o.i &= ~CONFIG_MANUAL_WRITE;
|
|
991
|
+
holdWithDerive(e, o);
|
|
992
|
+
} else o.i |= CONFIG_MANUAL_WRITE;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
const l = e.h;
|
|
997
|
+
if (l !== null) {
|
|
998
|
+
const t = i ?? Reflect.ownKeys(l);
|
|
999
|
+
for (const r of t) {
|
|
1000
|
+
const t = l[r];
|
|
1001
|
+
if (t !== undefined) {
|
|
1002
|
+
setSignal(t, r in n && !(e.del !== null && e.del.has(r)));
|
|
1003
|
+
if (e.fam !== null) holdWithDerive(e, t);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
if (e.dk !== null) {
|
|
1008
|
+
if (e.del !== null && e.del.size !== 0) bumpDeep(e); else for (const r of i ?? Reflect.ownKeys(n)) {
|
|
1009
|
+
if (r === $OWNER) continue;
|
|
1010
|
+
const o = n[r];
|
|
1011
|
+
const i = t[r];
|
|
1012
|
+
if (o !== null && typeof o === "object" ? !targetsEqual(i, o) : !isEqual(i, o)) {
|
|
1013
|
+
bumpDeep(e);
|
|
1014
|
+
break;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
// The container: structural readers hear membership/arrangement changes
|
|
1019
|
+
// only (R9) — the compare is the store's, against the committed frame. A
|
|
1020
|
+
// container carrying an arrangement guess is told whether or not anyone
|
|
1021
|
+
// subscribes: the write is a landing on the guess (`notifyContainer`).
|
|
1022
|
+
const u = e.k;
|
|
1023
|
+
if (u !== null && (u.t !== null || u.i & CONFIG_OVERRIDE)) {
|
|
1024
|
+
let r;
|
|
1025
|
+
if (e.ovl) {
|
|
1026
|
+
r = e.del !== null && e.del.size !== 0;
|
|
1027
|
+
if (!r) {
|
|
1028
|
+
for (const e of Reflect.ownKeys(n)) {
|
|
1029
|
+
if (!hasOwn.call(t, e)) {
|
|
1030
|
+
r = true;
|
|
273
1031
|
break;
|
|
274
1032
|
}
|
|
275
|
-
const o = t?.[$TARGET];
|
|
276
|
-
if (o === undefined) break;
|
|
277
|
-
const r = o.pb ?? o[STORE_VALUE];
|
|
278
|
-
if (r === t) break;
|
|
279
|
-
t = r;
|
|
280
1033
|
}
|
|
281
1034
|
}
|
|
1035
|
+
} else {
|
|
1036
|
+
r = Array.isArray(n) && Array.isArray(t) ? arrayStructureChanged(t, n) : membershipChanged(t, n);
|
|
282
1037
|
}
|
|
1038
|
+
if (r) notifyContainer(u, n);
|
|
283
1039
|
}
|
|
284
1040
|
}
|
|
285
1041
|
|
|
286
|
-
/**
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
1042
|
+
/** Accessor-node wake sentinel: readers re-run and re-invoke the getter. */ const FORCE = Symbol();
|
|
1043
|
+
|
|
1044
|
+
/** Two raws that wrap to the same target are the same logical value. */ function targetsEqual(e, n) {
|
|
1045
|
+
if (e === null || typeof e !== "object" || n === null || typeof n !== "object") return false;
|
|
1046
|
+
const t = lookupTarget(e, null);
|
|
1047
|
+
return t !== undefined && t === lookupTarget(n, null);
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
/** Positional identity IS content for arrays (R15). */ function arrayStructureChanged(e, n) {
|
|
1051
|
+
if (e.length !== n.length) return true;
|
|
1052
|
+
for (let t = 0; t < n.length; t++) {
|
|
1053
|
+
const r = e[t];
|
|
1054
|
+
const o = n[t];
|
|
1055
|
+
if (!isEqual(r, o) && !targetsEqual(r, o)) return true;
|
|
1056
|
+
}
|
|
1057
|
+
return false;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
function membershipChanged(e, n) {
|
|
1061
|
+
const t = Reflect.ownKeys(n);
|
|
1062
|
+
if (Reflect.ownKeys(e).length - +isOwned(e) !== t.length - +isOwned(n)) return true;
|
|
1063
|
+
for (const n of t) if (n !== $OWNER && !(n in e)) return true;
|
|
1064
|
+
return false;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
/** One leaf's notification for an adoption diff (descriptor-aware). */ function notifyKeyDiff(e, n, t, r, o = true) {
|
|
1068
|
+
if (e.acc === true || o && hasOwn.call(r, n) && lookupGetter.call(r, n) !== undefined) {
|
|
1069
|
+
e.acc = isOwnAccessor(r, n);
|
|
1070
|
+
const o = Object.getOwnPropertyDescriptor(t, n);
|
|
1071
|
+
const i = Object.getOwnPropertyDescriptor(r, n);
|
|
1072
|
+
// Data descriptors fall through, as in notifyWrites (#3949).
|
|
1073
|
+
if (!i || i.get || i.set) {
|
|
1074
|
+
if (o?.get !== i?.get || o?.set !== i?.set || o?.value !== i?.value) setSignal(e, () => FORCE);
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
const l = o?.value;
|
|
1078
|
+
const u = i?.value;
|
|
1079
|
+
if (o?.get || o?.set || !isEqual(l, u) && !targetsEqual(l, u)) setSignal(e, typeof u === "function" ? () => u : u);
|
|
1080
|
+
} else {
|
|
1081
|
+
const o = t[n];
|
|
1082
|
+
const i = r[n];
|
|
1083
|
+
if (!isEqual(o, i) && !targetsEqual(o, i)) setSignal(e, typeof i === "function" ? () => i : i);
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
function hasAccessorFlag(e) {
|
|
1088
|
+
return e.acc === true;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
/** One leaf's notification when the caller already has both values. */ function notifyKeyValue(e, n, t, r, o, i) {
|
|
1092
|
+
if (e.acc === true) {
|
|
1093
|
+
notifyKeyDiff(e, n, o, i, false);
|
|
1094
|
+
return;
|
|
1095
|
+
}
|
|
1096
|
+
if (!isEqual(t, r) && !targetsEqual(t, r)) setSignal(e, typeof r === "function" ? () => r : r);
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
/** The structural half of an adoption's notifications (reconcile does the
|
|
1100
|
+
* leaves itself): presence by `in`, the container by membership. Presence
|
|
1101
|
+
* is DIFFED `old` → `neu` like the leaves (#3743): a presence node is
|
|
1102
|
+
* written only where `in` changed between the view it was last told
|
|
1103
|
+
* (#3296) and the adoption — `setSignal` joins a held node's transaction
|
|
1104
|
+
* before its equality gate (A34 (1)), so repeating an unchanged absence to
|
|
1105
|
+
* a node an action holds would make the whole tick the action's (an
|
|
1106
|
+
* unrelated `a.value` stayed stale until the action settled). A real
|
|
1107
|
+
* change still writes, and on a held node still proposes. A live chained
|
|
1108
|
+
* `old` (a store proxy, §7b) reflects the inner store, not what the node
|
|
1109
|
+
* was last told — written unconditionally, as before.
|
|
294
1110
|
*
|
|
295
|
-
*
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
if (
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
const
|
|
311
|
-
|
|
1111
|
+
* The container is told of an arrangement change when it has structural
|
|
1112
|
+
* subscribers OR carries an arrangement guess (S4): for a guessed
|
|
1113
|
+
* container the write is the landing that judges the guess (Q-D, plan
|
|
1114
|
+
* sec. 39 — confirm, supersede, or hold beneath), not a subscriber
|
|
1115
|
+
* notification, and a landing whose truth changed no guessed key's value
|
|
1116
|
+
* or presence (a newer question's rows beneath an optimistic push) still
|
|
1117
|
+
* answers the arrangement — before, only the presence write reached the
|
|
1118
|
+
* lane, by accident of being unconditional. */ function notifyFoldTail(e, n, t) {
|
|
1119
|
+
const r = e.h;
|
|
1120
|
+
if (r !== null) {
|
|
1121
|
+
const e = n[$TARGET];
|
|
1122
|
+
for (const o of Reflect.ownKeys(r)) if (e || o in n !== o in t) setSignal(r[o], o in t);
|
|
1123
|
+
}
|
|
1124
|
+
const o = e.k;
|
|
1125
|
+
if (o !== null && (o.t !== null || o.i & CONFIG_OVERRIDE)) {
|
|
1126
|
+
const e = Array.isArray(t) && Array.isArray(n) ? arrayStructureChanged(n, t) : membershipChanged(n, t);
|
|
1127
|
+
if (e) notifyContainer(o, t);
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
/** An adoption's notifications: every node of the target, diffed `old` →
|
|
1132
|
+
* `neu` (value nodes by key, presence by `in`, the container by
|
|
1133
|
+
* membership, the deep witness by identity). */ function notifyFold(e, n, t) {
|
|
1134
|
+
if (e.dk !== null && n !== t) bumpDeep(e);
|
|
1135
|
+
const r = e.n;
|
|
1136
|
+
if (r !== null) {
|
|
1137
|
+
for (const e of Reflect.ownKeys(r)) notifyKeyDiff(r[e], e, n, t);
|
|
1138
|
+
}
|
|
1139
|
+
notifyFoldTail(e, n, t);
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
// ---------------------------------------------------------------------------
|
|
1143
|
+
// visibility
|
|
1144
|
+
let writing = 0;
|
|
1145
|
+
|
|
1146
|
+
/** Roots of the stores with an open setter: reads of THEIR drafts are the
|
|
1147
|
+
* writer's (no tracking, pending backing served); every other store reads
|
|
1148
|
+
* normally — a derive's external reads are its dependencies (#3037). */ let writeScopes = null;
|
|
1149
|
+
|
|
1150
|
+
const pendingNotify = new Set;
|
|
1151
|
+
|
|
1152
|
+
function scopeKey(e) {
|
|
1153
|
+
if (e.fam !== null) return e.fam;
|
|
1154
|
+
let n = e;
|
|
1155
|
+
while (n.u !== null) n = n.u;
|
|
1156
|
+
return n;
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
function inDraft(e) {
|
|
1160
|
+
return writeScopes !== null && writeScopes.has(scopeKey(e));
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
/** Shallow store: values served verbatim (a nested store proxy still wraps
|
|
1164
|
+
* into the family so writes stay isolated). */ function serveShallow(e, n, t) {
|
|
1165
|
+
if (t !== null && typeof t === "object" && t[$TARGET] !== undefined) return draftServe(e, wrap(t, e, n));
|
|
1166
|
+
return t;
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
/** A child proxy served to a draft joins the draft's scope. */ function draftServe(e, n) {
|
|
1170
|
+
if (writeScopes !== null && inDraft(e)) {
|
|
1171
|
+
const e = n?.[$TARGET];
|
|
1172
|
+
if (e !== undefined && e.v !== undefined) writeScopes.add(scopeKey(e));
|
|
1173
|
+
}
|
|
1174
|
+
return n;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
/** The committed backing: the container node's committed value while a
|
|
1178
|
+
* batch is staged on it (an adoption swaps `t.v` eagerly — INTERNALS §3,
|
|
1179
|
+
* "adoption is eager by contract" — and the node keeps the frame committed
|
|
1180
|
+
* readers under a hold still see), `t.v` otherwise. */ function committed(e) {
|
|
1181
|
+
const n = e.k;
|
|
1182
|
+
if (n !== null) return n.Oe !== NOT_PENDING ? asBacking(n.Ae) : e.v;
|
|
1183
|
+
// A node-less staging (`stageOn`): the fold queue remembers the frame.
|
|
1184
|
+
return e.pb !== null ? preBatch(e) : e.v;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
/** The pass that sees this flush's staging (signal parity — `read()`'s
|
|
1188
|
+
* staged arm): the computation running (`context`, never the owner — a
|
|
1189
|
+
* handler, an effect callback or `onSettled` has an owner but no pass),
|
|
1190
|
+
* unless it is a frame reader (children-forbidden, A32). Null = the
|
|
1191
|
+
* committed frame. */ function stagingReader() {
|
|
1192
|
+
let e = context;
|
|
1193
|
+
if (e === null) return null;
|
|
1194
|
+
if (e.Ht) e = e.gt ?? null;
|
|
1195
|
+
return e === null || e.i & CONFIG_CHILDREN_FORBIDDEN ? null : e;
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
/** The backing a read serves (RUL-1, signal parity): the draft and the write
|
|
1199
|
+
* override read the pending backing; a tracked reader reads it too — only
|
|
1200
|
+
* for presence and descriptors, its leaf (born from the two frames) decides
|
|
1201
|
+
* the value through core; a context-free or frame reader the committed
|
|
1202
|
+
* backing; a pass reading UNTRACKED asks the container node — `read()`
|
|
1203
|
+
* without a link: the node's value is the backing, chosen by the hold rules
|
|
1204
|
+
* (a render effect outside the parking flush sees committed and re-derives
|
|
1205
|
+
* at the landing; a memo joins the future; a staging is marked read). */
|
|
1206
|
+
/** The backing a read of `target` is served from — the truth (committed,
|
|
1207
|
+
* or the staging a pass may see). An optimistic family's lane values are
|
|
1208
|
+
* NOT composed here: a guessed key always has its node, and the node is
|
|
1209
|
+
* what serves its value (`nodeValue`); presence and enumeration compose
|
|
1210
|
+
* per key / per walk (`optimisticHas`, `enumerationSource`). */ function readSource(e, n, t = false) {
|
|
1211
|
+
// An optimistic setter's read before its first write sees what the writer
|
|
1212
|
+
// sees — the lanes' values and the tick's own guesses over the truth
|
|
1213
|
+
// (#3665), never a staging awaiting its fold: the draft is born on the
|
|
1214
|
+
// read (and notified at the setter's exit like any).
|
|
1215
|
+
if (userWrite && e.fam !== null && e.fam.opt === true && !pendingNotify.has(e) && inDraft(e)) {
|
|
1216
|
+
const n = ensurePB(e);
|
|
1217
|
+
pendingNotify.add(e);
|
|
1218
|
+
return n;
|
|
1219
|
+
}
|
|
1220
|
+
const r = e.pb;
|
|
1221
|
+
if (r === null) return e.v;
|
|
1222
|
+
if (inDraft(e) || writeOverride) {
|
|
1223
|
+
// The derive reads its own store through its draft: a key (or the
|
|
1224
|
+
// container) a transaction holds — a user's write inside an action,
|
|
1225
|
+
// held with it — is the future, and the pass that reads it derives
|
|
1226
|
+
// from the future (L2: a read of something already held joins it): the
|
|
1227
|
+
// derive's whole result reveals with the action (core R31 / #3733:
|
|
1228
|
+
// "under the transaction and revealed with it"), not key by key. A
|
|
1229
|
+
// user's setter reading its draft is no pass and joins nothing here.
|
|
1230
|
+
if (writeOverride && !userWrite) {
|
|
1231
|
+
const t = n !== undefined ? e.n?.[n] : undefined;
|
|
1232
|
+
if (t !== undefined) {
|
|
1233
|
+
if ((t.i & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD) joinPassTx(txOf(t));
|
|
1234
|
+
} else {
|
|
1235
|
+
const t = e.k;
|
|
1236
|
+
if (t !== null && (t.i & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && (n === undefined || heldKeyChanged(e, n))) joinPassTx(txOf(t));
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
312
1239
|
return r;
|
|
313
1240
|
}
|
|
314
|
-
const o = e.
|
|
315
|
-
//
|
|
316
|
-
//
|
|
317
|
-
// the
|
|
318
|
-
//
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
1241
|
+
const o = e.k;
|
|
1242
|
+
// A key whose leaf carries a lane's value (a guess, S4): the leaf serves
|
|
1243
|
+
// it — the container's frame is not consulted (a reader is not made a
|
|
1244
|
+
// stale reader of the container's hold for a key it does not see through
|
|
1245
|
+
// it).
|
|
1246
|
+
if (n !== undefined) {
|
|
1247
|
+
const t = e.n?.[n];
|
|
1248
|
+
if (t !== undefined && t.i & CONFIG_OVERRIDE) return committed(e);
|
|
1249
|
+
}
|
|
1250
|
+
const i = o !== null && (o.i & CONFIG_HELD) !== 0;
|
|
1251
|
+
// A held container: the hold's unit is the key (#3706). A key the HELD
|
|
1252
|
+
// staging left unchanged reads the ambient view — a mainline layer's
|
|
1253
|
+
// write above the hold publishes mainline (#3688, #3612); a key it
|
|
1254
|
+
// changed reads the frame core serves this reader.
|
|
1255
|
+
if (i && n !== undefined && !heldKeyChanged(e, n, t)) return r;
|
|
1256
|
+
const l = stagingReader();
|
|
1257
|
+
if (l === null) {
|
|
1258
|
+
if (getObserver() !== null) return r;
|
|
1259
|
+
// a frame reader's leaf decides the value
|
|
1260
|
+
// A projection's writes are its truth as soon as made (the derive is
|
|
1261
|
+
// the authority, nothing it writes is a proposal): a context-free reader
|
|
1262
|
+
// sees them before the flush — unless a hold keeps them staged.
|
|
1263
|
+
if (familyAhead(e)) return r;
|
|
1264
|
+
// A verdict window (`isPending`/`latest`) with no reader (a top-level
|
|
1265
|
+
// probe) judges the container like any node — for a key the batch
|
|
1266
|
+
// changed; an unchanged key is committed and final (A22: pending is per
|
|
1267
|
+
// 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.Ae) : e.v;
|
|
1269
|
+
return o === null ? committed(e) : asBacking(read(o));
|
|
1270
|
+
}
|
|
1271
|
+
// A pass reading a key the batch left unchanged: the same value in both
|
|
1272
|
+
// frames — committed, holding no one. Otherwise the container's frame by
|
|
1273
|
+
// core's rules — `read()` of the node WITHOUT a link (a value reader never
|
|
1274
|
+
// subscribes to the container): a render effect outside a hold's flush
|
|
1275
|
+
// sees committed and re-derives at the landing, a memo joins the future,
|
|
1276
|
+
// a staging read is marked. The key's own leaf, when it has one, decides
|
|
1277
|
+
// the value the same way.
|
|
1278
|
+
if (!i) {
|
|
1279
|
+
// A plain pass this flush already served a staging as the screen
|
|
1280
|
+
// (REACTIVE_STAGED_READ — what `read()` would set again here) reads an
|
|
1281
|
+
// unheld staging directly: an unchanged key is the same value in both
|
|
1282
|
+
// frames, a changed one is what the mark already stands for. The
|
|
1283
|
+
// per-key question is asked once per pass, not once per key (a
|
|
1284
|
+
// `mapArray` over 1k shifted rows asked it 1k times).
|
|
1285
|
+
if (l.R & REACTIVE_STAGED_READ && !(l.i & (CONFIG_VERDICT | CONFIG_CHILDREN_FORBIDDEN))) return r;
|
|
1286
|
+
if (n !== undefined && !keyChanged(e, n, t)) return committed(e);
|
|
1287
|
+
// A node-less staging (`stageOn`): what `read()` of its node would do
|
|
1288
|
+
// for this pass — a verdict reader sees the screen, any other derives
|
|
1289
|
+
// from the staging and is marked as having read it.
|
|
1290
|
+
if (o === null) {
|
|
1291
|
+
if (l.i & CONFIG_VERDICT && stagedScreen(l)) return committed(e);
|
|
1292
|
+
stagedRead(l);
|
|
1293
|
+
return r;
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
return asBacking(untrack(() => read(o)));
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
/** Did the HELD staging (the container node's, not a mainline layer above
|
|
1300
|
+
* it) change `key`? */ function heldKeyChanged(e, n, t = true) {
|
|
1301
|
+
return changedBetween(e, e.k.Ae, e.k.Oe, n, t) && !adoptChild(e, n);
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
/** The values of a held frame, built once per frame: neither the committed
|
|
1305
|
+
* frame nor the held staging changes while the hold stands (a draft writes a
|
|
1306
|
+
* clone), so the set stays true for every first read of the hold. */ const frameRaws = new WeakMap;
|
|
1307
|
+
|
|
1308
|
+
function rawsOf(e) {
|
|
1309
|
+
let n = frameRaws.get(e);
|
|
1310
|
+
if (n === undefined) frameRaws.set(e, n = new Set(Object.values(e)));
|
|
1311
|
+
return n;
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
/** The same row at a slot, as the adoption pairs rows (reconcile.ts): two
|
|
1315
|
+
* objects whose keys agree — positionally for an unkeyed family. */ function sameRow(e, n, t) {
|
|
1316
|
+
return e !== null && typeof e === "object" && n !== null && typeof n === "object" && (t === null || sameKey(t(e), t(n)));
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
/** A key of a held adoption whose old and new values are the same row that
|
|
1320
|
+
* nothing had proxied when the adoption ran, so `descend` never paired
|
|
1321
|
+
* them: paired now, as it would have been (#3712) — one target, the old
|
|
1322
|
+
* raw its committed frame, the new raw its staging, held by the same
|
|
1323
|
+
* transaction. Both raws resolve to it, so the slot reads as unchanged and
|
|
1324
|
+
* the row's own leaves decide: `id` the same in both frames holds no one,
|
|
1325
|
+
* `version` holds. A record's keys pair by name; an array's by the family's
|
|
1326
|
+
* key — without a family the pairing is not known here and the hold stands. */ function adoptChild(e, n) {
|
|
1327
|
+
const t = e.k;
|
|
1328
|
+
const r = asBacking(t.Ae);
|
|
1329
|
+
const o = r[n];
|
|
1330
|
+
const i = t.Oe[n];
|
|
1331
|
+
const l = Array.isArray(r) ? e.fam?.key : null;
|
|
1332
|
+
if (e.s || l === undefined || !sameRow(o, i, l) || !isWrappable(o) || !isWrappable(i) || Array.isArray(o) !== Array.isArray(i) || lookupTarget(o, e.fam) !== undefined || lookupTarget(i, e.fam) !== undefined ||
|
|
1333
|
+
// A raw in both frames (the same objects swapped or reordered) is its
|
|
1334
|
+
// own row: pairing it with another would alias the two after the commit.
|
|
1335
|
+
rawsOf(r).has(i) || rawsOf(t.Oe).has(o)) return false;
|
|
1336
|
+
// A raw-marked value wraps to itself, a proxy to its own target: neither
|
|
1337
|
+
// is a fresh target around `ov`.
|
|
1338
|
+
const u = wrap(o, e, n)[$TARGET];
|
|
1339
|
+
if (u === undefined || u.v !== o) return false;
|
|
1340
|
+
adoptPB(u, i, false);
|
|
1341
|
+
holdNode(getContainerNode(u), txOf(t));
|
|
1342
|
+
return true;
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
/** 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.i & CONFIG_HELD);
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
/** Did this batch change `key` — value (slot equality), presence, and (for
|
|
1350
|
+
* a `shape` reader — a descriptor read, a node's hold) enumerability or
|
|
1351
|
+
* accessor-ness? A swapped or non-plain prototype, or a chained backing,
|
|
1352
|
+
* changes every key (#3706: those hold the whole container).
|
|
1353
|
+
*
|
|
1354
|
+
* A VALUE read asks without `shape`: a key whose value is the same in both
|
|
1355
|
+
* frames serves the same value from either, whatever its descriptor did —
|
|
1356
|
+
* and the descriptor probes (`__lookupGetter__`, `propertyIsEnumerable`,
|
|
1357
|
+
* twice each) are the cost of the question on a wide container read under
|
|
1358
|
+
* a staging (a keyed reconcile's 1k rows, read by `mapArray` in the flush
|
|
1359
|
+
* that staged them: ~3× the tick). A container with accessors seen (`a`)
|
|
1360
|
+
* keeps the full test — a getter's answer is not a slot's. */ function keyChanged(e, n, t = true) {
|
|
1361
|
+
return changedBetween(e, committed(e), e.pb, n, t);
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
function changedBetween(e, n, t, r, o) {
|
|
1365
|
+
if (e.ch || t[$TARGET] !== undefined || Object.getPrototypeOf(t) !== Object.getPrototypeOf(n) || !plainProto(n)) return true;
|
|
1366
|
+
if (e.del !== null && e.del.has(r)) return r in n;
|
|
1367
|
+
const i = hasOwn.call(t, r) || e.ovl && hasOwn.call(e.v, r);
|
|
1368
|
+
if (i !== hasOwn.call(n, r)) return true;
|
|
1369
|
+
if (!i) return false;
|
|
1370
|
+
if ((o || e.a) && (isOwnAccessor(n, r) || isOwnAccessor(t, r) || propertyIsEnumerable.call(n, r) !== propertyIsEnumerable.call(t, r))) return true;
|
|
1371
|
+
const l = t[r];
|
|
1372
|
+
const u = n[r];
|
|
1373
|
+
return !isEqual(u, l) && !sameLogicalSlot(e, u, l);
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
/** Value of a key with a leaf: core's — `read()` links a tracked reader and,
|
|
1377
|
+
* tracked or not, applies the hold and A28 rules a signal's read does. A
|
|
1378
|
+
* projection's unheld staging is ahead for a handler (`familyAhead`). */ function nodeValue(e, n, t) {
|
|
1379
|
+
let r = read(e);
|
|
1380
|
+
if (e.Oe !== NOT_PENDING && !(e.i & CONFIG_HELD) && context === null && t.fam !== null) r = e.Oe;
|
|
1381
|
+
return r === FORCE ? n : r;
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
/** Serve a present data key: link the leaf for a tracked reader (creating it
|
|
1385
|
+
* on first read), wrap a wrappable child through the per-node wrap cache. */ function serveDataKey(e, n, t, r, o, i = -1) {
|
|
1386
|
+
// Read-through (§7b): the inner store's node is the truth; the outer's is
|
|
1387
|
+
// a subscription point only — unless it carries a guess (an optimistic
|
|
1388
|
+
// store over a store: the guess is the outer's, shown over the inner).
|
|
1389
|
+
const l = e.ch && r === e.v;
|
|
1390
|
+
let u = t;
|
|
1391
|
+
// A write-override read (a derive's draft) serves the backing: the derive
|
|
1392
|
+
// composes on the truth, never on a lane's or a hold's frame.
|
|
1393
|
+
if (!inDraft(e) && !writeOverride) {
|
|
1394
|
+
if (o === undefined && getObserver() !== null) o = getNode(e, n, i);
|
|
1395
|
+
if (o !== undefined) {
|
|
1396
|
+
const n = nodeValue(o, t, e);
|
|
1397
|
+
if (l) {
|
|
1398
|
+
// The link's committed value follows the inner store's (what a lane
|
|
1399
|
+
// over it covers — its reveal and revert compare against it, S4).
|
|
1400
|
+
o.Ae = t;
|
|
1401
|
+
if (o.i & CONFIG_OVERRIDE) u = n;
|
|
1402
|
+
} else u = n;
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
if (e.s) return serveShallow(e, n, u);
|
|
1406
|
+
// A derive's draft wrapper answers `$TARGET` without being a proxy: the
|
|
1407
|
+
// inner store's alias (#3767), resolved like a raw (#3859).
|
|
1408
|
+
if (e.ch && !l && u !== null && typeof u === "object" && u[$TARGET]?.px !== u) u = resolveChainedRaw(e, n, u);
|
|
1409
|
+
if (o !== undefined) {
|
|
1410
|
+
if (o.pxv === u && u !== undefined) return draftServe(e, o.px);
|
|
1411
|
+
if (!isWrappable(u)) return u;
|
|
1412
|
+
const t = wrap(u, e, n);
|
|
1413
|
+
o.px = t;
|
|
1414
|
+
o.pxv = u;
|
|
1415
|
+
return draftServe(e, t);
|
|
1416
|
+
}
|
|
1417
|
+
if (!isWrappable(u)) return u;
|
|
1418
|
+
return draftServe(e, wrap(u, e, n));
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
/** Is `n` among `c`'s dependencies (any pass's — a kept tail included)? */ function linkedTo(e, n) {
|
|
1422
|
+
for (let t = e.G; t !== null; t = t._) if (t.I === n) return true;
|
|
1423
|
+
return false;
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
/** The projection's obligation (plan sec. 31.7 Q-B): before any value is served
|
|
1427
|
+
* through a family target, the derive — the firewall — is brought up to
|
|
1428
|
+
* date WITHOUT the reader subscribing to it. Core's `read()` of the
|
|
1429
|
+
* firewall with no link does all of it: the pull, the status gate
|
|
1430
|
+
* (uninitialized / pending → NotReady, errored → the error; a verdict
|
|
1431
|
+
* window judges it; a pending source registers the reader for the landing)
|
|
1432
|
+
* and the reader's height. The derive's own draft ops are exempt (the write
|
|
1433
|
+
* override): the derive is the author. */ function pullFamily(e) {
|
|
1434
|
+
const n = e.fam.node;
|
|
1435
|
+
if (n === null || writeOverride || inDraft(e)) return;
|
|
1436
|
+
// A settled derive: the pull alone, no link (the barrier). A derive with a
|
|
1437
|
+
// flight up (or errored): the reader observes the flight exactly as a
|
|
1438
|
+
// reader of a memo would — linked, so it re-runs at the landing (the
|
|
1439
|
+
// next settled pass pulls without linking and the stale link trims), a
|
|
1440
|
+
// verdict reader registered, a tracked pass suspended.
|
|
1441
|
+
if (n.S & (STATUS_PENDING | STATUS_ERROR)) {
|
|
1442
|
+
// A render effect outside the flight's own flush is the frame, not a
|
|
1443
|
+
// derivation: it keeps what it shows and learns of the landing from the
|
|
1444
|
+
// leaves the landing changes (unchanged leaves say nothing — it is not a
|
|
1445
|
+
// stale reader of the derive). Inside the flush it suspends like any.
|
|
1446
|
+
// One that already observes the flight (linked by the pass that saw it
|
|
1447
|
+
// go up — the frame's hold, `blocked`) keeps observing it: a re-run for
|
|
1448
|
+
// another reason (a guess it read, the seam) that dropped the link would
|
|
1449
|
+
// release the hold with the flight still up. An errored derive has no
|
|
1450
|
+
// landing to learn of: the error is what the frame shows.
|
|
1451
|
+
const t = context;
|
|
1452
|
+
if (t !== null && t.u === EFFECT_RENDER && !(n.S & STATUS_ERROR) && n.i & CONFIG_HELD && flushTransaction !== txOf(n) && !linkedTo(t, n)) return;
|
|
1453
|
+
// An optimistic family carrying guesses that stand in for the flight
|
|
1454
|
+
// (A17): the guesses are the display — a reader is served the leaves
|
|
1455
|
+
// (committed plus the guesses) and does not suspend on the derive; a
|
|
1456
|
+
// verdict window still asks it (`isPending` is true while the flight is
|
|
1457
|
+
// up). The family's, not the target's: the edit stands in for the one
|
|
1458
|
+
// flight the family has.
|
|
1459
|
+
const r = e.fam.overlaid;
|
|
1460
|
+
if (verdict === null && n.S & STATUS_PENDING && r !== undefined && r.size !== 0) return;
|
|
1461
|
+
read(n);
|
|
1462
|
+
} else untrack(() => read(n));
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
/** A projection's writes are its derive's: a staging made while the derive
|
|
1466
|
+
* is held (a flight up — its continuation writes after an `await`, a
|
|
1467
|
+
* callback's late write) belongs to the derive's transaction and reveals
|
|
1468
|
+
* with it, never drained early — and answers the derive's question: a quiet
|
|
1469
|
+
* re-ask's landing is quiet through its reveal (A19 exc. 2; the leaf's own
|
|
1470
|
+
* classification, as core reads it — `heldNotFinal`). */ function holdWithDerive(e, n) {
|
|
1471
|
+
const t = e.fam?.node;
|
|
1472
|
+
if (t !== undefined && t !== null && !userWrite && // the derive's own write (its draft or landing), not a user setter's
|
|
1473
|
+
t.i & CONFIG_HELD && n.Oe !== NOT_PENDING &&
|
|
1474
|
+
// A node carrying a guess is the lane's: its staging is the truth beneath
|
|
1475
|
+
// the guess (`setSignal` → `laneWrite`), never re-homed by the store.
|
|
1476
|
+
!(n.i & (CONFIG_HELD | CONFIG_OVERRIDE))) {
|
|
1477
|
+
holdNode(n, txOf(t));
|
|
1478
|
+
n.m.It = t.m.It;
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
/** A chained backing's child (§7b): a raw the inner family owns, or holds
|
|
1483
|
+
* at this key, is served as the inner store's wrapper — never a fresh
|
|
1484
|
+
* raw-keyed one — so the chained targets are the same objects across
|
|
1485
|
+
* settled and pending views. */ function resolveChainedRaw(e, n, t) {
|
|
1486
|
+
const r = e.v[$TARGET];
|
|
1487
|
+
if (r.ch) {
|
|
1488
|
+
const e = resolveChainedRaw(r, n, t);
|
|
1489
|
+
return e === t ? t : wrap(e, r, n);
|
|
1490
|
+
}
|
|
1491
|
+
const o = lookupTarget(t, r.fam);
|
|
1492
|
+
if (o !== undefined) return o.px;
|
|
1493
|
+
if ((r.v[n] === t || r.pb?.[n] === t) && isWrappable(t)) return wrap(t, r, n);
|
|
1494
|
+
return t;
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
/** The backing a structural read serves: a tracked reader subscribes to the
|
|
1498
|
+
* container and reads the frame core serves it — committed or staging by
|
|
1499
|
+
* the hold rules (`read()` on the node: the node's value IS the backing);
|
|
1500
|
+
* anyone else reads by `readSource`. */ function enumerationSource(e) {
|
|
1501
|
+
if (!inDraft(e) && !writeOverride && getObserver() !== null) {
|
|
1502
|
+
// A container's arrangement guess is not a backing: the view composes
|
|
1503
|
+
// over the base it is shown over.
|
|
1504
|
+
const n = asBacking(read(getContainerNode(e)));
|
|
1505
|
+
return e.fam?.opt === true ? optHooks.view(e, n) : n;
|
|
1506
|
+
}
|
|
1507
|
+
const n = readSource(e);
|
|
1508
|
+
return optRead(e) ? optHooks.view(e, n) : n;
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
/** A read on an optimistic family that composes the lanes' values: not the
|
|
1512
|
+
* draft's (it composed already), not a derive's write-override read (the
|
|
1513
|
+
* derive composes on the truth). */ function optRead(e) {
|
|
1514
|
+
return e.fam !== null && e.fam.opt === true && !inDraft(e) && !writeOverride;
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
/** Pollution keys are never served from the prototype (core R30). */ const UNSAFE_KEYS = new Set([ "__proto__", "prototype", "constructor" ]);
|
|
1518
|
+
|
|
1519
|
+
// ---------------------------------------------------------------------------
|
|
1520
|
+
// traps
|
|
1521
|
+
const traps = {
|
|
1522
|
+
get(e, n, t) {
|
|
1523
|
+
// One typeof gates every brand-symbol compare off the hot string path.
|
|
1524
|
+
if (typeof n !== "string") {
|
|
1525
|
+
if (n === $TARGET) return e;
|
|
1526
|
+
if (n === $PROXY) return t;
|
|
1527
|
+
if (n === $OWNER) return undefined;
|
|
1528
|
+
// ownership stamp: never a user key
|
|
1529
|
+
if (n === $RECORD) return undefined;
|
|
1530
|
+
// a store is no view (see `viewOf`)
|
|
1531
|
+
// refresh()/isPending resolve the projection computed through $REFRESH.
|
|
1532
|
+
if (n === $REFRESH) return e.fam?.node ?? undefined;
|
|
1533
|
+
if (n === $TRACK) {
|
|
1534
|
+
if (e.fam !== null) pullFamily(e);
|
|
1535
|
+
if (!inDraft(e) && getObserver() !== null) {
|
|
1536
|
+
read(getContainerNode(e));
|
|
1537
|
+
// Structural chaining (§7b): a chained backing's $TRACK reads
|
|
1538
|
+
// through to the INNER store's container — structural
|
|
1539
|
+
// notifications land on the source's own node.
|
|
1540
|
+
const n = readSource(e);
|
|
1541
|
+
if (n[$TARGET] !== undefined) n[$TRACK];
|
|
1542
|
+
}
|
|
1543
|
+
return undefined;
|
|
1544
|
+
}
|
|
1545
|
+
// user symbols fall through to the generic path
|
|
1546
|
+
}
|
|
1547
|
+
// (The witness before the pull: a mark on an uninitialized derived
|
|
1548
|
+
// store is witnessed, then the pull throws — loading, declared pending.)
|
|
1549
|
+
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.ro(e, n);
|
|
1550
|
+
if (e.fam !== null) pullFamily(e);
|
|
1551
|
+
// Hot inline case: existing PLAIN node (non-accessor) read outside any
|
|
1552
|
+
// draft — the dbmon/uibench effect re-read shape. Core's `read()` serves
|
|
1553
|
+
// it (linking a tracked reader; the hold rules either way). Skips
|
|
1554
|
+
// serveDataKey's frame, the FORCE compare (only accessor keys ever hold
|
|
1555
|
+
// the sentinel), and isWrappable for primitives. ONE node-map lookup
|
|
1556
|
+
// serves this block and the accessor probe below. Before the backing is
|
|
1557
|
+
// consulted: a deleted key's node serves `undefined` (notifyWrites), and
|
|
1558
|
+
// a chained target's node carrying a GUESS is the value (A17 — the base
|
|
1559
|
+
// beneath it, and any hold there, is not this reader's concern; a link
|
|
1560
|
+
// without one reads through below).
|
|
1561
|
+
const r = e.n?.[n];
|
|
1562
|
+
if (writeScopes === null && !writeOverride) {
|
|
1563
|
+
const t = r;
|
|
1564
|
+
if (t !== undefined && t.acc !== true && (!e.ch || t.i & CONFIG_OVERRIDE)) {
|
|
1565
|
+
const r = nodeValue(t, undefined, e);
|
|
1566
|
+
if (r === null || typeof r !== "object") return r;
|
|
1567
|
+
if (e.s) return serveShallow(e, n, r);
|
|
1568
|
+
if (t.pxv === r) return t.px;
|
|
1569
|
+
if (isWrappable(r)) {
|
|
1570
|
+
const o = wrap(r, e, n);
|
|
1571
|
+
t.px = o;
|
|
1572
|
+
t.pxv = r;
|
|
1573
|
+
return o;
|
|
1574
|
+
}
|
|
1575
|
+
return r;
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
const o = readSource(e, n);
|
|
1579
|
+
// Overlay delete (#3044): a prototype overlay cannot shadow a delete, so
|
|
1580
|
+
// deleted keys are tracked aside and read as absent in the pending view.
|
|
1581
|
+
if (e.del !== null && o === e.pb && e.del.has(n)) {
|
|
1582
|
+
if (!inDraft(e) && getObserver() !== null) read(getNode(e, n));
|
|
1583
|
+
return undefined;
|
|
1584
|
+
}
|
|
1585
|
+
// Accessor keys serve through Reflect.get with the PROXY receiver
|
|
1586
|
+
// (R20/R29: internal reads track; the node is linked for shape-change
|
|
1587
|
+
// notification but its value is never served). Accessor-ness comes from
|
|
1588
|
+
// the node's cached flag; the first TRACKED read (which creates the
|
|
1589
|
+
// node) probes once — untracked node-less reads take the plain path,
|
|
1590
|
+
// where a raw-receiver getter still returns correct committed values.
|
|
1591
|
+
// Tracking suppression is PER-TARGET (inDraft), never global (#3037).
|
|
1592
|
+
let i = -1;
|
|
1593
|
+
{
|
|
1594
|
+
let l;
|
|
1595
|
+
if (r !== undefined) l = r.acc === true; else if (!inDraft(e) && getObserver() !== null) {
|
|
1596
|
+
l = isOwnAccessor(o, n);
|
|
1597
|
+
if (o === (e.pb ?? e.v)) i = l ? 1 : 0;
|
|
1598
|
+
} else l = false;
|
|
1599
|
+
if (l) {
|
|
1600
|
+
if (!inDraft(e) && getObserver() !== null) read(r ?? getNode(e, n, i));
|
|
1601
|
+
const l = Reflect.get(o, n, t);
|
|
1602
|
+
if (e.s) return serveShallow(e, n, l);
|
|
1603
|
+
return isWrappable(l) ? draftServe(e, wrap(l, e, n)) : l;
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
// Plain-data fast path: no descriptor allocation per read. Inherited
|
|
1607
|
+
// pollution keys are never served (core R30) — checked before the
|
|
1608
|
+
// proto-function branch can leak `constructor`. Overlay pending backings
|
|
1609
|
+
// chain to the committed backing, so "own in the view" means own on
|
|
1610
|
+
// either layer — a genuine prototype method is one that is own on
|
|
1611
|
+
// NEITHER.
|
|
1612
|
+
const l = e.ovl && o === e.pb;
|
|
1613
|
+
if ((n === "constructor" || n === "__proto__" || n === "prototype") && !hasOwn.call(o, n) && !(l && hasOwn.call(e.v, n))) return undefined;
|
|
1614
|
+
let u = o[n];
|
|
1615
|
+
if (u === undefined ? !hasOwn.call(o, n) && !(l && hasOwn.call(e.v, n)) : false) {
|
|
1616
|
+
// Inherited: prototype getters/methods run with the proxy receiver.
|
|
1617
|
+
u = Reflect.get(o, n, t);
|
|
1618
|
+
if (typeof u === "function") return u;
|
|
1619
|
+
// proto methods untracked
|
|
1620
|
+
// Read-through (§7b): the inner store answers an absent key too — the
|
|
1621
|
+
// outer's node is a subscription point unless it carries a guess.
|
|
1622
|
+
if (e.ch && o === e.v) return serveDataKey(e, n, u, o, r, i);
|
|
1623
|
+
// Reading a currently-absent own key subscribes to it (R12) — for any
|
|
1624
|
+
// target OUTSIDE its own draft scope, even mid-setter (#3037, above).
|
|
1625
|
+
if (u === undefined && !inDraft(e)) {
|
|
1626
|
+
if (getObserver() !== null) read(getNode(e, n, i));
|
|
1627
|
+
const t = e.n?.[n];
|
|
1628
|
+
if (t) {
|
|
1629
|
+
const r = nodeValue(t, undefined, e);
|
|
1630
|
+
if (e.s) return serveShallow(e, n, r);
|
|
1631
|
+
return isWrappable(r) ? draftServe(e, wrap(r, e, n)) : r;
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
if (e.s) return serveShallow(e, n, u);
|
|
1635
|
+
return isWrappable(u) ? draftServe(e, wrap(u, e, n)) : u;
|
|
1636
|
+
}
|
|
1637
|
+
if (typeof u === "function" && !hasOwn.call(o, n) && !(l && hasOwn.call(e.v, n))) return u;
|
|
1638
|
+
// proto method
|
|
1639
|
+
return serveDataKey(e, n, u, o, r, i);
|
|
1640
|
+
},
|
|
1641
|
+
has(e, n) {
|
|
1642
|
+
if (n === $TARGET || n === $PROXY || n === $TRACK) return true;
|
|
1643
|
+
if (n === $OWNER || n === $RECORD) return false;
|
|
1644
|
+
// (The witness before the pull: a mark on an uninitialized derived
|
|
1645
|
+
// store is witnessed, then the pull throws — loading, declared pending.)
|
|
1646
|
+
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.ro(e, n);
|
|
1647
|
+
if (e.fam !== null) pullFamily(e);
|
|
1648
|
+
const t = readSource(e, n);
|
|
1649
|
+
// A tracked reader's presence node answers (born from the two frames;
|
|
1650
|
+
// core decides which it sees); everyone else reads the backing. A
|
|
1651
|
+
// chained target's node is a subscription point (§7b): the inner store
|
|
1652
|
+
// answers through the backing — unless the outer node carries a guess.
|
|
1653
|
+
if (!inDraft(e) && !writeOverride) {
|
|
1654
|
+
if (getObserver() !== null) {
|
|
1655
|
+
const r = getHasNode(e, n);
|
|
1656
|
+
const o = read(r);
|
|
1657
|
+
if (!e.ch || t !== e.v) return !!o;
|
|
1658
|
+
const i = n in t;
|
|
1659
|
+
r.Ae = i;
|
|
1660
|
+
// the link follows the inner store (S4)
|
|
1661
|
+
if (r.i & CONFIG_OVERRIDE) return !!o;
|
|
1662
|
+
return i;
|
|
1663
|
+
} else if (optRead(e)) {
|
|
1664
|
+
const t = optHooks.has(e, n);
|
|
1665
|
+
if (t !== undefined) return t;
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
return n in t && !(e.del !== null && t === e.pb && e.del.has(n));
|
|
1669
|
+
},
|
|
1670
|
+
ownKeys(e) {
|
|
1671
|
+
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.ro(e, undefined);
|
|
1672
|
+
if (e.fam !== null) pullFamily(e);
|
|
1673
|
+
return visibleKeys(e, enumerationSource(e));
|
|
1674
|
+
},
|
|
1675
|
+
getOwnPropertyDescriptor(e, n) {
|
|
1676
|
+
if (n === $OWNER || n === $RECORD) return undefined;
|
|
1677
|
+
const t = getObserver();
|
|
1678
|
+
if (verdict !== null && affectsHooks !== null && t === null) affectsHooks.ro(e, n);
|
|
1679
|
+
if (e.fam !== null) pullFamily(e);
|
|
1680
|
+
// An enumerator (spread, Object.entries) already holds the container
|
|
1681
|
+
// node and reads its frame; a descriptor read on its own tracks
|
|
1682
|
+
// presence (R13).
|
|
1683
|
+
const r = t !== null && !inDraft(e) && !writeOverride;
|
|
1684
|
+
const o = r && observerHoldsContainer(e, t);
|
|
1685
|
+
let i;
|
|
1686
|
+
if (o) i = enumerationSource(e); else if (r) {
|
|
1687
|
+
// The presence node's frame (core decides which) is the one described
|
|
1688
|
+
// (a chained target's inner store answers through the backing, §7b);
|
|
1689
|
+
// the descriptor itself comes from the backing the container's frame
|
|
1690
|
+
// serves this reader (`readSource`: a held container's changed key —
|
|
1691
|
+
// enumerability, accessor-ness — reads committed for a stale reader,
|
|
1692
|
+
// the staging for a member; #3706 contrast).
|
|
1693
|
+
const t = getHasNode(e, n);
|
|
1694
|
+
const r = read(t);
|
|
1695
|
+
i = readSource(e, n, true);
|
|
1696
|
+
if (!r && (!e.ch || t.i & CONFIG_OVERRIDE || !(n in i))) return undefined;
|
|
1697
|
+
} else i = readSource(e, n, true);
|
|
1698
|
+
let l;
|
|
1699
|
+
if (!o && optRead(e)) {
|
|
1700
|
+
const t = optHooks.descriptor(e, n);
|
|
1701
|
+
if (t === null) return undefined;
|
|
1702
|
+
l = t;
|
|
1703
|
+
}
|
|
1704
|
+
l ??= visibleDescriptor(e, i, n);
|
|
1705
|
+
if (l === undefined) return undefined;
|
|
1706
|
+
if (!(n === "length" && Array.isArray(e))) l.configurable = true;
|
|
1707
|
+
return l;
|
|
1708
|
+
},
|
|
1709
|
+
set(e, n, t) {
|
|
1710
|
+
const r = inDraft(e);
|
|
1711
|
+
const o = !r && writeOverride;
|
|
1712
|
+
if (!r && !o) return true;
|
|
1713
|
+
if (n === "__proto__") return true;
|
|
1714
|
+
// pollution guard (core R30)
|
|
1715
|
+
const i = e.s ? t : unwrapValue(t);
|
|
1716
|
+
const l = ensurePB(e);
|
|
1717
|
+
pendingNotify.add(e);
|
|
1718
|
+
if (Array.isArray(l)) {
|
|
1719
|
+
if (n === "length") e.wk = WK_ALL; else if (e.wk !== WK_ALL) {
|
|
1720
|
+
const t = e.wk ??= new Set;
|
|
1721
|
+
t.add(n);
|
|
1722
|
+
t.add("length");
|
|
1723
|
+
}
|
|
1724
|
+
} else {
|
|
1725
|
+
if (e.wk !== WK_ALL) (e.wk ??= new Set).add(n);
|
|
1726
|
+
if (!(n in l)) e.kc++;
|
|
1727
|
+
}
|
|
1728
|
+
if (UNSAFE_KEYS.has(n)) {
|
|
1729
|
+
Object.defineProperty(l, n, {
|
|
1730
|
+
value: i,
|
|
1731
|
+
writable: true,
|
|
1732
|
+
enumerable: true,
|
|
1733
|
+
configurable: true
|
|
1734
|
+
});
|
|
1735
|
+
if (e.del !== null) e.del.delete(n);
|
|
1736
|
+
return true;
|
|
1737
|
+
}
|
|
1738
|
+
if (e.ovl && e.sc !== 2 && !hasOwn.call(l, n)) {
|
|
1739
|
+
Object.defineProperty(l, n, {
|
|
1740
|
+
value: i,
|
|
1741
|
+
writable: true,
|
|
1742
|
+
enumerable: true,
|
|
1743
|
+
configurable: true
|
|
1744
|
+
});
|
|
1745
|
+
} else l[n] = i;
|
|
1746
|
+
if (e.del !== null) e.del.delete(n);
|
|
1747
|
+
if (e.s && i !== null && typeof i === "object") markRawOne(i);
|
|
1748
|
+
if (o) notifyWrites(e);
|
|
1749
|
+
return true;
|
|
1750
|
+
},
|
|
1751
|
+
defineProperty(e, n, t) {
|
|
1752
|
+
const r = inDraft(e);
|
|
1753
|
+
const o = !r && writeOverride;
|
|
1754
|
+
if (!r && !o) return true;
|
|
1755
|
+
if (n === "__proto__") return true;
|
|
1756
|
+
if (t.get || t.set) e.a = true;
|
|
1757
|
+
if ("value" in t) t = {
|
|
1758
|
+
...t,
|
|
1759
|
+
value: unwrapValue(t.value)
|
|
1760
|
+
};
|
|
1761
|
+
const i = ensurePB(e);
|
|
1762
|
+
if (e.a || !(t.enumerable && t.writable && t.configurable)) e.sc = 1;
|
|
1763
|
+
pendingNotify.add(e);
|
|
1764
|
+
if (e.wk !== WK_ALL) (e.wk ??= new Set).add(n);
|
|
1765
|
+
Object.defineProperty(i, n, t);
|
|
1766
|
+
if (e.del !== null) e.del.delete(n);
|
|
1767
|
+
if (o) notifyWrites(e);
|
|
1768
|
+
return true;
|
|
1769
|
+
},
|
|
1770
|
+
deleteProperty(e, n) {
|
|
1771
|
+
const t = inDraft(e);
|
|
1772
|
+
const r = !t && writeOverride;
|
|
1773
|
+
if (!t && !r) return true;
|
|
1774
|
+
const o = ensurePB(e);
|
|
1775
|
+
pendingNotify.add(e);
|
|
1776
|
+
if (e.wk !== WK_ALL) (e.wk ??= new Set).add(n);
|
|
1777
|
+
delete o[n];
|
|
1778
|
+
if (e.ovl && hasOwn.call(e.v, n)) (e.del ??= new Set).add(n);
|
|
1779
|
+
if (r) notifyWrites(e);
|
|
1780
|
+
return true;
|
|
1781
|
+
}
|
|
1782
|
+
};
|
|
1783
|
+
|
|
1784
|
+
/** Is `obs` subscribed to the container BY THIS PASS (its enumerator holds
|
|
1785
|
+
* it)? A link left from a previous pass is stale — the reader must subscribe
|
|
1786
|
+
* to the key's presence node or a later add never re-runs it. */ function observerHoldsContainer(e, n) {
|
|
1787
|
+
const t = e.k;
|
|
1788
|
+
if (t === null) return false;
|
|
1789
|
+
const r = t.Dt;
|
|
1790
|
+
return r !== null && r.l === n && (!(n.R & REACTIVE_RECOMPUTING_DEPS) || r.Pe === n.pe);
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
function storeSetter(e, n, t = true) {
|
|
1794
|
+
const r = e[$TARGET];
|
|
1795
|
+
const o = writeScopes;
|
|
1796
|
+
writeScopes = new Set;
|
|
1797
|
+
writeScopes.add(scopeKey(r));
|
|
1798
|
+
const i = userWrite;
|
|
1799
|
+
userWrite = t;
|
|
1800
|
+
writing++;
|
|
1801
|
+
let l;
|
|
1802
|
+
try {
|
|
1803
|
+
// No untrack: the writing flag already disables store-node linking
|
|
1804
|
+
// (draft reads never self-track, proj R2), while EXTERNAL reads (signals
|
|
1805
|
+
// inside a projection derive) must keep tracking — they are the derive's
|
|
1806
|
+
// dependencies.
|
|
1807
|
+
l = n(e);
|
|
1808
|
+
} finally {
|
|
1809
|
+
writing--;
|
|
1810
|
+
writeScopes = o;
|
|
1811
|
+
// Outermost setter exit: emit write-time notifications (one node write
|
|
1812
|
+
// per changed observed key) so holds and lanes engage now.
|
|
1813
|
+
if (writing === 0 && pendingNotify.size) {
|
|
1814
|
+
const e = [ ...pendingNotify ];
|
|
1815
|
+
pendingNotify.clear();
|
|
1816
|
+
for (const n of e) notifyWrites(n);
|
|
1817
|
+
// A direct commit (a projection's creation run) folds at the setter's
|
|
1818
|
+
// exit, unqueued: the backing is the pre-batch one (nothing swapped).
|
|
1819
|
+
if (directCommit) for (const n of e) if (n.pb !== null) foldTarget(n, n.v);
|
|
1820
|
+
}
|
|
1821
|
+
userWrite = i;
|
|
1822
|
+
}
|
|
1823
|
+
if (l !== undefined && l !== e && isWrappable(l)) {
|
|
1824
|
+
// A returned replacement: on an optimistic family a user's is itself an
|
|
1825
|
+
// optimistic edit — diffed against the view as guesses (`next` parity);
|
|
1826
|
+
// otherwise it is adopted as the incoming truth.
|
|
1827
|
+
if (t && r.fam?.opt === true && !writeOverride) optHooks.writes(r, unwrapValue(l)); else adoptPB(r, unwrapValue(l));
|
|
1828
|
+
}
|
|
1829
|
+
// A34 (3), #3612 — the derived store's twin of `setMemo`: a user write to
|
|
1830
|
+
// a derivation another transaction holds is nobody's proposal; it becomes
|
|
1831
|
+
// the draft's prior state and the hold re-derives over it (the writes
|
|
1832
|
+
// joined the hold through `setSignal`; the derive re-runs under it).
|
|
1833
|
+
if (t && heldDerivationHit) {
|
|
1834
|
+
heldDerivationHit = false;
|
|
1835
|
+
const e = r.fam.node;
|
|
1836
|
+
if (!(e.R & REACTIVE_DIRTY)) {
|
|
1837
|
+
e.R = e.R & ~REACTIVE_CHECK | REACTIVE_DIRTY;
|
|
1838
|
+
insertIntoHeap(e, dirtyQueue);
|
|
1839
|
+
schedule();
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
function createStore(e, n) {
|
|
1845
|
+
const t = n?.shallow === true;
|
|
1846
|
+
const r = wrap(e);
|
|
1847
|
+
if (t) {
|
|
1848
|
+
r[$TARGET].s = true;
|
|
1849
|
+
markRawIngest(e);
|
|
1850
|
+
}
|
|
1851
|
+
{
|
|
1852
|
+
const e = getOwner();
|
|
1853
|
+
// Dev-tier graph registration (owner signal lists, onGraph); the
|
|
1854
|
+
// `_owner` write itself never reaches the proxy, see storeOwners.
|
|
1855
|
+
registerGraph(r, e);
|
|
1856
|
+
// Only once the engine is installed, like the node stamping it feeds: a
|
|
1857
|
+
// WeakMap.set per fresh store is a growing ephemeron table and,
|
|
1858
|
+
// disabled, buys nothing.
|
|
1859
|
+
if (attrHooks !== null) {
|
|
1860
|
+
storeOwners.set(r[$TARGET], e);
|
|
1861
|
+
nameStore(r, n?.name);
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
const setter = e => storeSetter(r, e);
|
|
1865
|
+
return [ r, setter ];
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
/** True when `proxy` is a SHALLOW store (children served verbatim, slots
|
|
1869
|
+
* replaced by reference — #2932). The list driver uses this to choose the
|
|
1870
|
+
* slot-patch channel (collected row bodies) over per-record registration. */ function storeIsShallow(e) {
|
|
1871
|
+
const n = e?.[$TARGET];
|
|
1872
|
+
return n !== undefined && n.s === true;
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
/** True when `proxy` belongs to a projection family (S3). */ function storeHasFamily(e) {
|
|
1876
|
+
const n = e?.[$TARGET];
|
|
1877
|
+
return n !== undefined && n.fam !== null;
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
/** True when `proxy` belongs to an OPTIMISTIC family (S4). */ function storeHasOptimisticFamily(e) {
|
|
1881
|
+
const n = e?.[$TARGET];
|
|
1882
|
+
return n !== undefined && n.fam?.opt === true;
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
// ---------------------------------------------------------------------------
|
|
1886
|
+
// enumeration views (one visibility rule for the traps AND the deep walk)
|
|
1887
|
+
function visibleKeys(e, n) {
|
|
1888
|
+
let t;
|
|
1889
|
+
if (e.ovl && n === e.pb) {
|
|
1890
|
+
t = Reflect.ownKeys(e.v);
|
|
1891
|
+
const r = e.del;
|
|
1892
|
+
if (r !== null && r.size !== 0) t = t.filter(e => !r.has(e));
|
|
1893
|
+
for (const r of Reflect.ownKeys(n)) {
|
|
1894
|
+
if (!hasOwn.call(e.v, r)) t.push(r);
|
|
1895
|
+
}
|
|
1896
|
+
} else t = Reflect.ownKeys(n);
|
|
1897
|
+
// The ownership stamp is the last symbol on an owned backing: hide it.
|
|
1898
|
+
for (let e = t.length - 1; e >= 0 && typeof t[e] === "symbol"; e--) {
|
|
1899
|
+
if (t[e] === $OWNER) {
|
|
1900
|
+
t.splice(e, 1);
|
|
1901
|
+
break;
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
return t;
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
function visibleDescriptor(e, n, t) {
|
|
1908
|
+
let r = Object.getOwnPropertyDescriptor(n, t);
|
|
1909
|
+
if (e.ovl && n === e.pb) {
|
|
1910
|
+
if (e.del !== null && e.del.has(t)) return undefined;
|
|
1911
|
+
if (r === undefined) r = Object.getOwnPropertyDescriptor(e.v, t);
|
|
1912
|
+
}
|
|
1913
|
+
return r;
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
// ---------------------------------------------------------------------------
|
|
1917
|
+
// deep / snapshot
|
|
1918
|
+
/** Tracking snapshot: subscribes every reachable record's container and deep
|
|
1919
|
+
* witness (one node per record, not one per path), then snapshots. */ function deep(e) {
|
|
1920
|
+
const n = e?.[$TARGET];
|
|
1921
|
+
if (n === undefined || n.px !== e) return e;
|
|
1922
|
+
const t = new Set;
|
|
1923
|
+
const childTarget = (e, n, t) => {
|
|
1924
|
+
let r = lookupTarget(n, e.fam);
|
|
1925
|
+
if (r === undefined) {
|
|
1926
|
+
if (!isWrappable(n)) return undefined;
|
|
1927
|
+
wrap(n, e, t);
|
|
1928
|
+
r = lookupTarget(n, e.fam) ?? n[$TARGET];
|
|
1929
|
+
}
|
|
1930
|
+
return r;
|
|
1931
|
+
};
|
|
1932
|
+
const walkT = e => {
|
|
1933
|
+
if (e.fam !== null) pullFamily(e);
|
|
1934
|
+
const n = enumerationSource(e);
|
|
1935
|
+
if (t.has(n)) return;
|
|
1936
|
+
t.add(n);
|
|
1937
|
+
read(getDeepNode(e));
|
|
1938
|
+
// Through a chain (#3323): every inner record's container AND deep
|
|
1939
|
+
// witness — base writes bump the inner witnesses.
|
|
1940
|
+
for (let n = e; n.ch; ) {
|
|
1941
|
+
n = n.v[$TARGET];
|
|
1942
|
+
read(getContainerNode(n));
|
|
1943
|
+
read(getDeepNode(n));
|
|
1944
|
+
}
|
|
1945
|
+
for (const t of visibleKeys(e, n)) {
|
|
1946
|
+
const r = visibleDescriptor(e, n, t);
|
|
1947
|
+
if (r === undefined) continue;
|
|
1948
|
+
if (r.get || r.set) {
|
|
1949
|
+
e.a = true;
|
|
1950
|
+
continue;
|
|
1951
|
+
// accessors track through their own reads when invoked
|
|
1952
|
+
}
|
|
1953
|
+
let o = r.value;
|
|
1954
|
+
if (o === null || typeof o !== "object") continue;
|
|
1955
|
+
if (e.ch && o[$TARGET] === undefined) o = resolveChainedRaw(e, t, o);
|
|
1956
|
+
const i = childTarget(e, o, t);
|
|
1957
|
+
if (i === undefined) continue;
|
|
1958
|
+
// raw-marked: leaf by contract
|
|
1959
|
+
walkT(i);
|
|
1960
|
+
}
|
|
1961
|
+
};
|
|
1962
|
+
walkT(n);
|
|
1963
|
+
return snapshot(e);
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
/** Non-tracking snapshot: source identity for unowned subtrees (zero copy),
|
|
1967
|
+
* a copy for owned ones (they are by definition modified relative to the
|
|
1968
|
+
* source). Sees the pending backing (R27). */ function snapshot(e) {
|
|
1969
|
+
const n = e?.[$TARGET];
|
|
1970
|
+
return snapshotWalk(e, new Map, n?.fam ?? null);
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
function snapshotWalk(e, n, t) {
|
|
1974
|
+
if (e === null || typeof e !== "object") return e;
|
|
1975
|
+
let r = e;
|
|
1976
|
+
for (let e = true; ;e = false) {
|
|
1977
|
+
const n = r?.[$TARGET]?.v !== undefined;
|
|
1978
|
+
let o = n ? r[$TARGET] : undefined;
|
|
1979
|
+
if (o === undefined && t !== null) o = lookupTarget(r, t);
|
|
1980
|
+
if (o === undefined) o = lookupTarget(r, null);
|
|
1981
|
+
if (o === undefined) break;
|
|
1982
|
+
if (e && !n && t !== null && o.fam !== t) {
|
|
1983
|
+
const e = t.map.get(o.px);
|
|
1984
|
+
if (e !== undefined) o = e;
|
|
1985
|
+
}
|
|
1986
|
+
if (o.fam !== null) t = o.fam;
|
|
1987
|
+
// R27: a snapshot sees the batch's pending backing — a held one (another
|
|
1988
|
+
// transaction's future) only from its own draft.
|
|
1989
|
+
let i = o.v;
|
|
1990
|
+
if (o.pb !== null && (inDraft(o) || writeOverride || o.k === null || !(o.k.i & CONFIG_HELD))) {
|
|
1991
|
+
if (o.ovl) materializePB(o);
|
|
1992
|
+
i = o.pb;
|
|
1993
|
+
}
|
|
1994
|
+
if (optRead(o)) i = optHooks.view(o, i);
|
|
1995
|
+
if (i === r) break;
|
|
1996
|
+
r = i;
|
|
1997
|
+
}
|
|
1998
|
+
if (!isWrappable(r)) return r;
|
|
1999
|
+
const o = n.get(r);
|
|
2000
|
+
if (o !== undefined) return o;
|
|
2001
|
+
if (isOwned(r)) {
|
|
2002
|
+
const e = Array.isArray(r);
|
|
2003
|
+
const o = e ? [] : Object.create(Object.getPrototypeOf(r));
|
|
2004
|
+
n.set(r, o);
|
|
2005
|
+
for (const i of Reflect.ownKeys(r)) {
|
|
2006
|
+
if (e && i === "length" || i === $OWNER) continue;
|
|
2007
|
+
const l = Object.getOwnPropertyDescriptor(r, i);
|
|
2008
|
+
if (typeof i === "symbol" && !l.enumerable) continue;
|
|
2009
|
+
if (l.get || l.set) {
|
|
2010
|
+
Object.defineProperty(o, i, l);
|
|
2011
|
+
continue;
|
|
2012
|
+
}
|
|
2013
|
+
const u = l.value;
|
|
2014
|
+
const f = u !== null && typeof u === "object" ? snapshotWalk(u, n, t) : u;
|
|
2015
|
+
if (l.enumerable && l.writable && l.configurable) o[i] = f; else Object.defineProperty(o, i, {
|
|
2016
|
+
...l,
|
|
2017
|
+
value: f
|
|
2018
|
+
});
|
|
2019
|
+
}
|
|
2020
|
+
if (e && o.length !== r.length) o.length = r.length;
|
|
2021
|
+
return o;
|
|
2022
|
+
}
|
|
2023
|
+
n.set(r, r);
|
|
2024
|
+
let i = null;
|
|
2025
|
+
for (const e of Reflect.ownKeys(r)) {
|
|
2026
|
+
const o = Object.getOwnPropertyDescriptor(r, e);
|
|
2027
|
+
if (!o || o.get || o.set) continue;
|
|
2028
|
+
const l = o.value;
|
|
2029
|
+
if (l === null || typeof l !== "object") continue;
|
|
2030
|
+
const u = snapshotWalk(l, n, t);
|
|
2031
|
+
if (u !== l) {
|
|
2032
|
+
if (i === null) {
|
|
2033
|
+
i = Array.isArray(r) ? [ ...r ] : Object.create(Object.getPrototypeOf(r), Object.getOwnPropertyDescriptors(r));
|
|
2034
|
+
n.set(r, i);
|
|
2035
|
+
}
|
|
2036
|
+
i[e] = u;
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
return i ?? r;
|
|
2040
|
+
}
|
|
2041
|
+
|
|
2042
|
+
export { $PROXY, $TARGET, $TRACK, LaneView, adoptPB, arrayStructureChanged, asBacking, bumpDeep, cloneRaw, committed, containerEquals, createStore, deep, getContainerNode, getHasNode, getNode, hasAccessorFlag, materializePB, membershipChanged, nameStore, notifyFold, notifyFoldTail, notifyKeyDiff, notifyKeyValue, resolveChainedRaw, runDirect, sameKey, sameRow, snapshot, storeHasFamily, storeHasOptimisticFamily, storeIsShallow, storeSetter, targetsEqual, unwrapValue, userWriting, wrap };
|