@solidjs/signals 2.0.0-beta.9 → 2.0.0-rc.1

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 (106) hide show
  1. package/README.md +14 -10
  2. package/dist/dev.js +6740 -2011
  3. package/dist/node.cjs +7927 -3340
  4. package/dist/prod/affects.js +126 -0
  5. package/dist/prod/boundaries.js +572 -0
  6. package/dist/prod/core/action.js +139 -0
  7. package/dist/prod/core/async.js +607 -0
  8. package/dist/prod/core/constants.js +92 -0
  9. package/dist/prod/core/context.js +67 -0
  10. package/dist/prod/core/core.js +828 -0
  11. package/dist/prod/core/dev.js +3 -0
  12. package/dist/prod/core/effect.js +145 -0
  13. package/dist/prod/core/error.js +68 -0
  14. package/dist/prod/core/external.js +98 -0
  15. package/dist/prod/core/graph.js +104 -0
  16. package/dist/prod/core/heap.js +140 -0
  17. package/dist/prod/core/invariants.js +42 -0
  18. package/dist/prod/core/lanes.js +140 -0
  19. package/dist/prod/core/optimistic.js +262 -0
  20. package/dist/prod/core/owner.js +302 -0
  21. package/dist/prod/core/scheduler.js +757 -0
  22. package/dist/prod/core/verdict.js +369 -0
  23. package/dist/prod/index.js +45 -0
  24. package/dist/prod/map.js +319 -0
  25. package/dist/prod/signals.js +394 -0
  26. package/dist/prod/store/index.js +36 -0
  27. package/dist/prod/store/next/optimistic.js +377 -0
  28. package/dist/prod/store/next/projection.js +172 -0
  29. package/dist/prod/store/next/reconcile.js +327 -0
  30. package/dist/prod/store/next/store.js +1232 -0
  31. package/dist/prod/store/next/target.js +20 -0
  32. package/dist/prod/store/store.js +325 -0
  33. package/dist/prod/store/storePath.js +103 -0
  34. package/dist/prod/store/utils.js +201 -0
  35. package/dist/types/affects.d.ts +47 -0
  36. package/dist/types/boundaries.d.ts +60 -13
  37. package/dist/types/core/action.d.ts +35 -6
  38. package/dist/types/core/async.d.ts +16 -1
  39. package/dist/types/core/constants.d.ts +35 -6
  40. package/dist/types/core/context.d.ts +10 -2
  41. package/dist/types/core/core.d.ts +66 -63
  42. package/dist/types/core/dev.d.ts +17 -2
  43. package/dist/types/core/effect.d.ts +1 -2
  44. package/dist/types/core/error.d.ts +33 -0
  45. package/dist/types/core/external.d.ts +0 -11
  46. package/dist/types/core/graph.d.ts +2 -1
  47. package/dist/types/core/heap.d.ts +8 -0
  48. package/dist/types/core/index.d.ts +3 -2
  49. package/dist/types/core/invariants.d.ts +59 -0
  50. package/dist/types/core/lanes.d.ts +2 -0
  51. package/dist/types/core/optimistic.d.ts +6 -0
  52. package/dist/types/core/owner.d.ts +50 -3
  53. package/dist/types/core/scheduler.d.ts +85 -11
  54. package/dist/types/core/types.d.ts +66 -1
  55. package/dist/types/core/verdict.d.ts +2 -0
  56. package/dist/types/index.d.ts +3 -2
  57. package/dist/types/map.d.ts +21 -5
  58. package/dist/types/signals.d.ts +183 -12
  59. package/dist/types/store/index.d.ts +16 -6
  60. package/dist/types/store/next/optimistic.d.ts +20 -0
  61. package/dist/types/store/next/projection.d.ts +24 -0
  62. package/dist/types/store/next/reconcile.d.ts +3 -0
  63. package/dist/types/store/next/store.d.ts +71 -0
  64. package/dist/types/store/next/target.d.ts +99 -0
  65. package/dist/types/store/optimistic.d.ts +3 -3
  66. package/dist/types/store/projection.d.ts +10 -6
  67. package/dist/types/store/reconcile.d.ts +31 -8
  68. package/dist/types/store/store.d.ts +91 -71
  69. package/dist/types/store/utils.d.ts +0 -40
  70. package/dist/types-cjs/affects.d.cts +47 -0
  71. package/dist/types-cjs/boundaries.d.cts +60 -13
  72. package/dist/types-cjs/core/action.d.cts +35 -6
  73. package/dist/types-cjs/core/async.d.cts +16 -1
  74. package/dist/types-cjs/core/constants.d.cts +35 -6
  75. package/dist/types-cjs/core/context.d.cts +10 -2
  76. package/dist/types-cjs/core/core.d.cts +66 -63
  77. package/dist/types-cjs/core/dev.d.cts +17 -2
  78. package/dist/types-cjs/core/effect.d.cts +1 -2
  79. package/dist/types-cjs/core/error.d.cts +33 -0
  80. package/dist/types-cjs/core/external.d.cts +0 -11
  81. package/dist/types-cjs/core/graph.d.cts +2 -1
  82. package/dist/types-cjs/core/heap.d.cts +8 -0
  83. package/dist/types-cjs/core/index.d.cts +3 -2
  84. package/dist/types-cjs/core/invariants.d.cts +59 -0
  85. package/dist/types-cjs/core/lanes.d.cts +2 -0
  86. package/dist/types-cjs/core/optimistic.d.cts +6 -0
  87. package/dist/types-cjs/core/owner.d.cts +50 -3
  88. package/dist/types-cjs/core/scheduler.d.cts +85 -11
  89. package/dist/types-cjs/core/types.d.cts +66 -1
  90. package/dist/types-cjs/core/verdict.d.cts +2 -0
  91. package/dist/types-cjs/index.d.cts +3 -2
  92. package/dist/types-cjs/map.d.cts +21 -5
  93. package/dist/types-cjs/signals.d.cts +183 -12
  94. package/dist/types-cjs/store/index.d.cts +16 -6
  95. package/dist/types-cjs/store/next/optimistic.d.cts +20 -0
  96. package/dist/types-cjs/store/next/projection.d.cts +24 -0
  97. package/dist/types-cjs/store/next/reconcile.d.cts +3 -0
  98. package/dist/types-cjs/store/next/store.d.cts +71 -0
  99. package/dist/types-cjs/store/next/target.d.cts +99 -0
  100. package/dist/types-cjs/store/optimistic.d.cts +3 -3
  101. package/dist/types-cjs/store/projection.d.cts +10 -6
  102. package/dist/types-cjs/store/reconcile.d.cts +31 -8
  103. package/dist/types-cjs/store/store.d.cts +91 -71
  104. package/dist/types-cjs/store/utils.d.cts +0 -40
  105. package/package.json +12 -9
  106. package/dist/prod.js +0 -3627
@@ -0,0 +1,1232 @@
1
+ import { unwrapOverride, $REFRESH, CONFIG_OWNED_WRITE, NOT_PENDING, STATUS_UNINITIALIZED, STATUS_ERROR, CONFIG_CHILDREN_FORBIDDEN } from "../../core/constants.js";
2
+
3
+ import { setSignal, isEqual, read, pendingCheckActive, readNodeFast, READ_SLOW, signal } from "../../core/core.js";
4
+
5
+ import { projectionWriteActive, setProjectionWriteActive, schedule, setStoreCommitHook } from "../../core/scheduler.js";
6
+
7
+ import { getObserver, getOwner } from "../../core/owner.js";
8
+
9
+ import { $TARGET, isWrappable, markRawIngest, $PROXY, getWriteOverride, markRawOne, witnessAffectsMark, $TRACK, affectsScopesLive, inheritAffectsMarks, $AFFECTS, rawValuesUsed, isRawValue, setNextAffectsNodeResolver } from "../store.js";
10
+
11
+ import { storeNextLookup, optHooks, ownedRaw, devAssertNeverUserMutation } from "./target.js";
12
+
13
+ /**
14
+ * Store rewrite — increment 2: plain deep stores with pending-backing writes.
15
+ * Contract: INTERNALS-STORE-STATE.md.
16
+ *
17
+ * Write model (RUL-1, unified): the first draft write to a target creates its
18
+ * pending backing `pb` — a descriptor-preserving CoW clone. The draft mutates
19
+ * `pb` natively (array methods, defineProperty, deletes all just work).
20
+ * Reads: drafts and owner-context reads see `pb`; context-free reads see the
21
+ * committed `b` until flush. At flush commit (core's storeCommitHook), each
22
+ * written target folds: diff old `b` vs new backing notifies exactly the
23
+ * changed keys through equality-gated nodes, then `b` becomes the new
24
+ * backing. Setter return-value replacement parks the UNOWNED incoming object
25
+ * in `pb` — adoption: fold swaps it in, ownership resets (2026-08-16c).
26
+ *
27
+ * Nodes carry no pending state — they are pure subscription points; `pb` is
28
+ * the pending home. Laziness: a written target with no subscriptions folds as
29
+ * a pointer swap with zero node work.
30
+ */
31
+ // ---------------------------------------------------------------------------
32
+ // wrap / dedupe
33
+ function createTarget(e, t, n, r = t?.fam ?? null) {
34
+ // The proxy target carries the array exotic class when the value is an
35
+ // array, so Array.isArray(proxy) is true; the fields live on it directly.
36
+ // Direct field assignment in one fixed order (no Object.assign literal
37
+ // copy): every target shares a hidden-class transition chain — createTarget
38
+ // was the #2 store cost in the uibench creation profile.
39
+ const i = Array.isArray(e) ? [] : {};
40
+ i.v = e;
41
+ // Chained-backing flag (backing IS another store's proxy, §7b) — cached so
42
+ // the hot read path never does a per-read symbol lookup on the backing.
43
+ i.ch = e[$TARGET] !== undefined;
44
+ i.pb = null;
45
+ i.n = null;
46
+ i.h = null;
47
+ i.k = null;
48
+ i.dk = null;
49
+ i.u = t;
50
+ i.pk = n;
51
+ i.px = null;
52
+ i.d = false;
53
+ i.a = false;
54
+ i.sc = false;
55
+ i.nc = 0;
56
+ i.adopted = false;
57
+ i.fam = r;
58
+ i.s = false;
59
+ i.px = new Proxy(i, traps);
60
+ // Legacy interop: shared machinery (affects walks, wrap dedupe) reads the
61
+ // proxy off looked-up targets as a field.
62
+ i[$PROXY] = i.px;
63
+ (r?.map ?? storeNextLookup).set(e, i);
64
+ return i;
65
+ }
66
+
67
+ function wrapNext(e, t = null, n = null, r = t?.fam ?? null) {
68
+ // markRaw'd values never wrap through ANY store (R42; sticky raw-marking
69
+ // is one half of the never-both-wrapped-and-raw invariant, RUL-12).
70
+ if (rawValuesUsed && isRawValue(e)) return e;
71
+ const i = (r?.map ?? storeNextLookup).get(e);
72
+ if (i !== undefined) return i.px;
73
+ const o = e[$TARGET];
74
+ if (o !== undefined && o.px === e) {
75
+ // Foreign-family proxies re-wrap into THIS family (writes stay isolated);
76
+ // same-family and plain-store proxies pass through.
77
+ if (r === null || o.fam === r) return e;
78
+ return createTarget(e, t, n, r).px;
79
+ }
80
+ return createTarget(e, t, n, r).px;
81
+ }
82
+
83
+ /** Unwrap our own proxies to their current backing; leave everything else. */ function unwrapValue(e) {
84
+ if (e == null || typeof e !== "object") return e;
85
+ const t = e[$TARGET];
86
+ if (t !== undefined && t.px === e && t.v !== undefined) return t.pb ?? t.v;
87
+ return e;
88
+ }
89
+
90
+ // ---------------------------------------------------------------------------
91
+ // nodes: pure subscription points (values used only for equality gating)
92
+ function getNode(e, t, n) {
93
+ const r = e.n ??= Object.create(null);
94
+ let i = r[t];
95
+ if (i === undefined) {
96
+ const o = i = signal(n, {
97
+ // Logical-slot equality: values resolving to the same child target
98
+ // are the same slot (privatization/adoption swap raw identity without
99
+ // changing the logical value — only changed leaves notify, R9).
100
+ equals: (t, n) => isEqual(t, n) || sameLogicalSlot(e, t, n),
101
+ unobserved() {
102
+ // A live affects() mark keeps the node addressable (sweep parity).
103
+ if (o.t) return;
104
+ if (e.n && e.n[t] === o) {
105
+ delete e.n[t];
106
+ e.nc--;
107
+ }
108
+ }
109
+ },
110
+ // Projection nodes carry the projection computed as their firewall:
111
+ // reads through them link the derive's status/lifecycle (§7b).
112
+ e.fam?.node ?? undefined);
113
+ // Store nodes are ownedWrite: the setter carries the owned-scope write
114
+ // guard; node-level setSignals are internal notification machinery.
115
+ o.T |= CONFIG_OWNED_WRITE;
116
+ // Accessor-ness resolved ONCE per node (no per-object descriptor scan):
117
+ // accessor keys serve through Reflect.get with the proxy receiver.
118
+ o.acc = isOwnAccessor(e.pb ?? e.v, t);
119
+ // Wrap cache: the proxy last served for this key and the raw it wrapped.
120
+ // Raw-as-truth stores raw in nodes, so every object read needs a wrapper;
121
+ // one pointer compare (pxv === value) replaces the per-read WeakMap
122
+ // lookup in wrapNext — the dominant read-path cost vs legacy, whose
123
+ // nodes stored pre-wrapped values. A replaced child fails the compare
124
+ // and re-wraps; at most one stale proxy is pinned until the next read.
125
+ o.px = undefined;
126
+ o.pxv = undefined;
127
+ // Optimistic families: arm the override slot — setSignal routes armed
128
+ // nodes through the core engine (lanes, ownership, reverts all native).
129
+ if (e.fam?.opt) o.De = NOT_PENDING;
130
+ // A node born inside a live mark's identity scope inherits the mark
131
+ // (the declaration walk could only cover nodes existing then).
132
+ if (t !== $AFFECTS && affectsScopesLive()) inheritAffectsMarks(o, e.v, t);
133
+ r[t] = i;
134
+ e.nc++;
135
+ markDescendants(e);
136
+ }
137
+ return i;
138
+ }
139
+
140
+ function sameLogicalSlot(e, t, n) {
141
+ if (t === null || typeof t !== "object" || n === null || typeof n !== "object") return false;
142
+ const r = e.fam?.map ?? storeNextLookup;
143
+ const i = r.get(t);
144
+ return i !== undefined && i === r.get(n);
145
+ }
146
+
147
+ function getHasNode(e, t, n) {
148
+ const r = e.h ??= Object.create(null);
149
+ let i = r[t];
150
+ if (i === undefined) {
151
+ const o = i = signal(n, {
152
+ equals: isEqual,
153
+ unobserved() {
154
+ if (o.t) return;
155
+ if (e.h && e.h[t] === o) delete e.h[t];
156
+ }
157
+ }, e.fam?.node ?? undefined);
158
+ o.T |= CONFIG_OWNED_WRITE;
159
+ if (e.fam?.opt) o.De = NOT_PENDING;
160
+ if (affectsScopesLive()) inheritAffectsMarks(o, e.v, t);
161
+ r[t] = i;
162
+ markDescendants(e);
163
+ }
164
+ return i;
165
+ }
166
+
167
+ function getKeySetNode(e) {
168
+ let t = e.k;
169
+ if (t === null) {
170
+ const n = t = signal(0, {
171
+ equals: false,
172
+ unobserved() {
173
+ if (e.k === n) e.k = null;
174
+ }
175
+ }, e.fam?.node ?? undefined);
176
+ n.T |= CONFIG_OWNED_WRITE;
177
+ if (e.fam?.opt) n.De = NOT_PENDING;
178
+ e.k = t;
179
+ markDescendants(e);
180
+ }
181
+ return t;
182
+ }
183
+
184
+ function getDeepNode(e) {
185
+ let t = e.dk;
186
+ if (t === null) {
187
+ const n = t = signal(0, {
188
+ equals: false,
189
+ unobserved() {
190
+ if (e.dk === n) e.dk = null;
191
+ }
192
+ }, e.fam?.node ?? undefined);
193
+ n.T |= CONFIG_OWNED_WRITE;
194
+ if (e.fam?.opt) n.De = NOT_PENDING;
195
+ if (affectsScopesLive()) inheritAffectsMarks(n, e.v, $TRACK);
196
+ e.dk = t;
197
+ markDescendants(e);
198
+ }
199
+ return t;
200
+ }
201
+
202
+ /** Deep-witness bump: any value/shape change on a record with a live deep()
203
+ * subscriber notifies it. One null check when unused. */ function bumpDeep(e) {
204
+ if (e.dk !== null) setSignal(e.dk, 1);
205
+ }
206
+
207
+ function markDescendants(e) {
208
+ let t = e;
209
+ while (t && !t.d) {
210
+ t.d = true;
211
+ t = t.u;
212
+ }
213
+ }
214
+
215
+ // ---------------------------------------------------------------------------
216
+ // pending backing + fold (the single mutation point)
217
+ /** target → committed backing at batch start (the fold diff's old side). */ const foldOlds = new Map;
218
+
219
+ let hookInstalled = false;
220
+
221
+ function cloneRaw(e, t) {
222
+ // Descriptor-preserving shallow clone (R29: installed getters stay live;
223
+ // ruled 2026-08-17: frozen sources clone unfrozen — theirs stays frozen).
224
+ // Data descriptors normalize to writable+configurable (the clone is OURS to
225
+ // mutate — R51's "source-non-configurable is writable through the store");
226
+ // enumerability and accessors are preserved. The scan doubles as the
227
+ // accessor-flag detector (free — we're enumerating descriptors anyway).
228
+ const n = Object.getOwnPropertyDescriptors(e);
229
+ for (const r of Reflect.ownKeys(n)) {
230
+ const i = n[r];
231
+ if (r === "length" && Array.isArray(e)) continue;
232
+ i.configurable = true;
233
+ if (!i.get && !i.set) i.writable = true; else if (t) t.a = true;
234
+ }
235
+ return Array.isArray(e) ? Object.defineProperties([], n) : Object.create(Object.getPrototypeOf(e), n);
236
+ }
237
+
238
+ function ensurePB(e) {
239
+ let t = e.pb;
240
+ if (t === null) {
241
+ t = e.pb = cloneRaw(e.v, e);
242
+ // Optimistic families: seed USER drafts from the OPTIMISTIC VIEW
243
+ // (committed + active node overrides), so follow-up writes compose on
244
+ // optimism instead of clobbering from base (#2951's compose half).
245
+ // AUTHORITATIVE drafts (projection recompute / write-override landings)
246
+ // seed from committed truth — seeding overrides there would fold a lane
247
+ // value into the committed home ("authority wins at reveal" would break).
248
+ if (e.fam?.opt && !projectionWriteActive && !getWriteOverride()) {
249
+ const n = e.n;
250
+ if (n !== null) {
251
+ for (const e of Reflect.ownKeys(n)) {
252
+ const r = n[e];
253
+ if (hasActiveOverride(r)) t[e] = unwrapOverride(r.De);
254
+ }
255
+ }
256
+ const r = e.h;
257
+ if (r !== null) {
258
+ for (const e of Reflect.ownKeys(r)) {
259
+ const n = r[e];
260
+ if (hasActiveOverride(n) && !unwrapOverride(n.De)) delete t[e];
261
+ }
262
+ }
263
+ }
264
+ ownedRaw.add(t);
265
+ (e.fam?.map ?? storeNextLookup).set(t, e);
266
+ queueFold(e);
267
+ }
268
+ return t;
269
+ }
270
+
271
+ /**
272
+ * Adoption (2026-08-16c): the incoming object becomes the committed backing
273
+ * IMMEDIATELY — reconcile is eagerly visible to every reader (shipped
274
+ * contract; only its notifications batch), unlike setter writes which stay
275
+ * pending until flush. Ownership resets (incoming is unowned/user data). Any
276
+ * staged draft clone folds into the diff and is discarded — next is the
277
+ * authoritative base (R21/R32).
278
+ */ function adoptPB(e, t, n = false) {
279
+ // Eager mode (plain-store adoption): the caller notifies inline after its
280
+ // descent — no foldOlds queue/drain round trip (the reconcile diff IS the
281
+ // fold diff; ~half of dbmon tick time was this duplication).
282
+ if (!n) {
283
+ queueFold(e);
284
+ // records the pre-batch old before we swap
285
+ e.adopted = true;
286
+ }
287
+ e.pb = null;
288
+ e.v = t;
289
+ e.ch = t[$TARGET] !== undefined;
290
+ (e.fam?.map ?? storeNextLookup).set(t, e);
291
+ }
292
+
293
+ function queueFold(e) {
294
+ if (foldOlds.has(e)) return;
295
+ if (foldOlds.size === 0) {
296
+ if (!hookInstalled) {
297
+ hookInstalled = true;
298
+ setStoreCommitHook(drainFolds);
299
+ }
300
+ schedule();
301
+ // once per batch — drain clears the map
302
+ }
303
+ foldOlds.set(e, e.v);
304
+ }
305
+
306
+ /** Committed-time privatization for parent-chain slot updates (path copying). */ function privatizeCommitted(e) {
307
+ if (ownedRaw.has(e.v)) return;
308
+ const t = cloneRaw(e.v, e);
309
+ ownedRaw.add(t);
310
+ storeNextLookup.set(t, e);
311
+ e.v = t;
312
+ e.ch = false;
313
+ if (e.u) {
314
+ privatizeCommitted(e.u);
315
+ devAssertNeverUserMutation(e.u.v);
316
+ e.u.v[e.pk] = e.v;
317
+ }
318
+ }
319
+
320
+ function drainFolds() {
321
+ if (foldOlds.size === 0) return;
322
+ const e = [ ...foldOlds ];
323
+ foldOlds.clear();
324
+ for (const [t, n] of e) {
325
+ if (t.pb !== null) {
326
+ // Setter path: nodes were setSignal'd at setter exit (write-time
327
+ // notification — transitions/holds ride core machinery). Commit the
328
+ // backing only for keys whose nodes have committed; a still-pending
329
+ // node (transition-held) re-queues the target for the settling flush.
330
+ let e = false;
331
+ const r = t.pb;
332
+ const i = t.n;
333
+ if (i !== null) {
334
+ for (const t of Reflect.ownKeys(i)) {
335
+ const n = i[t];
336
+ if (n._e !== NOT_PENDING) {
337
+ e = true;
338
+ break;
339
+ }
340
+ }
341
+ }
342
+ if (e) {
343
+ foldOlds.set(t, n);
344
+ // re-queue: commit happens when the hold settles
345
+ continue;
346
+ }
347
+ t.v = r;
348
+ t.ch = false;
349
+ // pb is always a plain clone
350
+ t.pb = null;
351
+ }
352
+ if (t.v === n) continue;
353
+ // adopted then re-adopted back, or no-op
354
+ // Path copying (CAS: see the eager-fold twin above).
355
+ if (t.u && t.u.v[t.pk] === n) {
356
+ privatizeCommitted(t.u);
357
+ devAssertNeverUserMutation(t.u.v);
358
+ t.u.v[t.pk] = t.v;
359
+ }
360
+ if (t.adopted) {
361
+ t.adopted = false;
362
+ notifyFold(t, n, t.v);
363
+ }
364
+ }
365
+ }
366
+
367
+ /**
368
+ * Setter-exit notification (write channel): diff the draft's pending backing
369
+ * against committed and setSignal every changed OBSERVED key — write-time
370
+ * notification with commit deferred to node commit, so transition holds,
371
+ * isPending, affects, and lane machinery ride the core natively (§3's
372
+ * "pending home = the node when a node exists"). Unobserved keys stay in the
373
+ * pending backing and fold directly at commit.
374
+ */ function notifyWrites(e) {
375
+ const t = e.pb;
376
+ if (t === null) return;
377
+ // Optimistic channel: user writes on an optimistic family become node-level
378
+ // engine writes (armed nodes route setSignal through optimisticWrite) — the
379
+ // committed backing is NEVER touched; the draft clone is discarded. Reverts,
380
+ // per-transaction ownership, and flash-at-flush are all core-native.
381
+ // Projection recompute writes (projectionWriteActive) and projection draft
382
+ // writes (write-override, incl. post-await async landings) are
383
+ // authoritative and take the plain channel below (they commit silently
384
+ // under overrides per the engine's no-revert-stash contract).
385
+ if (e.fam?.opt) {
386
+ if (!projectionWriteActive && !getWriteOverride()) {
387
+ optHooks.notifyOptimisticWrites(e, t);
388
+ return;
389
+ }
390
+ // Authoritative path on an optimistic family: armed nodes must commit
391
+ // silently (engine bypass) — without this, a landing's setSignals would
392
+ // create lanes and block their own transition's settle.
393
+ if (!projectionWriteActive) {
394
+ setProjectionWriteActive(true);
395
+ try {
396
+ notifyWrites(e);
397
+ } finally {
398
+ setProjectionWriteActive(false);
399
+ }
400
+ return;
401
+ }
402
+ }
403
+ const n = e.v;
404
+ const r = e.n;
405
+ if (r !== null) {
406
+ for (const e of Reflect.ownKeys(r)) {
407
+ const i = r[e];
408
+ // Per-key accessor handling: the node's cached flag plus ONE getter
409
+ // probe on the incoming side (getters arriving via merge/adoption).
410
+ // Setter-only props read as data (value undefined) so lookupSetter is
411
+ // not consulted on this hot path; prototype getters never own nodes.
412
+ if (i.acc === true || hasOwn.call(t, e) && lookupGetter.call(t, e) !== undefined) {
413
+ i.acc = isOwnAccessor(t, e);
414
+ const r = Object.getOwnPropertyDescriptor(n, e);
415
+ const o = Object.getOwnPropertyDescriptor(t, e);
416
+ if (r && (r.get || r.set) || o && (o.get || o.set)) {
417
+ if (r?.get !== o?.get || r?.set !== o?.set || r?.value !== o?.value) setSignal(i, () => FORCE);
418
+ continue;
419
+ }
420
+ if (!isEqual(r?.value, o?.value)) setSignal(i, () => o?.value);
421
+ continue;
422
+ }
423
+ // No old-side pre-compare: t.v lags across multi-batch windows (a
424
+ // projection recompute can run before the prior fold commits) — the
425
+ // node's OWN current value is the true old side, and setSignal's
426
+ // internal equality already checks exactly that.
427
+ const o = t[e];
428
+ setSignal(i, () => o);
429
+ }
430
+ }
431
+ const i = e.h;
432
+ if (i !== null) {
433
+ for (const e of Reflect.ownKeys(i)) setSignal(i[e], e in t);
434
+ }
435
+ // Deep-witness (dk): setter writes must notify a deep() subscriber even on
436
+ // keys with no node. O(pb keys) equality only when a witness exists.
437
+ if (e.dk !== null) {
438
+ for (const r of Reflect.ownKeys(t)) {
439
+ const i = t[r];
440
+ const o = n[r];
441
+ if (i !== null && typeof i === "object" ? !targetsEqual(o, i) : !isEqual(o, i)) {
442
+ bumpDeep(e);
443
+ break;
444
+ }
445
+ }
446
+ }
447
+ if (e.k !== null) {
448
+ const r = Array.isArray(t) && Array.isArray(n) ? arrayStructureChanged(n, t) : membershipChanged(n, t);
449
+ if (r) setSignal(e.k, e => e + 1);
450
+ }
451
+ // Projection backing folds split by channel (two pinned contracts):
452
+ // - sync-derive drafts (recompute body): NEVER eager — a downstream async
453
+ // hold can form LATER in the same flush and the leaf must stay at stale
454
+ // committed for context-free readers (spec-async "pends only the written
455
+ // leaf"). drainFolds commits when held-ness is knowable.
456
+ // - post-await async LANDINGS (write-override per-op, microtask context —
457
+ // no enclosing flush can capture them): the data-level commit is
458
+ // IMMEDIATE — landed truth shows to untracked readers even while a
459
+ // downstream consumer's own async still holds the effect-level reveal
460
+ // (spec-async "verdicts never inherit consumers' in-flight state").
461
+ if (e.fam !== null && e.pb !== null && getWriteOverride()) {
462
+ const n = e.v;
463
+ e.pb = null;
464
+ e.v = t;
465
+ e.ch = false;
466
+ if (e.u && e.u.v[e.pk] === n) {
467
+ privatizeCommitted(e.u);
468
+ devAssertNeverUserMutation(e.u.v);
469
+ e.u.v[e.pk] = t;
470
+ }
471
+ }
472
+ }
473
+
474
+ const FORCE = Symbol();
475
+
476
+ /** Same logical slot: both values resolve to one (re-pointed) child target —
477
+ * adoption preserved identity, so the slot did not change (R9). */ function targetsEqual(e, t) {
478
+ if (e === null || typeof e !== "object") return false;
479
+ const n = storeNextLookup.get(e);
480
+ return n !== undefined && n === storeNextLookup.get(t);
481
+ }
482
+
483
+ function arrayStructureChanged(e, t) {
484
+ if (e.length !== t.length) return true;
485
+ for (let n = 0; n < t.length; n++) {
486
+ const r = e[n];
487
+ const i = t[n];
488
+ if (!isEqual(r, i) && !targetsEqual(r, i)) return true;
489
+ }
490
+ return false;
491
+ }
492
+
493
+ function membershipChanged(e, t) {
494
+ const n = Reflect.ownKeys(t);
495
+ if (Reflect.ownKeys(e).length !== n.length) return true;
496
+ for (const t of n) if (!(t in e)) return true;
497
+ return false;
498
+ }
499
+
500
+ /**
501
+ * The fold diff walks SUBSCRIPTION KEYS ONLY (legacy parity: `for key in
502
+ * nodes`): nodes exist exactly where something tracked, so unobserved data
503
+ * costs nothing here regardless of object size. Accessor safety rides the
504
+ * sticky `t.a` flag — a node's key was necessarily read, so the get trap has
505
+ * already seen whether it is an accessor.
506
+ */
507
+ /** One node's fold notification (shared by notifyFold's walk and the fused
508
+ * adoption walk): accessor-aware compare + equality/identity-gated setSignal. */ function notifyKeyDiff(e, t, n, r,
509
+ // The incoming-side getter probe covers SETTER-channel arrivals (return-
510
+ // form merges, defineProperty) — those flow through notifyWrites/
511
+ // notifyFold, which probe. The RECONCILE channel (fused walk) passes
512
+ // false: reconcile adopts immutable data by contract (R2a) and the pinned
513
+ // getter-preservation tests are all setter-channel; skipping ~2 Annex-B
514
+ // calls per key per tick is a measured dbmon win.
515
+ i = true) {
516
+ if (e.acc === true || i && hasOwn.call(r, t) && lookupGetter.call(r, t) !== undefined) {
517
+ e.acc = isOwnAccessor(r, t);
518
+ const i = Object.getOwnPropertyDescriptor(n, t);
519
+ const o = Object.getOwnPropertyDescriptor(r, t);
520
+ if (i && (i.get || i.set) || o && (o.get || o.set)) {
521
+ // Accessor involved: never invoke; force-notify on shape change so
522
+ // subscribers re-read (and re-track) through the trap.
523
+ if (i?.get !== o?.get || i?.set !== o?.set || i?.value !== o?.value) setSignal(e, () => FORCE);
524
+ return;
525
+ }
526
+ const f = i?.value;
527
+ const u = o?.value;
528
+ if (!isEqual(f, u) && !targetsEqual(f, u)) setSignal(e, typeof u === "function" ? () => u : u);
529
+ } else {
530
+ const i = n[t];
531
+ const o = r[t];
532
+ // Direct value write when not a function (setSignal treats functions as
533
+ // updaters) — saves a closure allocation per changed key on the fold
534
+ // hot path.
535
+ if (!isEqual(i, o) && !targetsEqual(i, o)) setSignal(e, typeof o === "function" ? () => o : o);
536
+ }
537
+ }
538
+
539
+ /** Accessor-flag probe for the fused walk's early-continue (accessor keys
540
+ * can never identity-skip: their VALUE is the descriptor's product). */ function hasAccessorFlag(e) {
541
+ return e.acc === true;
542
+ }
543
+
544
+ /** Fused-walk per-key notification with values already in hand: the caller
545
+ * fetched both sides and handled the identity skip; this applies the
546
+ * accessor branch (cached flag only — reconcile channel) or the plain
547
+ * equality/identity-gated write. */ function notifyKeyValue(e, t, n, r, i, o) {
548
+ if (e.acc === true) {
549
+ notifyKeyDiff(e, t, i, o, false);
550
+ return;
551
+ }
552
+ // The pre-compare is NOT redundant with the node's equals: setSignal parks
553
+ // a pending value and registers with the batch before equality applies at
554
+ // commit (RUL-1), so identity-preserved slots (adopted child containers —
555
+ // every row's fresh `queries` array) must be gated out HERE or each one
556
+ // pays the full write machinery every tick (measured: +0.5ms/tick dbmon).
557
+ if (!isEqual(n, r) && !targetsEqual(n, r)) setSignal(e, typeof r === "function" ? () => r : r);
558
+ }
559
+
560
+ /** Presence + membership halves of a fold notification (shared tail). */ function notifyFoldTail(e, t, n) {
561
+ const r = e.h;
562
+ if (r !== null) {
563
+ for (const e of Reflect.ownKeys(r)) setSignal(r[e], e in n);
564
+ }
565
+ if (e.k !== null) {
566
+ const r = Array.isArray(n) && Array.isArray(t) ? arrayStructureChanged(t, n) : membershipChanged(t, n);
567
+ if (r) setSignal(e.k, e => e + 1);
568
+ }
569
+ }
570
+
571
+ function notifyFold(e, t, n) {
572
+ if (e.dk !== null && t !== n) bumpDeep(e);
573
+ // Optimistic targets: adoption notifications are authoritative landings —
574
+ // bypass the engine (commit into _value; active overrides keep shadowing
575
+ // until their transaction settles, per the no-revert-stash contract).
576
+ if (e.fam?.opt && !projectionWriteActive) {
577
+ setProjectionWriteActive(true);
578
+ try {
579
+ notifyFold(e, t, n);
580
+ } finally {
581
+ setProjectionWriteActive(false);
582
+ }
583
+ return;
584
+ }
585
+ const r = e.n;
586
+ if (r !== null) {
587
+ for (const e of Reflect.ownKeys(r)) {
588
+ notifyKeyDiff(r[e], e, t, n);
589
+ }
590
+ }
591
+ const i = e.h;
592
+ if (i !== null) {
593
+ for (const e of Reflect.ownKeys(i)) setSignal(i[e], e in n);
594
+ }
595
+ if (e.k !== null) {
596
+ // Key-set/$TRACK: objects notify on membership; arrays on any index or
597
+ // length change (mapArray and iteration re-read values — R15).
598
+ const r = Array.isArray(n) && Array.isArray(t) ? arrayStructureChanged(t, n) : membershipChanged(t, n);
599
+ if (r) setSignal(e.k, e => e + 1);
600
+ }
601
+ }
602
+
603
+ // ---------------------------------------------------------------------------
604
+ // traps
605
+ /** >0 while inside a setter: writes allowed, reads are read-your-writes. */ let writing = 0;
606
+
607
+ /** Write scope keys (a family object or a plain store's root target): draft
608
+ * semantics — write permission, read-your-writes, tracking suppression —
609
+ * apply ONLY to targets under a scope being written. Reads of OTHER stores
610
+ * inside a setter track normally (they are dependencies: a projection derive
611
+ * reading another store must link it). */ let writeScopes = null;
612
+
613
+ function scopeKey(e) {
614
+ if (e.fam !== null) return e.fam;
615
+ let t = e;
616
+ while (t.u !== null) t = t.u;
617
+ return t;
618
+ }
619
+
620
+ function inDraft(e) {
621
+ return writeScopes !== null && writeScopes.has(scopeKey(e));
622
+ }
623
+
624
+ /** Shallow serve rule (#2932): raw-marked data serves VERBATIM, but a
625
+ * store-proxy slot value gets a boundary wrapper in THIS store's own family —
626
+ * write isolation through derived chains (downstream writes must never land
627
+ * upstream). markRawOne skips proxies for exactly this reason. */ function serveShallow(e, t, n) {
628
+ if (n !== null && typeof n === "object" && n[$TARGET] !== undefined) return draftServe(e, wrapNext(n, e, t));
629
+ return n;
630
+ }
631
+
632
+ /** Draft reads extend write permission to reachable stores (legacy Writing
633
+ * semantics: wrapping a child through a draft get admits it — cross-store
634
+ * writes like `s.inner.a = 10` work when `inner` is another store's proxy). */ function draftServe(e, t) {
635
+ if (writeScopes !== null && inDraft(e)) {
636
+ const e = t?.[$TARGET];
637
+ if (e !== undefined && e.v !== undefined) writeScopes.add(scopeKey(e));
638
+ }
639
+ return t;
640
+ }
641
+
642
+ /** Targets written during the current (outermost) setter — notified at exit. */ const pendingNotify = new Set;
643
+
644
+ const UNSAFE_KEYS = new Set([ "__proto__", "prototype", "constructor" ]);
645
+
646
+ /** Mirror of core read()'s context rule: the OWNER context (not the tracking
647
+ * observer) decides pending visibility, with Roots resolving to their parent
648
+ * computed (#2687 — untracked reads inside mapArray Roots see in-flight
649
+ * values mid-flush). CHILDREN_FORBIDDEN execution scopes (createTrackedEffect
650
+ * / onSettled callbacks) get COMMITTED visibility (#3006), same as core. */ function inOwnerContext() {
651
+ const e = getOwner();
652
+ if (e === null) return false;
653
+ const t = e.Tt ? e.It : e;
654
+ return t != null && !(t.T & CONFIG_CHILDREN_FORBIDDEN);
655
+ }
656
+
657
+ /** A pending fold is transition-held when any written node's parked value is
658
+ * stamped by a live transition (a plain batch parking — the lazy-recompute
659
+ * read case — has no transition stamp and serves fresh). */ function foldHeld(e) {
660
+ const t = e.n;
661
+ if (t === null) return false;
662
+ for (const e of Reflect.ownKeys(t)) {
663
+ const n = t[e];
664
+ if (n._e !== NOT_PENDING && n.Ne != null && n.Ne.fn !== true) return true;
665
+ }
666
+ return false;
667
+ }
668
+
669
+ function readSource(e) {
670
+ // Signal-parity visibility (core read(): owner-context reads serve
671
+ // _pendingValue, context-free reads serve committed — effects recompute
672
+ // BEFORE commitPendingNodes in the flush, so the pending view must be
673
+ // servable). Drafts (setter window OR projection write-override) and
674
+ // owner-context reads see the pending backing; context-free reads see
675
+ // committed. Node reads apply the same rule, so both homes agree.
676
+ if (e.pb !== null && (inDraft(e) || getWriteOverride() || inOwnerContext() ||
677
+ // A projection's pending backing is authoritative-elect: serve it to
678
+ // context-free readers too UNLESS a transition is holding the node
679
+ // commits (downstream async hold — stale committed is the contract).
680
+ e.fam !== null && !foldHeld(e))) return e.pb;
681
+ return e.v;
682
+ }
683
+
684
+ const hasOwn = Object.prototype.hasOwnProperty;
685
+
686
+ // Allocation-free own-accessor probe (replaces eager descriptor scans — the
687
+ // single biggest creation cost in the uibench profile): Annex-B lookups
688
+ // return the fn or undefined with no descriptor object. Own data properties
689
+ // shadow prototype accessors, so hasOwn + lookup is an exact own-check.
690
+ const lookupGetter = Object.prototype.__lookupGetter__;
691
+
692
+ const lookupSetter = Object.prototype.__lookupSetter__;
693
+
694
+ function isOwnAccessor(e, t) {
695
+ return hasOwn.call(e, t) && (lookupGetter.call(e, t) !== undefined || lookupSetter.call(e, t) !== undefined);
696
+ }
697
+
698
+ /** Authoritative-write wrapper exported for the optimistic module: sets the
699
+ * scheduler's projectionWriteActive through THIS module's binding (proven to
700
+ * share the instance core reads — cross-module live-binding writes from other
701
+ * store modules were observed not to propagate under the test transform). */ function runAuthoritative(e) {
702
+ const t = projectionWriteActive;
703
+ setProjectionWriteActive(true);
704
+ try {
705
+ return e();
706
+ } finally {
707
+ setProjectionWriteActive(t);
708
+ }
709
+ }
710
+
711
+ /** Active optimistic override on an armed node (armed slot idles at
712
+ * NOT_PENDING; undefined = unarmed plain node). */ function hasActiveOverride(e) {
713
+ return e.De !== undefined && e.De !== NOT_PENDING;
714
+ }
715
+
716
+ /** Context-aware node view for reads outside tracking: active override >
717
+ * held pending (owner context) > the BACKING value. Committed truth lives in
718
+ * the backing (single-home rule, O6) — node `_value` is never served here,
719
+ * so a lazy recompute's landing is immediately visible to the untracked
720
+ * reader that forced it (backing commits eagerly; node values fold at flush).
721
+ * FORCE sentinels never surface (they only bump subscribers of accessor
722
+ * keys, which are served by the trap, not the node). */ function nodeValue(e, t) {
723
+ const n = hasActiveOverride(e) ? unwrapOverride(e.De) : e._e !== NOT_PENDING && inOwnerContext() ? e._e : t;
724
+ return n === FORCE ? t : n;
725
+ }
726
+
727
+ /** Serve an own data key: node-first when a node exists (pending visibility,
728
+ * holds, lanes ride the node); backing otherwise. Chained backings (§7b: the
729
+ * backing IS another store's proxy) serve the read-through value — the outer
730
+ * node is linked only for adoption-swap notification, its value never
731
+ * shadows the live chain. */ function serveDataKey(e, t, n, r, i) {
732
+ const o = e.ch && r === e.v;
733
+ let f = n;
734
+ // §6: on optimistic arrays LENGTH IS A VIEW, not a node value — one home
735
+ // (backing ± presence overrides) for both length and indices makes torn
736
+ // iteration impossible (a length node's value rides different visibility
737
+ // rails than index overrides mid-settle). The node still carries
738
+ // subscriptions; its value is never served here.
739
+ if (t === "length" && e.fam?.opt === true && !o && Array.isArray(r)) {
740
+ if (!inDraft(e)) {
741
+ const r = e.n?.length;
742
+ if (r !== undefined) {
743
+ if (getObserver() !== null) read(r);
744
+ } else if (getObserver() !== null) {
745
+ read(getNode(e, t, n));
746
+ }
747
+ }
748
+ return optHooks.optimisticView(e, r).length;
749
+ }
750
+ if (inDraft(e)) {
751
+ // Optimistic drafts before their first write have no pending backing yet;
752
+ // reads must still see the live optimistic view (compose, not clobber —
753
+ // #2951). Once ensurePB runs, the seeded clone carries the view.
754
+ if (e.fam?.opt && e.pb === null) {
755
+ const n = e.n?.[t];
756
+ if (n !== undefined && hasActiveOverride(n)) f = unwrapOverride(n.De);
757
+ }
758
+ } else {
759
+ if (i !== undefined) {
760
+ // §7b: a lane value on the outer node SHADOWS read-through — an active
761
+ // override pierces the chained gate; otherwise chained backings always
762
+ // serve the live inner value.
763
+ if (getObserver() !== null) {
764
+ // read()'s plain-signal fast path hoisted over the call (legacy trap
765
+ // parity): READ_SLOW = a global read window or non-plain node.
766
+ let e = readNodeFast(i);
767
+ if (e === READ_SLOW) e = read(i);
768
+ if (!o || hasActiveOverride(i)) f = e === FORCE ? n : e;
769
+ } else if (!o || hasActiveOverride(i)) {
770
+ f = nodeValue(i, n);
771
+ }
772
+ } else if (getObserver() !== null) {
773
+ read(getNode(e, t, n));
774
+ }
775
+ }
776
+ // Shallow stores serve data raw; store-proxy slots get boundary wrappers.
777
+ if (e.s) return serveShallow(e, t, f);
778
+ if (i !== undefined) {
779
+ // Wrap cache (see getNode): only wrappables are ever cached, so a hit
780
+ // skips isWrappable too — pointer-compare replaces both checks.
781
+ if (i.pxv === f && f !== undefined) return draftServe(e, i.px);
782
+ if (!isWrappable(f)) return f;
783
+ const n = wrapNext(f, e, t);
784
+ i.px = n;
785
+ i.pxv = f;
786
+ return draftServe(e, n);
787
+ }
788
+ if (!isWrappable(f)) return f;
789
+ return draftServe(e, wrapNext(f, e, t));
790
+ }
791
+
792
+ /** §6c store-wide status gate for reads that DON'T flow through a node:
793
+ * untracked/raw fallthrough must still throw while the derive is
794
+ * uninitialized (seed invisibility, proj R23) or errored (memo parity).
795
+ * TRACKED reads never call this — store nodes carry `_firewall`, so core
796
+ * read() links the node and throws the firewall's error itself (the node
797
+ * link is what wakes async-memo readers when the landing writes values;
798
+ * the firewall link rides the same read). */ function firewallGate(e) {
799
+ const t = e.fam?.node;
800
+ if (t != null && t.S & (STATUS_UNINITIALIZED | STATUS_ERROR)) read(t);
801
+ }
802
+
803
+ const traps = {
804
+ get(e, t, n) {
805
+ // One typeof gates every brand-symbol compare off the hot string path
806
+ // (four symbol comparisons per property read otherwise).
807
+ if (typeof t !== "string") {
808
+ if (t === $TARGET) return e;
809
+ if (t === $PROXY) return n;
810
+ // refresh()/isPending resolve the projection computed through $REFRESH.
811
+ if (t === $REFRESH) return e.fam?.node ?? undefined;
812
+ if (t === $TRACK) {
813
+ if (pendingCheckActive) witnessAffectsMark(e, t);
814
+ if (e.fam !== null && getObserver() === null && !inDraft(e)) firewallGate(e);
815
+ if (!inDraft(e) && getObserver() !== null) {
816
+ read(getKeySetNode(e));
817
+ // Structural chaining (§7b, #2864 / core R21): a chained backing's
818
+ // $TRACK reads through to the INNER store's key-set — structural
819
+ // notifications land on the source's own node, never on this
820
+ // wrapper view's.
821
+ const t = readSource(e);
822
+ if (t[$TARGET] !== undefined) t[$TRACK];
823
+ }
824
+ return undefined;
825
+ }
826
+ // user symbols fall through to the generic path
827
+ }
828
+ if (pendingCheckActive) witnessAffectsMark(e, t);
829
+ if (e.fam !== null && getObserver() === null && !inDraft(e)) firewallGate(e);
830
+ const r = readSource(e);
831
+ // Hot inline case: existing PLAIN node (non-accessor), unchained backing,
832
+ // tracked read of a present data key — the dbmon/uibench effect re-read
833
+ // shape. Skips serveDataKey's frame, the FORCE compare (only accessor
834
+ // keys ever hold the sentinel), and isWrappable for primitives.
835
+ if (e.ch === false && writeScopes === null) {
836
+ const n = e.n?.[t];
837
+ if (n !== undefined && n.acc !== true && getObserver() !== null) {
838
+ let r = readNodeFast(n);
839
+ if (r === READ_SLOW) r = read(n);
840
+ if (r === null || typeof r !== "object") return r;
841
+ if (e.s) return serveShallow(e, t, r);
842
+ if (n.pxv === r) return n.px;
843
+ if (isWrappable(r)) {
844
+ const i = wrapNext(r, e, t);
845
+ n.px = i;
846
+ n.pxv = r;
847
+ return i;
848
+ }
849
+ return r;
850
+ }
851
+ }
852
+ // Accessor keys serve through Reflect.get with the PROXY receiver
853
+ // (R20/R29: internal reads track; the node is linked for shape-change
854
+ // notification but its value is never served). Accessor-ness comes from
855
+ // the node's cached flag; the first TRACKED read (which creates the
856
+ // node) probes once — untracked node-less reads take the plain path,
857
+ // where a raw-receiver getter still returns correct committed values.
858
+ const i = e.n?.[t];
859
+ {
860
+ const o = i !== undefined ? i.acc === true : !writing && getObserver() !== null && isOwnAccessor(r, t);
861
+ if (o) {
862
+ if (!writing && getObserver() !== null) read(i ?? getNode(e, t, undefined));
863
+ const o = Reflect.get(r, t, n);
864
+ if (e.s) return serveShallow(e, t, o);
865
+ return isWrappable(o) ? draftServe(e, wrapNext(o, e, t)) : o;
866
+ }
867
+ }
868
+ // Plain-data fast path: no descriptor allocation per read.
869
+ // Inherited pollution keys are never served (core R30) — checked before
870
+ // the proto-function branch can leak `constructor`. Interned-string
871
+ // compares beat a Set hash on this per-read path.
872
+ if ((t === "constructor" || t === "__proto__" || t === "prototype") && !hasOwn.call(r, t)) return undefined;
873
+ let o = r[t];
874
+ if (o === undefined ? !hasOwn.call(r, t) : false) {
875
+ // Inherited: prototype getters/methods run with the proxy receiver.
876
+ o = Reflect.get(r, t, n);
877
+ if (typeof o === "function") return o;
878
+ // proto methods untracked
879
+ // Reading a currently-absent own key subscribes to it (R12).
880
+ if (o === undefined && !writing) {
881
+ if (getObserver() !== null) read(getNode(e, t, undefined));
882
+ const n = e.n?.[t];
883
+ if (n) {
884
+ const r = nodeValue(n, undefined);
885
+ if (e.s) return serveShallow(e, t, r);
886
+ return isWrappable(r) ? draftServe(e, wrapNext(r, e, t)) : r;
887
+ }
888
+ } else if (o === undefined && inDraft(e) && e.fam?.opt && e.pb === null) {
889
+ const n = e.n?.[t];
890
+ if (n !== undefined && hasActiveOverride(n)) o = unwrapOverride(n.De);
891
+ }
892
+ if (e.s) return serveShallow(e, t, o);
893
+ return isWrappable(o) ? draftServe(e, wrapNext(o, e, t)) : o;
894
+ }
895
+ if (typeof o === "function" && !hasOwn.call(r, t)) return o;
896
+ // proto method
897
+ return serveDataKey(e, t, o, r, i);
898
+ },
899
+ has(e, t) {
900
+ if (t === $TARGET || t === $PROXY || t === $TRACK) return true;
901
+ if (pendingCheckActive) witnessAffectsMark(e, t);
902
+ if (e.fam !== null && getObserver() === null && !inDraft(e)) firewallGate(e);
903
+ const n = readSource(e);
904
+ let r = t in n;
905
+ if (!inDraft(e)) {
906
+ if (getObserver() !== null) {
907
+ const n = getHasNode(e, t, r);
908
+ const i = read(n);
909
+ if (hasActiveOverride(n)) r = !!i;
910
+ } else {
911
+ const n = e.h?.[t];
912
+ if (n !== undefined && hasActiveOverride(n)) r = !!unwrapOverride(n.De);
913
+ }
914
+ } else if (e.fam?.opt && e.pb === null) {
915
+ const n = e.h?.[t];
916
+ if (n !== undefined && hasActiveOverride(n)) r = !!unwrapOverride(n.De);
917
+ }
918
+ return r;
919
+ },
920
+ ownKeys(e) {
921
+ if (pendingCheckActive) witnessAffectsMark(e);
922
+ if (e.fam !== null && getObserver() === null && !inDraft(e)) firewallGate(e);
923
+ if (!inDraft(e) && getObserver() !== null) read(getKeySetNode(e));
924
+ const t = Reflect.ownKeys(readSource(e));
925
+ // Optimistic membership overlay: presence-node overrides add/remove keys
926
+ // (per-transaction lifecycle rides the nodes — §6, FINDING-2's fix).
927
+ // Draft reads before the first write overlay too (pb, once created, is
928
+ // seeded with the view).
929
+ if (e.fam?.opt && e.h !== null && (!inDraft(e) || e.pb === null)) {
930
+ let n = null;
931
+ for (const r of Reflect.ownKeys(e.h)) {
932
+ const i = e.h[r];
933
+ if (!hasActiveOverride(i)) continue;
934
+ n ??= new Set(t);
935
+ if (unwrapOverride(i.De)) n.add(r); else n.delete(r);
936
+ }
937
+ if (n !== null) return [ ...n ];
938
+ }
939
+ return t;
940
+ },
941
+ getOwnPropertyDescriptor(e, t) {
942
+ const n = Object.getOwnPropertyDescriptor(readSource(e), t);
943
+ if (e.fam?.opt && !inDraft(e)) {
944
+ const r = e.h?.[t];
945
+ if (r !== undefined && hasActiveOverride(r)) {
946
+ if (!unwrapOverride(r.De)) return undefined;
947
+ // opt delete
948
+ if (n === undefined) {
949
+ const n = e.n?.[t];
950
+ return {
951
+ value: n !== undefined ? nodeValue(n, undefined) : undefined,
952
+ writable: true,
953
+ enumerable: true,
954
+ configurable: true
955
+ };
956
+ }
957
+ }
958
+ }
959
+ if (n === undefined) return undefined;
960
+ // Array targets carry a real non-configurable `length` the proxy
961
+ // invariant forces us to report faithfully; everything else reports
962
+ // configurable via target indirection (core R51).
963
+ if (!(t === "length" && Array.isArray(e))) n.configurable = true;
964
+ return n;
965
+ },
966
+ set(e, t, n) {
967
+ // Writes require the target's draft scope OR the projection write
968
+ // override (post-await async draft writes arrive outside any window);
969
+ // everything else is silently ignored (R23).
970
+ const r = inDraft(e);
971
+ const i = !r && getWriteOverride();
972
+ if (!r && !i) return true;
973
+ if (t === "__proto__") return true;
974
+ // pollution guard (core R30)
975
+ const o = ensurePB(e);
976
+ pendingNotify.add(e);
977
+ // Own data keys literally named "prototype"/"constructor" land as data —
978
+ // defineProperty sidesteps a proto-chain setter named the same.
979
+ if (UNSAFE_KEYS.has(t)) {
980
+ Object.defineProperty(o, t, {
981
+ value: unwrapValue(n),
982
+ writable: true,
983
+ enumerable: true,
984
+ configurable: true
985
+ });
986
+ return true;
987
+ }
988
+ // Shallow slots store what was written VERBATIM — another store's proxy
989
+ // passes through by reference (#2932; markRawOne skips proxies), while
990
+ // deep stores unwrap to raw backings.
991
+ const f = e.s ? n : unwrapValue(n);
992
+ o[t] = f;
993
+ // Shallow ingest: written records are sticky raw-marked (one entity is
994
+ // never both deep-wrapped and raw — R41/#2932, shared invariant).
995
+ if (e.s && f !== null && typeof f === "object") markRawOne(f);
996
+ // Override-mode (post-await draft) writes have no setter exit — notify
997
+ // per-op (setSignal equality-gates repeats).
998
+ if (i) notifyWrites(e);
999
+ return true;
1000
+ },
1001
+ defineProperty(e, t, n) {
1002
+ const r = inDraft(e);
1003
+ const i = !r && getWriteOverride();
1004
+ if (!r && !i) return true;
1005
+ if (t === "__proto__") return true;
1006
+ if (n.get || n.set) e.a = true;
1007
+ const o = ensurePB(e);
1008
+ pendingNotify.add(e);
1009
+ if ("value" in n) n = {
1010
+ ...n,
1011
+ value: unwrapValue(n.value)
1012
+ };
1013
+ Object.defineProperty(o, t, n);
1014
+ if (i) notifyWrites(e);
1015
+ return true;
1016
+ },
1017
+ deleteProperty(e, t) {
1018
+ const n = inDraft(e);
1019
+ const r = !n && getWriteOverride();
1020
+ if (!n && !r) return true;
1021
+ const i = ensurePB(e);
1022
+ pendingNotify.add(e);
1023
+ delete i[t];
1024
+ if (r) notifyWrites(e);
1025
+ return true;
1026
+ }
1027
+ };
1028
+
1029
+ /** Low-level setter primitive: opens write mode on a next proxy, runs `fn`,
1030
+ * emits write-time notifications at outermost exit, applies returned
1031
+ * replacements as adoptions. `guard=false` skips the owned-scope dev guard —
1032
+ * projection recomputes legitimately write from inside their computed. */ function storeSetterNext(e, t, n = true) {
1033
+ const r = e[$TARGET];
1034
+ const i = writeScopes;
1035
+ writeScopes = new Set;
1036
+ writeScopes.add(scopeKey(r));
1037
+ writing++;
1038
+ let o;
1039
+ try {
1040
+ // No untrack: the writing flag already disables store-node linking
1041
+ // (draft reads never self-track, proj R2), while EXTERNAL reads (signals
1042
+ // inside a projection derive) must keep tracking — they are the derive's
1043
+ // dependencies.
1044
+ o = t(e);
1045
+ } finally {
1046
+ writing--;
1047
+ writeScopes = i;
1048
+ // Outermost setter exit: emit write-time notifications (setSignal per
1049
+ // changed observed key) so transition holds and lanes engage now.
1050
+ if (writing === 0 && pendingNotify.size) {
1051
+ const e = [ ...pendingNotify ];
1052
+ pendingNotify.clear();
1053
+ for (const t of e) notifyWrites(t);
1054
+ }
1055
+ }
1056
+ if (o !== undefined && o !== e && isWrappable(o)) {
1057
+ // Returned replacement: on an optimistic family (outside authoritative
1058
+ // writes) the replacement is itself an optimistic edit — diff it against
1059
+ // the visible view as engine writes (reverts at settle). Otherwise it is
1060
+ // an adoption of the incoming object (unowned).
1061
+ if (r.fam?.opt && !projectionWriteActive && !getWriteOverride()) {
1062
+ optHooks.notifyOptimisticWrites(r, unwrapValue(o));
1063
+ } else {
1064
+ adoptPB(r, unwrapValue(o));
1065
+ }
1066
+ }
1067
+ }
1068
+
1069
+ // Affects integration: the legacy affects machinery reads next targets
1070
+ // structurally (aliased field names); only node CREATION dispatches here.
1071
+ setNextAffectsNodeResolver((e, t) => t === $AFFECTS ? getNode(e, $AFFECTS, undefined) : getNode(e, t, (e.pb ?? e.v)[t]));
1072
+
1073
+ function createStoreNext(e, t = false) {
1074
+ const n = wrapNext(e);
1075
+ if (t) {
1076
+ n[$TARGET].s = true;
1077
+ markRawIngest(e);
1078
+ }
1079
+ const setter = e => storeSetterNext(n, e);
1080
+ return [ n, setter ];
1081
+ }
1082
+
1083
+ /** Tracking deep snapshot (`deep()` for next targets): subscribes to the
1084
+ * key-set and every property node at every reachable level, then returns the
1085
+ * plain view. Shared references and cycles handled via the visited set. */ function deepNext(e) {
1086
+ const t = e?.[$TARGET];
1087
+ if (t === undefined || t.px !== e) return e;
1088
+ const n = new Set;
1089
+ // One membership node + one deep-witness node PER RECORD (legacy $TRACK
1090
+ // parity): the walk stays O(records) in subscriptions instead of O(paths)
1091
+ // in per-key nodes, and it walks TARGETS directly — no per-child proxy
1092
+ // round-trip (wrapNext → proxy → $TARGET trap) on the re-walk every
1093
+ // effect run performs.
1094
+ const walkT = e => {
1095
+ const t = readSource(e);
1096
+ if (n.has(t)) return;
1097
+ n.add(t);
1098
+ read(getKeySetNode(e));
1099
+ read(getDeepNode(e));
1100
+ const r = e.fam?.map ?? storeNextLookup;
1101
+ for (const n of Reflect.ownKeys(t)) {
1102
+ const i = Object.getOwnPropertyDescriptor(t, n);
1103
+ if (i === undefined) continue;
1104
+ if (i.get || i.set) {
1105
+ e.a = true;
1106
+ continue;
1107
+ // accessors track through their own reads when invoked
1108
+ }
1109
+ const o = i.value;
1110
+ if (o === null || typeof o !== "object") continue;
1111
+ // Stored proxies (chained slots) resolve through their own target;
1112
+ // raw children through the family map, created on first visit.
1113
+ let f = o[$TARGET] ?? r.get(o);
1114
+ if (f === undefined) {
1115
+ if (!isWrappable(o)) continue;
1116
+ wrapNext(o, e, n);
1117
+ f = r.get(o);
1118
+ if (f === undefined) continue;
1119
+ // raw-marked: leaf by contract
1120
+ }
1121
+ walkT(f);
1122
+ }
1123
+ };
1124
+ walkT(t);
1125
+ return snapshotNext(e);
1126
+ }
1127
+
1128
+ /**
1129
+ * Snapshot with per-object registration resolution (RUL-12 DAG ruling): every
1130
+ * reachable wrappable resolves through its target's CURRENT backing, so
1131
+ * privatized subtrees are seen through any parent path. Identity-preserving:
1132
+ * a subtree with no substitutions below returns its own object (zero copy for
1133
+ * settled, never-diverged graphs).
1134
+ */ function snapshotNext(e) {
1135
+ const t = e?.[$TARGET];
1136
+ return snapshotWalk(e, new Map, t?.fam ?? null);
1137
+ }
1138
+
1139
+ function snapshotWalk(e, t, n) {
1140
+ if (e === null || typeof e !== "object") return e;
1141
+ // Resolve through the registration: proxies AND raws map to their target's
1142
+ // current backing (stale raw pointers through other parents resolve here).
1143
+ // Loops for chained backings (§7b: a projection's backing can be another
1144
+ // store's proxy — snapshot unwraps to the base raw).
1145
+ let r = e;
1146
+ // Chained backings can pass through several targets; optimistic overrides
1147
+ // on OUTER targets shadow the chain (§7b), so collect every opt target
1148
+ // encountered and compose their views over the resolved base, innermost
1149
+ // outward.
1150
+ let i = null;
1151
+ for (;;) {
1152
+ let e = r?.[$TARGET]?.v !== undefined ? r[$TARGET] : undefined;
1153
+ if (e === undefined && n !== null) e = n.map.get(r);
1154
+ if (e === undefined) e = storeNextLookup.get(r);
1155
+ if (e === undefined) break;
1156
+ if (e.fam !== null) n = e.fam;
1157
+ if (e.fam?.opt === true) (i ??= []).push(e);
1158
+ const t = e.pb ?? e.v;
1159
+ if (t === r) break;
1160
+ r = t;
1161
+ }
1162
+ if (!isWrappable(r)) return r;
1163
+ // Optimistic families: compose the visible view; a composed view is a fresh
1164
+ // object and snapshots via the owned/copy path (pinned `not.toBe` identity).
1165
+ if (i !== null) {
1166
+ let e = r;
1167
+ for (let t = i.length - 1; t >= 0; t--) e = optHooks.optimisticView(i[t], e);
1168
+ if (e !== r) {
1169
+ const i = t.get(r);
1170
+ if (i !== undefined) return i;
1171
+ const o = Array.isArray(e);
1172
+ const f = o ? [] : Object.create(Object.getPrototypeOf(e));
1173
+ t.set(r, f);
1174
+ for (const r of Reflect.ownKeys(e)) {
1175
+ if (o && r === "length") continue;
1176
+ const i = e[r];
1177
+ f[r] = i !== null && typeof i === "object" ? snapshotWalk(i, t, n) : i;
1178
+ }
1179
+ if (o) f.length = e.length;
1180
+ return f;
1181
+ }
1182
+ }
1183
+ const o = t.get(r);
1184
+ if (o !== undefined) return o;
1185
+ // OWNED (written) subtrees snapshot as copies (§7b: identity is only for
1186
+ // subtrees "unmodified relative to source"): non-enumerable symbols are
1187
+ // excluded (recon-snap R29), and the copy registers BEFORE descent so
1188
+ // cycles keep identity (FINDING-3).
1189
+ if (ownedRaw.has(r)) {
1190
+ const e = Array.isArray(r);
1191
+ const i = e ? [] : Object.create(Object.getPrototypeOf(r));
1192
+ t.set(r, i);
1193
+ for (const o of Reflect.ownKeys(r)) {
1194
+ if (e && o === "length") continue;
1195
+ const f = Object.getOwnPropertyDescriptor(r, o);
1196
+ if (typeof o === "symbol" && !f.enumerable) continue;
1197
+ if (f.get || f.set) {
1198
+ Object.defineProperty(i, o, f);
1199
+ continue;
1200
+ }
1201
+ const u = f.value;
1202
+ const s = u !== null && typeof u === "object" ? snapshotWalk(u, t, n) : u;
1203
+ if (f.enumerable && f.writable && f.configurable) i[o] = s; else Object.defineProperty(i, o, {
1204
+ ...f,
1205
+ value: s
1206
+ });
1207
+ }
1208
+ if (e && i.length !== r.length) i.length = r.length;
1209
+ return i;
1210
+ }
1211
+ // UNOWNED (shared/user) subtrees keep identity unless a descendant
1212
+ // substituted; copy-on-substitution preserves the documented CoW contract.
1213
+ t.set(r, r);
1214
+ let f = null;
1215
+ for (const e of Reflect.ownKeys(r)) {
1216
+ const i = Object.getOwnPropertyDescriptor(r, e);
1217
+ if (!i || i.get || i.set) continue;
1218
+ const o = i.value;
1219
+ if (o === null || typeof o !== "object") continue;
1220
+ const u = snapshotWalk(o, t, n);
1221
+ if (u !== o) {
1222
+ if (f === null) {
1223
+ f = Array.isArray(r) ? [ ...r ] : Object.create(Object.getPrototypeOf(r), Object.getOwnPropertyDescriptors(r));
1224
+ t.set(r, f);
1225
+ }
1226
+ f[e] = u;
1227
+ }
1228
+ }
1229
+ return f ?? r;
1230
+ }
1231
+
1232
+ export { adoptPB, bumpDeep, createStoreNext, deepNext, getHasNode, getKeySetNode, getNode, hasAccessorFlag, hasActiveOverride, notifyFold, notifyFoldTail, notifyKeyDiff, notifyKeyValue, runAuthoritative, snapshotNext, storeSetterNext, targetsEqual, unwrapValue, wrapNext };