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

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