@solidjs/signals 2.0.0-rc.2 → 2.0.0-rc.20

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