@solidjs/signals 2.0.0-rc.8 → 2.0.0-rc.9

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 (90) hide show
  1. package/dist/dev-shared.js +1399 -285
  2. package/dist/dev.attribution.js +459 -307
  3. package/dist/dev.js +1864 -435
  4. package/dist/observe/affects.js +3 -1
  5. package/dist/observe/attribution.js +7 -1
  6. package/dist/observe/boundaries.js +209 -154
  7. package/dist/observe/core/action.js +18 -8
  8. package/dist/observe/core/async.js +220 -110
  9. package/dist/observe/core/attribution-costs.js +66 -0
  10. package/dist/observe/core/attribution-feedback.js +282 -0
  11. package/dist/observe/core/attribution-hooks.js +23 -1
  12. package/dist/observe/core/attribution-queries.js +28 -0
  13. package/dist/observe/core/attribution.js +262 -485
  14. package/dist/observe/core/constants.js +34 -1
  15. package/dist/observe/core/context.js +3 -3
  16. package/dist/observe/core/core.js +867 -367
  17. package/dist/observe/core/dev.js +78 -17
  18. package/dist/observe/core/effect.js +67 -51
  19. package/dist/observe/core/error-hooks.js +71 -0
  20. package/dist/observe/core/external.js +4 -4
  21. package/dist/observe/core/graph.js +37 -37
  22. package/dist/observe/core/heap.js +45 -45
  23. package/dist/observe/core/invariants.js +2 -0
  24. package/dist/observe/core/lanes.js +86 -49
  25. package/dist/observe/core/optimistic.js +242 -95
  26. package/dist/observe/core/owner.js +98 -84
  27. package/dist/observe/core/scheduler.js +543 -305
  28. package/dist/observe/core/verdict.js +247 -129
  29. package/dist/observe/index.js +7 -3
  30. package/dist/observe/map.js +126 -124
  31. package/dist/observe/signals.js +33 -17
  32. package/dist/observe/store/index.js +2 -0
  33. package/dist/observe/store/next/optimistic.js +141 -132
  34. package/dist/observe/store/next/projection.js +34 -21
  35. package/dist/observe/store/next/reconcile.js +58 -56
  36. package/dist/observe/store/next/store.js +260 -146
  37. package/dist/observe/store/store.js +5 -3
  38. package/dist/observe/store/utils.js +948 -135
  39. package/dist/prod/attribution.js +26 -17
  40. package/dist/prod/boundaries.js +128 -76
  41. package/dist/prod/core/action.js +16 -8
  42. package/dist/prod/core/async.js +251 -143
  43. package/dist/prod/core/constants.js +34 -1
  44. package/dist/prod/core/context.js +3 -3
  45. package/dist/prod/core/core.js +843 -347
  46. package/dist/prod/core/dev.js +17 -1
  47. package/dist/prod/core/effect.js +48 -34
  48. package/dist/prod/core/error-hooks.js +71 -0
  49. package/dist/prod/core/external.js +4 -4
  50. package/dist/prod/core/graph.js +37 -37
  51. package/dist/prod/core/heap.js +45 -45
  52. package/dist/prod/core/lanes.js +90 -53
  53. package/dist/prod/core/optimistic.js +247 -100
  54. package/dist/prod/core/owner.js +57 -45
  55. package/dist/prod/core/scheduler.js +543 -305
  56. package/dist/prod/core/verdict.js +245 -127
  57. package/dist/prod/index.js +7 -3
  58. package/dist/prod/map.js +112 -112
  59. package/dist/prod/signals.js +28 -12
  60. package/dist/prod/store/next/optimistic.js +135 -128
  61. package/dist/prod/store/next/projection.js +31 -20
  62. package/dist/prod/store/next/store.js +325 -252
  63. package/dist/prod/store/store.js +2 -2
  64. package/dist/prod/store/utils.js +946 -135
  65. package/dist/types/attribution.d.ts +7 -2
  66. package/dist/types/attribution.prod.d.ts +10 -1
  67. package/dist/types/boundaries.d.ts +10 -1
  68. package/dist/types/core/action.d.ts +12 -5
  69. package/dist/types/core/attribution-costs.d.ts +35 -0
  70. package/dist/types/core/attribution-feedback.d.ts +133 -0
  71. package/dist/types/core/attribution-hooks.d.ts +28 -3
  72. package/dist/types/core/attribution-queries.d.ts +10 -0
  73. package/dist/types/core/attribution.d.ts +51 -172
  74. package/dist/types/core/constants.d.ts +33 -0
  75. package/dist/types/core/core.d.ts +186 -5
  76. package/dist/types/core/dev.d.ts +129 -9
  77. package/dist/types/core/error-hooks.d.ts +71 -0
  78. package/dist/types/core/index.d.ts +3 -1
  79. package/dist/types/core/invariants.d.ts +4 -0
  80. package/dist/types/core/lanes.d.ts +31 -4
  81. package/dist/types/core/scheduler.d.ts +95 -2
  82. package/dist/types/core/types.d.ts +3 -0
  83. package/dist/types/index.d.ts +2 -2
  84. package/dist/types/signals.d.ts +8 -0
  85. package/dist/types/store/index.d.ts +2 -1
  86. package/dist/types/store/next/optimistic.d.ts +1 -1
  87. package/dist/types/store/next/store.d.ts +6 -5
  88. package/dist/types/store/next/target.d.ts +1 -1
  89. package/dist/types/store/utils.d.ts +177 -6
  90. package/package.json +1 -1
@@ -1,8 +1,8 @@
1
- import { unwrapOverride, $REFRESH, NOT_PENDING, CONFIG_OWNED_WRITE, CONFIG_OPTIMISTIC, STATUS_UNINITIALIZED, STATUS_ERROR, CONFIG_HELD_TRUTH, CONFIG_CHILDREN_FORBIDDEN, CONFIG_AUTHORITATIVE_READ } from "../../core/constants.js";
1
+ import { unwrapOverride, $REFRESH, CONFIG_CHILDREN_FORBIDDEN, CONFIG_OWNED_WRITE, NOT_PENDING, CONFIG_OPTIMISTIC, STATUS_UNINITIALIZED, STATUS_ERROR, CONFIG_AUTHORITATIVE_READ } from "../../core/constants.js";
2
2
 
3
- import { setSlotUnobserved, setSignal, isEqual, read, pendingCheckActive, latestReadActive, readNodeFast, READ_SLOW, signal, unlinkFirewallChild, ext, setLatestReadActive, prepareComputed, slotSignal, context } from "../../core/core.js";
3
+ import { setSlotUnobserved, setSignal, isEqual, read, hasActiveOverride, pendingCheckActive, visibleOverride, latestReadActive, readNodeFast, READ_SLOW, signal, unlinkFirewallChild, ext, ownsHold, setLatestReadActive, prepareComputed, slotSignal, serve, stale, recordStaleReplay, enterStagedRead, context } from "../../core/core.js";
4
4
 
5
- import { projectionWriteActive, setProjectionWriteActive, activeTransition, schedule, currentTransition, setStoreCommitHook } from "../../core/scheduler.js";
5
+ import { projectionWriteActive, setProjectionWriteActive, activeTransition, schedule, currentTransition, deferSlotRelease, setStoreCommitHook } from "../../core/scheduler.js";
6
6
 
7
7
  import "../../core/invariants.js";
8
8
 
@@ -121,11 +121,11 @@ function wrapNext(e, t = null, n = null, r = t?.fam ?? null) {
121
121
  if (rawValuesUsed && isRawValue(e)) return e;
122
122
  const i = lookupTarget(e, r);
123
123
  if (i !== undefined) return i.px;
124
- const o = e[$TARGET];
125
- if (o !== undefined && o.px === e) {
124
+ const l = e[$TARGET];
125
+ if (l !== undefined && l.px === e) {
126
126
  // Foreign-family proxies re-wrap into THIS family (writes stay isolated);
127
127
  // same-family and plain-store proxies pass through.
128
- if (r === null || o.fam === r) return e;
128
+ if (r === null || l.fam === r) return e;
129
129
  return createTarget(e, t, n, r).px;
130
130
  }
131
131
  return createTarget(e, t, n, r).px;
@@ -153,7 +153,7 @@ function wrapNext(e, t = null, n = null, r = t?.fam ?? null) {
153
153
  // (privatization/adoption swap raw identity without changing the logical
154
154
  // value — only changed leaves notify, R9).
155
155
  const slotNodeEquals = function(e, t) {
156
- return isEqual(e, t) || sameLogicalSlot(this.Ut, e, t);
156
+ return isEqual(e, t) || sameLogicalSlot(this.Wt, e, t);
157
157
  };
158
158
 
159
159
  // Shared slot-node unobserved handler (create-floor diet): registered once;
@@ -162,8 +162,13 @@ const slotNodeEquals = function(e, t) {
162
162
  setSlotUnobserved(e => {
163
163
  // A live affects() mark keeps the node addressable (sweep parity).
164
164
  if (e.o?.t) return;
165
- const t = e.Ut;
166
- const n = e.xt;
165
+ // An active override or a staged write is state only the node holds (an
166
+ // optimistic signal keeps its override whether or not anything reads it —
167
+ // store parity, posture-store-parity S7): defer the release to the flush
168
+ // that resolves it (the scheduler's transient-node sweep).
169
+ if (hasActiveOverride(e) || e.ve !== NOT_PENDING) return deferSlotRelease(e);
170
+ const t = e.Wt;
171
+ const n = e.qt;
167
172
  if (t.n && t.n[n] === e) {
168
173
  delete t.n[n];
169
174
  t.nc--;
@@ -181,8 +186,8 @@ function getNode(e, t, n,
181
186
  // callers), 0/1 = probed.
182
187
  r = -1) {
183
188
  const i = e.n ??= Object.create(null);
184
- let o = i[t];
185
- if (o === undefined) {
189
+ let l = i[t];
190
+ if (l === undefined) {
186
191
  // Born holding (#3330 store twin): a key first read while a live
187
192
  // transaction holds an ADOPTION on this target (adoptPB's `ht`) is
188
193
  // created as if it had existed when the hold was notified — the adopted
@@ -191,11 +196,15 @@ r = -1) {
191
196
  // (stageHeldKey). Its held-adoption notification (stageHeldAdoptions)
192
197
  // ran before the node existed and the drain has nothing left to say.
193
198
  // Without this the node was born from whichever view its first reader
194
- // saw and never learned the other. (The held-FOLD twin a setter's
195
- // write to an unobserved key landing only in the pending backing — is
196
- // #3336's, and lands with #3337.)
197
- const l = heldAdoptionTransition(e);
198
- if (l !== null) n = e.hv[t];
199
+ // saw and never learned the other. Two kinds of hold, one rule (#3336):
200
+ // - a held FOLD (pb): a setter's write to an unobserved key landed only
201
+ // in the pending backing; committed is `v[key]`, staged is `pb[key]`
202
+ // (undefined for a deleted key);
203
+ // - a held ADOPTION (ht): the adopted value already swapped into `v`;
204
+ // committed is the held view `hv[key]`, staged is `v[key]`.
205
+ const o = heldFoldTransition(e);
206
+ let f = heldAdoptionTransition(e);
207
+ if (f !== null) n = e.hv[t]; else if ((f = o) !== null) n = e.v[t];
199
208
  // Create-floor diet: slotSignal bakes the whole node into one literal —
200
209
  // no options object, no equals/unobserved closures, no NodeExtension,
201
210
  // no post-construction expandos (acc + the wrap cache px/pxv are
@@ -206,7 +215,7 @@ r = -1) {
206
215
  // notification machinery. Projection nodes carry the projection
207
216
  // computed as their firewall: reads through them link the derive's
208
217
  // status/lifecycle (§7b).
209
- const f = o = slotSignal(n, slotNodeEquals, e, t,
218
+ const s = l = slotSignal(n, slotNodeEquals, e, t,
210
219
  // Accessor-ness resolved ONCE per node (no per-object descriptor
211
220
  // scan on reads): accessor keys serve through Reflect.get with the
212
221
  // proxy receiver.
@@ -214,25 +223,25 @@ r = -1) {
214
223
  // Optimistic families: arm the override slot — setSignal routes armed
215
224
  // nodes through the core engine (lanes, ownership, reverts all native).
216
225
  if (e.fam?.opt) {
217
- ext(f).be = NOT_PENDING;
218
- f.T |= CONFIG_OPTIMISTIC;
226
+ ext(s).Ce = NOT_PENDING;
227
+ s.T |= CONFIG_OPTIMISTIC;
219
228
  }
220
229
  // A node born inside a live mark's identity scope inherits the mark
221
230
  // (the declaration walk could only cover nodes existing then).
222
- if (t !== $AFFECTS && affectsScopesLive()) inheritAffectsMarks(f, e.v, t);
223
- if (l !== null) stageHeldKey(f, e.v[t], l);
224
- i[t] = o;
231
+ if (t !== $AFFECTS && affectsScopesLive()) inheritAffectsMarks(s, e.v, t);
232
+ if (f !== null) stageHeldKey(s, o !== null ? e.del !== null && e.del.has(t) ? undefined : e.pb[t] : e.v[t], f);
233
+ i[t] = l;
225
234
  e.nc++;
226
235
  markDescendants(e);
227
236
  }
228
- return o;
237
+ return l;
229
238
  }
230
239
 
231
240
  /** The live transaction holding an adoption on `target` (adoptPB's `ht`;
232
241
  * a latest()-pull PLAIN_HOLD is not a transaction), else null. */ function heldAdoptionTransition(e) {
233
242
  if (e.ht === null || e.ht === PLAIN_HOLD || heldMaskView(e) === null) return null;
234
243
  const t = currentTransition(e.ht);
235
- return t.ft === false ? t : null;
244
+ return t.Tt === false ? t : null;
236
245
  }
237
246
 
238
247
  /**
@@ -248,11 +257,54 @@ r = -1) {
248
257
  * setSignal: it is not a new write (it staged with the adoption's batch) and
249
258
  * it walks no subscriber (the node has none yet). Transition-stamped now, as
250
259
  * `runFolded` does — no parked-flush pass will stamp it.
251
- */ function stageHeldKey(e, t, n) {
252
- if (slotNodeEquals.call(e, e.me, t)) return;
253
- e.Ge = t;
254
- e.ge = n;
255
- n.$t.push(e);
260
+ */
261
+ /** The live transaction holding `target`'s pending backing (the #3089
262
+ * write-time stamp, resolved through merges), else null. */ function liveFoldTransition(e) {
263
+ if (e.pb === null) return null;
264
+ const t = foldBatches.get(e);
265
+ if (t === undefined) return null;
266
+ const n = currentTransition(t);
267
+ return n.Tt === false ? n : null;
268
+ }
269
+
270
+ /** liveFoldTransition for node materialization (stageHeldKey). Inside the
271
+ * draft the backing is the setter's working copy, not a flushed hold — its
272
+ * nodes take their writes at setter exit (notifyWrites). Optimistic families
273
+ * hold at the backing: tentative writes are node overrides over a discarded
274
+ * clone, and a truth-staged landing is masked from ordinary readers by
275
+ * heldTruthMasked — neither is a plain staged write to mirror. Chained
276
+ * backings serve the inner store's live value, never a node value. */ function heldFoldTransition(e) {
277
+ if (e.ch || e.fam?.opt === true || inDraft(e)) return null;
278
+ return liveFoldTransition(e);
279
+ }
280
+
281
+ /**
282
+ * Rule 1 at the backing, the hold half (core serve()'s stale-of-foreign and
283
+ * A29 arms, for a container instead of a node): given the transaction
284
+ * holding what a reader `c` (a pass; callers serve context-free readers the
285
+ * committed container themselves) is about to be served, is the STAGED
286
+ * container its to see? Its own hold, or none — yes. A foreign hold — a
287
+ * stale pass (render effect) keeps the committed frame and is recorded for
288
+ * replay at the hold's commit (recordStaleReplay, A15 / A26); a deriving
289
+ * pass takes the staged world and enters the transaction (enterStagedRead,
290
+ * A29). Shared by both hold kinds: a setter's fold (committed `v`, staged
291
+ * `pb`, the write-time stamp) and an adoption under a transaction
292
+ * (committed = the held view `hv`, staged = the adopted `v`, #3074).
293
+ */ function holdVisible(e, t) {
294
+ if (e === null || ownsHold(e)) return true;
295
+ if (stale) {
296
+ recordStaleReplay(e, t);
297
+ return false;
298
+ }
299
+ enterStagedRead(null, e);
300
+ return true;
301
+ }
302
+
303
+ function stageHeldKey(e, t, n) {
304
+ if (slotNodeEquals.call(e, e.Qe, t)) return;
305
+ e.ve = t;
306
+ e.Ge = n;
307
+ n.Ot.push(e);
256
308
  }
257
309
 
258
310
  function sameLogicalSlot(e, t, n) {
@@ -265,22 +317,28 @@ function getHasNode(e, t, n) {
265
317
  const r = e.h ??= Object.create(null);
266
318
  let i = r[t];
267
319
  if (i === undefined) {
268
- const o = i = signal(n, {
320
+ const l = i = signal(n, {
269
321
  equals: isEqual,
270
322
  unobserved() {
271
- if (o.o?.t) return;
272
- if (e.h && e.h[t] === o) {
323
+ if (l.o?.t) return;
324
+ // The structural twin of the value slot's rule (setSlotUnobserved,
325
+ // S7): an optimistic add/delete lives on this presence node as its
326
+ // override — releasing it with the override on would let `in`,
327
+ // `Object.keys` and descriptors fall back to committed structure
328
+ // while the action is live. Defer to the flush that resolves it.
329
+ if (hasActiveOverride(l) || l.ve !== NOT_PENDING) return deferSlotRelease(l);
330
+ if (e.h && e.h[t] === l) {
273
331
  delete e.h[t];
274
- unlinkFirewallChild(o);
332
+ unlinkFirewallChild(l);
275
333
  }
276
334
  }
277
335
  }, e.fam?.node ?? undefined);
278
- o.T |= CONFIG_OWNED_WRITE;
336
+ l.T |= CONFIG_OWNED_WRITE;
279
337
  if (e.fam?.opt) {
280
- ext(o).be = NOT_PENDING;
281
- o.T |= CONFIG_OPTIMISTIC;
338
+ ext(l).Ce = NOT_PENDING;
339
+ l.T |= CONFIG_OPTIMISTIC;
282
340
  }
283
- if (affectsScopesLive()) inheritAffectsMarks(o, e.v, t);
341
+ if (affectsScopesLive()) inheritAffectsMarks(l, e.v, t);
284
342
  r[t] = i;
285
343
  markDescendants(e);
286
344
  }
@@ -301,7 +359,7 @@ function getKeySetNode(e) {
301
359
  }, e.fam?.node ?? undefined);
302
360
  n.T |= CONFIG_OWNED_WRITE;
303
361
  if (e.fam?.opt) {
304
- ext(n).be = NOT_PENDING;
362
+ ext(n).Ce = NOT_PENDING;
305
363
  n.T |= CONFIG_OPTIMISTIC;
306
364
  }
307
365
  e.k = t;
@@ -324,7 +382,7 @@ function getDeepNode(e) {
324
382
  }, e.fam?.node ?? undefined);
325
383
  n.T |= CONFIG_OWNED_WRITE;
326
384
  if (e.fam?.opt) {
327
- ext(n).be = NOT_PENDING;
385
+ ext(n).Ce = NOT_PENDING;
328
386
  n.T |= CONFIG_OPTIMISTIC;
329
387
  }
330
388
  if (affectsScopesLive()) inheritAffectsMarks(n, e.v, $TRACK);
@@ -486,14 +544,14 @@ function ensurePB(e) {
486
544
  if (n !== null) {
487
545
  for (const e of Reflect.ownKeys(n)) {
488
546
  const r = n[e];
489
- if (hasActiveOverride(r)) t[e] = unwrapOverride(r.o?.be);
547
+ if (hasActiveOverride(r)) t[e] = unwrapOverride(r.o?.Ce);
490
548
  }
491
549
  }
492
550
  const r = e.h;
493
551
  if (r !== null) {
494
552
  for (const e of Reflect.ownKeys(r)) {
495
553
  const n = r[e];
496
- if (hasActiveOverride(n) && !unwrapOverride(n.o?.be)) delete t[e];
554
+ if (hasActiveOverride(n) && !unwrapOverride(n.o?.Ce)) delete t[e];
497
555
  }
498
556
  }
499
557
  }
@@ -517,7 +575,7 @@ function ensurePB(e) {
517
575
  * foldHeld's node stamps). */ function heldMaskView(e) {
518
576
  const t = e.ht;
519
577
  if (t === null) return null;
520
- if (t !== PLAIN_HOLD && currentTransition(t)?.ft === true) return e.ht = e.hv = null;
578
+ if (t !== PLAIN_HOLD && currentTransition(t)?.Tt === true) return e.ht = e.hv = null;
521
579
  return e.hv;
522
580
  }
523
581
 
@@ -721,7 +779,7 @@ function drainFolds() {
721
779
  // keys; observed keys also hit the pending-node held check below).
722
780
  const e = foldBatches.get(t);
723
781
  if (e !== undefined) {
724
- if (currentTransition(e).ft === false) {
782
+ if (currentTransition(e).Tt === false) {
725
783
  foldOlds.set(t, n);
726
784
  continue;
727
785
  }
@@ -733,8 +791,8 @@ function drainFolds() {
733
791
  // node (transition-held) re-queues the target for the settling flush.
734
792
  let r = false;
735
793
  const i = t.pb;
736
- const o = t.n;
737
- if (o !== null) {
794
+ const l = t.n;
795
+ if (l !== null) {
738
796
  // Only written keys can hold (their nodes took the setSignal); the
739
797
  // wk bound keeps this O(written) — see notifyWrites. Same fallback
740
798
  // rules as the notify (WK_ALL / accessors / non-plain prototypes).
@@ -742,10 +800,10 @@ function drainFolds() {
742
800
  const n = e === null || e === WK_ALL || t.a === true ||
743
801
  // Overlay pbs chain to the COMMITTED object (#3044) — plainness is
744
802
  // the committed container's prototype, not the overlay's.
745
- !plainProto(t.ovl ? t.v : i) ? Reflect.ownKeys(o) : e;
803
+ !plainProto(t.ovl ? t.v : i) ? Reflect.ownKeys(l) : e;
746
804
  for (const e of n) {
747
- const t = o[e];
748
- if (t !== undefined && t.Ge !== NOT_PENDING) {
805
+ const t = l[e];
806
+ if (t !== undefined && t.ve !== NOT_PENDING) {
749
807
  r = true;
750
808
  break;
751
809
  }
@@ -887,20 +945,20 @@ function drainFolds() {
887
945
  // bound never engages for overlay writes (every plain-object setter batch
888
946
  // would full-scan: the exact selection-map workload wk exists for; jf
889
947
  // `select` regressed 2x on this).
890
- const o = i === WK_ALL || e.a === true || !plainProto(e.ovl ? e.v : t) ? null : i;
948
+ const l = i === WK_ALL || e.a === true || !plainProto(e.ovl ? e.v : t) ? null : i;
891
949
  if (r !== null) {
892
- const i = o ?? Reflect.ownKeys(r);
893
- for (const o of i) {
894
- const i = r[o];
950
+ const i = l ?? Reflect.ownKeys(r);
951
+ for (const l of i) {
952
+ const i = r[l];
895
953
  if (i === undefined) continue;
896
954
  // Per-key accessor handling: the node's cached flag plus ONE getter
897
955
  // probe on the incoming side (getters arriving via merge/adoption).
898
956
  // Setter-only props read as data (value undefined) so lookupSetter is
899
957
  // not consulted on this hot path; prototype getters never own nodes.
900
- if (i.acc === true || hasOwn.call(t, o) && lookupGetter.call(t, o) !== undefined) {
901
- i.acc = isOwnAccessor(t, o);
902
- const e = Object.getOwnPropertyDescriptor(n, o);
903
- const r = Object.getOwnPropertyDescriptor(t, o);
958
+ if (i.acc === true || hasOwn.call(t, l) && lookupGetter.call(t, l) !== undefined) {
959
+ i.acc = isOwnAccessor(t, l);
960
+ const e = Object.getOwnPropertyDescriptor(n, l);
961
+ const r = Object.getOwnPropertyDescriptor(t, l);
904
962
  if (e && (e.get || e.set) || r && (r.get || r.set)) {
905
963
  if (e?.get !== r?.get || e?.set !== r?.set || e?.value !== r?.value) setSignal(i, () => FORCE);
906
964
  continue;
@@ -912,26 +970,26 @@ function drainFolds() {
912
970
  // projection recompute can run before the prior fold commits) — the
913
971
  // node's OWN current value is the true old side, and setSignal's
914
972
  // internal equality already checks exactly that.
915
- const l = e.del !== null && e.del.has(o) ? undefined : t[o];
916
- setSignal(i, () => l);
973
+ const o = e.del !== null && e.del.has(l) ? undefined : t[l];
974
+ setSignal(i, () => o);
917
975
  }
918
976
  }
919
- const l = e.h;
920
- if (l !== null) {
921
- const n = o ?? Reflect.ownKeys(l);
977
+ const o = e.h;
978
+ if (o !== null) {
979
+ const n = l ?? Reflect.ownKeys(o);
922
980
  for (const r of n) {
923
- const n = l[r];
981
+ const n = o[r];
924
982
  if (n !== undefined) setSignal(n, r in t && !(e.del !== null && e.del.has(r)));
925
983
  }
926
984
  }
927
985
  // Deep-witness (dk): setter writes must notify a deep() subscriber even on
928
986
  // keys with no node. O(written/pb keys) equality only when a witness exists.
929
987
  if (e.dk !== null) {
930
- if (e.del !== null && e.del.size !== 0) bumpDeep(e); else for (const r of o ?? Reflect.ownKeys(t)) {
988
+ if (e.del !== null && e.del.size !== 0) bumpDeep(e); else for (const r of l ?? Reflect.ownKeys(t)) {
931
989
  if (r === $OWNER) continue;
932
990
  const i = t[r];
933
- const o = n[r];
934
- if (i !== null && typeof i === "object" ? !targetsEqual(o, i) : !isEqual(o, i)) {
991
+ const l = n[r];
992
+ if (i !== null && typeof i === "object" ? !targetsEqual(l, i) : !isEqual(l, i)) {
935
993
  bumpDeep(e);
936
994
  break;
937
995
  }
@@ -1041,23 +1099,23 @@ i = true) {
1041
1099
  if (e.acc === true || i && hasOwn.call(r, t) && lookupGetter.call(r, t) !== undefined) {
1042
1100
  e.acc = isOwnAccessor(r, t);
1043
1101
  const i = Object.getOwnPropertyDescriptor(n, t);
1044
- const o = Object.getOwnPropertyDescriptor(r, t);
1045
- if (i && (i.get || i.set) || o && (o.get || o.set)) {
1102
+ const l = Object.getOwnPropertyDescriptor(r, t);
1103
+ if (i && (i.get || i.set) || l && (l.get || l.set)) {
1046
1104
  // Accessor involved: never invoke; force-notify on shape change so
1047
1105
  // subscribers re-read (and re-track) through the trap.
1048
- if (i?.get !== o?.get || i?.set !== o?.set || i?.value !== o?.value) setSignal(e, () => FORCE);
1106
+ if (i?.get !== l?.get || i?.set !== l?.set || i?.value !== l?.value) setSignal(e, () => FORCE);
1049
1107
  return;
1050
1108
  }
1051
- const l = i?.value;
1052
- const f = o?.value;
1053
- if (!isEqual(l, f) && !targetsEqual(l, f)) setSignal(e, typeof f === "function" ? () => f : f);
1109
+ const o = i?.value;
1110
+ const f = l?.value;
1111
+ if (!isEqual(o, f) && !targetsEqual(o, f)) setSignal(e, typeof f === "function" ? () => f : f);
1054
1112
  } else {
1055
1113
  const i = n[t];
1056
- const o = r[t];
1114
+ const l = r[t];
1057
1115
  // Direct value write when not a function (setSignal treats functions as
1058
1116
  // updaters) — saves a closure allocation per changed key on the fold
1059
1117
  // hot path.
1060
- if (!isEqual(i, o) && !targetsEqual(i, o)) setSignal(e, typeof o === "function" ? () => o : o);
1118
+ if (!isEqual(i, l) && !targetsEqual(i, l)) setSignal(e, typeof l === "function" ? () => l : l);
1061
1119
  }
1062
1120
  }
1063
1121
 
@@ -1069,9 +1127,9 @@ i = true) {
1069
1127
  /** Fused-walk per-key notification with values already in hand: the caller
1070
1128
  * fetched both sides and handled the identity skip; this applies the
1071
1129
  * accessor branch (cached flag only — reconcile channel) or the plain
1072
- * equality/identity-gated write. */ function notifyKeyValue(e, t, n, r, i, o) {
1130
+ * equality/identity-gated write. */ function notifyKeyValue(e, t, n, r, i, l) {
1073
1131
  if (e.acc === true) {
1074
- notifyKeyDiff(e, t, i, o, false);
1132
+ notifyKeyDiff(e, t, i, l, false);
1075
1133
  return;
1076
1134
  }
1077
1135
  // The pre-compare is NOT redundant with the node's equals: setSignal parks
@@ -1200,22 +1258,12 @@ const UNSAFE_KEYS = new Set([ "__proto__", "prototype", "constructor" ]);
1200
1258
  * observer) decides pending visibility, with Roots resolving to their parent
1201
1259
  * computed (#2687 — untracked reads inside mapArray Roots see in-flight
1202
1260
  * values mid-flush). CHILDREN_FORBIDDEN execution scopes (createTrackedEffect
1203
- * / onSettled callbacks) get COMMITTED visibility (#3006), same as core. */ function inOwnerContext() {
1204
- const e = getOwner();
1205
- if (e === null) return false;
1206
- const t = e.gt ? e.bt : e;
1207
- return t != null && !(t.T & CONFIG_CHILDREN_FORBIDDEN);
1208
- }
1209
-
1210
- /** CHILDREN_FORBIDDEN execution scope (createTrackedEffect / onSettled
1211
- * callbacks). Distinct from context-free: these scopes get committed
1212
- * visibility even against a projection's authoritative-elect pending
1213
- * backing (#3082) — parity with signals, where core read() serves
1214
- * committed to them regardless of staged writes. */ function inForbiddenScope() {
1261
+ * / onSettled callbacks) get COMMITTED visibility (#3006), same as core. */
1262
+ /** Core read()'s reader: the current computation, a root reading as its
1263
+ * parent computed (`context` persists under untrack — an untracked read
1264
+ * inside an effect is still that effect's read). */ function readerContext() {
1215
1265
  const e = getOwner();
1216
- if (e === null) return false;
1217
- const t = e.gt ? e.bt : e;
1218
- return t != null && !!(t.T & CONFIG_CHILDREN_FORBIDDEN);
1266
+ return e === null ? null : e.xt ? e.Qt ?? null : e;
1219
1267
  }
1220
1268
 
1221
1269
  /** A pending fold is transition-held when any written node's parked value is
@@ -1225,19 +1273,24 @@ const UNSAFE_KEYS = new Set([ "__proto__", "prototype", "constructor" ]);
1225
1273
  if (t === null) return false;
1226
1274
  for (const e of Reflect.ownKeys(t)) {
1227
1275
  const n = t[e];
1228
- if (n.Ge !== NOT_PENDING && n.ge != null && n.ge.ft !== true) return true;
1276
+ if (n.ve !== NOT_PENDING && n.Ge != null && n.Ge.Tt !== true) return true;
1229
1277
  }
1230
1278
  return false;
1231
1279
  }
1232
1280
 
1233
1281
  function readSource(e) {
1234
- // Held view first (#3074): an adoption staged under a live hold serves the
1235
- // pre-hold committed backing to committed-visibility readers. Speculative
1236
- // readers — drafts, write-override, owner-context computeds recomputing
1237
- // inside the transaction, and latest() reads see the adopted backing.
1238
- if (e.ht !== null && !latestReadActive && !inDraft(e) && !getWriteOverride() && !inOwnerContext()) {
1239
- const t = heldMaskView(e);
1240
- if (t !== null) return t;
1282
+ // Adoption hold first (#3074): an adoption staged under a live transaction
1283
+ // (or a latest()-pull, PLAIN_HOLD) serves the pre-hold committed view to
1284
+ // committed-visibility readers — context-free and children-forbidden ones,
1285
+ // and stale passes off a foreign hold. Drafts, write-override and latest()
1286
+ // see the adopted backing.
1287
+ const t = e.ht;
1288
+ if (t !== null && !latestReadActive && !inDraft(e) && !getWriteOverride()) {
1289
+ const n = heldMaskView(e);
1290
+ if (n !== null) {
1291
+ const e = readerContext();
1292
+ if (e === null || e.T & CONFIG_CHILDREN_FORBIDDEN || !holdVisible(t === PLAIN_HOLD ? null : currentTransition(t), e)) return n;
1293
+ }
1241
1294
  }
1242
1295
  return pendingBackingVisible(e, false) ? e.pb : e.v;
1243
1296
  }
@@ -1257,21 +1310,28 @@ function readSource(e) {
1257
1310
  * ordinary pending staging regardless of owner context (the documented
1258
1311
  * divergence from context-free per-key reads) — but never through a hold:
1259
1312
  * held truth stays masked exactly as it is for per-key readers. */ function pendingBackingVisible(e, t) {
1260
- return e.pb !== null && (inDraft(e) || getWriteOverride() ||
1261
- // Owner-context (and speculative-peek) readers see the pending
1262
- // backing EXCEPT held truth on an optimistic family (#3164 fold):
1263
- // a live pb on an opt family outside the draft/write-override windows
1264
- // is a staged landing (tentative drafts never outlive their setter),
1265
- // and only the authoritative postures and latest() see it (the
1266
- // backing-level twin of core read()'s A17-for-held-truth arm;
1267
- // ordinary readers keep committed until the transaction's reveal).
1268
- (t || inOwnerContext()) && !heldTruthMasked(e) ||
1269
- // A projection's pending backing is authoritative-elect: serve it to
1270
- // context-free readers too UNLESS a transition is holding the node
1271
- // commits (downstream async hold stale committed is the contract)
1272
- // or the reader is a CHILDREN_FORBIDDEN scope, which never observes
1273
- // its own unsettled write (#3082, signal parity per #3006).
1274
- e.fam !== null && !heldTruthMasked(e) && !foldHeld(e) && !inForbiddenScope());
1313
+ if (e.pb === null) return false;
1314
+ // The writer's own channels compose on the pending backing regardless.
1315
+ if (inDraft(e) || getWriteOverride()) return true;
1316
+ // HELD truth on an optimistic family (#3164 fold) is masked from ordinary
1317
+ // readers until the transaction's reveal (the backing-level twin of core
1318
+ // serve()'s CONFIG_HELD_TRUTH arm; authoritative postures and latest()
1319
+ // tunnel through inside heldTruthMasked).
1320
+ if (heldTruthMasked(e)) return false;
1321
+ const n = readerContext();
1322
+ if (n === null || n.T & CONFIG_CHILDREN_FORBIDDEN) {
1323
+ // No pass, or a children-forbidden one: the committed frame (A32) —
1324
+ // except the speculative peek (deep()/snapshot()), which sees ordinary
1325
+ // pending staging but never through a live foreign hold, and a
1326
+ // projection's pending backing, authoritative-elect for context-free
1327
+ // readers UNLESS a transition holds the node commits (downstream async
1328
+ // hold — stale committed is the contract; the write-time stamp covers
1329
+ // keys with no node, #3336) or the scope is children-forbidden (#3082).
1330
+ const r = liveFoldTransition(e);
1331
+ if (t) return r === null || ownsHold(r);
1332
+ return e.fam !== null && n === null && !foldHeld(e) && r === null;
1333
+ }
1334
+ return holdVisible(liveFoldTransition(e), n);
1275
1335
  }
1276
1336
 
1277
1337
  /** #3164 fold: HELD truth on an optimistic family — a pending backing
@@ -1316,11 +1376,6 @@ function isOwnAccessor(e, t) {
1316
1376
  }
1317
1377
  }
1318
1378
 
1319
- /** Active optimistic override on an armed node (armed slot idles at
1320
- * NOT_PENDING; undefined = unarmed plain node). */ function hasActiveOverride(e) {
1321
- return e.o?.be !== undefined && e.o?.be !== NOT_PENDING;
1322
- }
1323
-
1324
1379
  /** The reading computation is until()'s authoritative-view predicate — same
1325
1380
  * source of truth as core read()'s A17 carve-out (`context`, which persists
1326
1381
  * under untrack). optimisticView()'s composition gate consults exactly this:
@@ -1348,19 +1403,17 @@ function isOwnAccessor(e, t) {
1348
1403
  * reader that forced it (backing commits eagerly; node values fold at flush).
1349
1404
  * FORCE sentinels never surface (they only bump subscribers of accessor
1350
1405
  * keys, which are served by the trap, not the node). */ function nodeValue(e, t) {
1351
- // latest() sees the in-flight parked value like an owner-context reader
1352
- // does (#3075) signal/memo parity for store-node-backed keys.
1353
- // Authoritative-view reads (until()'s predicate) skip the override arm
1354
- // only: staged pending values are authoritative, overrides are the
1355
- // caller's optimism.
1356
- const n = !authoritativeServe() && hasActiveOverride(e) ? unwrapOverride(e.o?.be) : e.Ge !== NOT_PENDING && (latestReadActive ||
1357
- // Owner-context pending visibility except HELD truth (#3164,
1358
- // see CONFIG_HELD_TRUTH: fold-staged or entangle-stolen
1359
- // confirming truth), which only authoritative/latest readers
1360
- // see (core read()'s A17-for-held-truth twin; ordinary readers
1361
- // keep committed until the transaction's reveal latest() is
1362
- // exempted by the leading arm above).
1363
- (inOwnerContext() || authoritativeServe()) && !(e.T & CONFIG_HELD_TRUTH && !authoritativeServe())) ? e.Ge : t;
1406
+ // Store-only tunnels, ahead of Rule 1: truth authors (authoritativeServe
1407
+ // the projection derive's draft, the write-override continuation) see
1408
+ // staged truth and never an override; latest() reaching this untracked
1409
+ // path for a store key sees the in-flight parked value like an
1410
+ // owner-context reader does (#3075), the visible override first.
1411
+ let n;
1412
+ if (authoritativeServe()) n = e.ve !== NOT_PENDING ? e.ve : t; else if (latestReadActive) n = visibleOverride(e) ? unwrapOverride(e.o?.Ce) : e.ve !== NOT_PENDING ? e.ve : t;
1413
+ // Otherwise the one slow selection core read() uses (serve): override,
1414
+ // lane gate, A28, readerSeesCommitted / A29 — with the BACKING as the
1415
+ // committed value (single-home rule, O6).
1416
+ else n = serve(e, readerContext(), e.Te || e, t);
1364
1417
  return n === FORCE ? t : n;
1365
1418
  }
1366
1419
 
@@ -1391,15 +1444,15 @@ function isOwnAccessor(e, t) {
1391
1444
  * holds, lanes ride the node); backing otherwise. Chained backings (§7b: the
1392
1445
  * backing IS another store's proxy) serve the read-through value — the outer
1393
1446
  * node is linked only for adoption-swap notification, its value never
1394
- * shadows the live chain. */ function serveDataKey(e, t, n, r, i, o = -1) {
1395
- const l = e.ch && r === e.v;
1447
+ * shadows the live chain. */ function serveDataKey(e, t, n, r, i, l = -1) {
1448
+ const o = e.ch && r === e.v;
1396
1449
  let f = n;
1397
1450
  // §6: on optimistic arrays LENGTH IS A VIEW, not a node value — one home
1398
1451
  // (backing ± presence overrides) for both length and indices makes torn
1399
1452
  // iteration impossible (a length node's value rides different visibility
1400
1453
  // rails than index overrides mid-settle). The node still carries
1401
1454
  // subscriptions; its value is never served here.
1402
- if (t === "length" && e.fam?.opt === true && !l && Array.isArray(r)) {
1455
+ if (t === "length" && e.fam?.opt === true && !o && Array.isArray(r)) {
1403
1456
  if (!inDraft(e)) {
1404
1457
  const r = e.n?.length;
1405
1458
  if (r !== undefined) {
@@ -1411,7 +1464,7 @@ function isOwnAccessor(e, t) {
1411
1464
  // Truth authors read the backing's own length — an optimistic row from
1412
1465
  // the caller's transaction must not shift where the author's next write
1413
1466
  // lands (#3108).
1414
- return (authoritativeServe() ? r : optHooks.optimisticView(e, r)).length;
1467
+ return (authoritativeServe() ? r : optHooks.optimisticView(e, r, inDraft(e))).length;
1415
1468
  }
1416
1469
  if (inDraft(e)) {
1417
1470
  // Optimistic drafts before their first write have no pending backing yet;
@@ -1421,31 +1474,31 @@ function isOwnAccessor(e, t) {
1421
1474
  // seeding rule, applied to the read side (#3108).
1422
1475
  if (e.fam?.opt && draftSeesOverrides(e) && !authoritativeServe()) {
1423
1476
  const n = e.n?.[t];
1424
- if (n !== undefined && hasActiveOverride(n)) f = unwrapOverride(n.o?.be);
1477
+ if (n !== undefined && hasActiveOverride(n)) f = unwrapOverride(n.o?.Ce);
1425
1478
  }
1426
1479
  } else {
1427
- if (i !== undefined) {
1428
- // §7b: a lane value on the outer node SHADOWS read-through an active
1429
- // override pierces the chained gate; otherwise chained backings always
1430
- // serve the live inner value.
1431
- if (getObserver() !== null) {
1432
- // read()'s plain-signal fast path hoisted over the call (legacy trap
1433
- // parity): READ_SLOW = a global read window or non-plain node.
1434
- let e = readNodeFast(i);
1435
- if (e === READ_SLOW) e = read(i);
1436
- if (!l || hasActiveOverride(i)) f = e === FORCE ? n : e;
1437
- } else if (!l || hasActiveOverride(i)) {
1438
- f = nodeValue(i, n);
1439
- }
1440
- } else if (getObserver() !== null) {
1441
- // First tracked read: create + link, and let the wrap-cache branch
1442
- // below populate px/pxv so read #2 skips wrapNext (slice 2).
1443
- read(i = getNode(e, t, n, o));
1480
+ // §7b: a lane value on the outer node SHADOWS read-through — an active
1481
+ // override pierces the chained gate; otherwise chained backings always
1482
+ // serve the live inner value.
1483
+ if (getObserver() !== null) {
1484
+ // First tracked read: create + link (the wrap-cache branch below
1485
+ // populates px/pxv so read #2 skips wrapNext, slice 2). The value is
1486
+ // served THROUGH the node from this read on a node born under a held
1487
+ // fold carries the hold (getNode, #3336), and the backing it was read
1488
+ // from does not.
1489
+ if (i === undefined) i = getNode(e, t, n, l);
1490
+ // read()'s plain-signal fast path hoisted over the call (legacy trap
1491
+ // parity): READ_SLOW = a global read window or non-plain node.
1492
+ let r = readNodeFast(i);
1493
+ if (r === READ_SLOW) r = read(i);
1494
+ if (!o || hasActiveOverride(i)) f = r === FORCE ? n : r;
1495
+ } else if (i !== undefined && (!o || hasActiveOverride(i))) {
1496
+ f = nodeValue(i, n);
1444
1497
  }
1445
1498
  }
1446
1499
  // Shallow stores serve data raw; store-proxy slots get boundary wrappers.
1447
1500
  if (e.s) return serveShallow(e, t, f);
1448
- if (e.ch && !l && f !== null && typeof f === "object" && f[$TARGET] === undefined) f = resolveChainedRaw(e, t, f);
1501
+ if (e.ch && !o && f !== null && typeof f === "object" && f[$TARGET] === undefined) f = resolveChainedRaw(e, t, f);
1449
1502
  if (i !== undefined) {
1450
1503
  // Wrap cache (see getNode): only wrappables are ever cached, so a hit
1451
1504
  // skips isWrappable too — pointer-compare replaces both checks.
@@ -1581,18 +1634,18 @@ const traps = {
1581
1634
  // node creation downstream reuses it instead of re-scanning the
1582
1635
  // descriptor, but only when the probed object IS the one getNode would
1583
1636
  // scan (pb ?? v).
1584
- let o = -1;
1637
+ let l = -1;
1585
1638
  {
1586
- let l;
1587
- if (i !== undefined) l = i.acc === true; else if (!inDraft(e) && getObserver() !== null) {
1588
- l = isOwnAccessor(r, t);
1589
- if (r === (e.pb ?? e.v)) o = l ? 1 : 0;
1590
- } else l = false;
1591
- if (l) {
1592
- if (!inDraft(e) && getObserver() !== null) read(i ?? getNode(e, t, undefined, o));
1593
- const l = Reflect.get(r, t, n);
1594
- if (e.s) return serveShallow(e, t, l);
1595
- return isWrappable(l) ? draftServe(e, wrapNext(l, e, t)) : l;
1639
+ let o;
1640
+ if (i !== undefined) o = i.acc === true; else if (!inDraft(e) && getObserver() !== null) {
1641
+ o = isOwnAccessor(r, t);
1642
+ if (r === (e.pb ?? e.v)) l = o ? 1 : 0;
1643
+ } else o = false;
1644
+ if (o) {
1645
+ if (!inDraft(e) && getObserver() !== null) read(i ?? getNode(e, t, undefined, l));
1646
+ const o = Reflect.get(r, t, n);
1647
+ if (e.s) return serveShallow(e, t, o);
1648
+ return isWrappable(o) ? draftServe(e, wrapNext(o, e, t)) : o;
1596
1649
  }
1597
1650
  }
1598
1651
  // Plain-data fast path: no descriptor allocation per read.
@@ -1602,10 +1655,10 @@ const traps = {
1602
1655
  // backings chain to the committed backing, so "own in the view" means
1603
1656
  // own on either layer (ownInView) — a genuine prototype method is one
1604
1657
  // that is own on NEITHER.
1605
- const l = e.ovl && r === e.pb;
1606
- if ((t === "constructor" || t === "__proto__" || t === "prototype") && !hasOwn.call(r, t) && !(l && hasOwn.call(e.v, t))) return undefined;
1658
+ const o = e.ovl && r === e.pb;
1659
+ if ((t === "constructor" || t === "__proto__" || t === "prototype") && !hasOwn.call(r, t) && !(o && hasOwn.call(e.v, t))) return undefined;
1607
1660
  let f = r[t];
1608
- if (f === undefined ? !hasOwn.call(r, t) && !(l && hasOwn.call(e.v, t)) : false) {
1661
+ if (f === undefined ? !hasOwn.call(r, t) && !(o && hasOwn.call(e.v, t)) : false) {
1609
1662
  // Inherited: prototype getters/methods run with the proxy receiver.
1610
1663
  f = Reflect.get(r, t, n);
1611
1664
  if (typeof f === "function") return f;
@@ -1613,7 +1666,7 @@ const traps = {
1613
1666
  // Reading a currently-absent own key subscribes to it (R12) — for any
1614
1667
  // target OUTSIDE its own draft scope, even mid-setter (#3037, above).
1615
1668
  if (f === undefined && !inDraft(e)) {
1616
- if (getObserver() !== null) read(getNode(e, t, undefined, o));
1669
+ if (getObserver() !== null) read(getNode(e, t, undefined, l));
1617
1670
  const n = e.n?.[t];
1618
1671
  if (n) {
1619
1672
  const r = nodeValue(n, undefined);
@@ -1625,14 +1678,14 @@ const traps = {
1625
1678
  // the caller's optimism is not truth (has-trap twin below).
1626
1679
  !authoritativeServe()) {
1627
1680
  const n = e.n?.[t];
1628
- if (n !== undefined && hasActiveOverride(n)) f = unwrapOverride(n.o?.be);
1681
+ if (n !== undefined && visibleOverride(n)) f = unwrapOverride(n.o?.Ce);
1629
1682
  }
1630
1683
  if (e.s) return serveShallow(e, t, f);
1631
1684
  return isWrappable(f) ? draftServe(e, wrapNext(f, e, t)) : f;
1632
1685
  }
1633
- if (typeof f === "function" && !hasOwn.call(r, t) && !(l && hasOwn.call(e.v, t))) return f;
1686
+ if (typeof f === "function" && !hasOwn.call(r, t) && !(o && hasOwn.call(e.v, t))) return f;
1634
1687
  // proto method
1635
- return serveDataKey(e, t, f, r, i, o);
1688
+ return serveDataKey(e, t, f, r, i, l);
1636
1689
  },
1637
1690
  has(e, t) {
1638
1691
  if (t === $TARGET || t === $PROXY || t === $TRACK) return true;
@@ -1652,11 +1705,11 @@ const traps = {
1652
1705
  if (hasActiveOverride(n)) r = !!i;
1653
1706
  } else if (!authoritativeServe()) {
1654
1707
  const n = e.h?.[t];
1655
- if (n !== undefined && hasActiveOverride(n)) r = !!unwrapOverride(n.o?.be);
1708
+ if (n !== undefined && visibleOverride(n)) r = !!unwrapOverride(n.o?.Ce);
1656
1709
  }
1657
1710
  } else if (e.fam?.opt && draftSeesOverrides(e) && !authoritativeServe()) {
1658
1711
  const n = e.h?.[t];
1659
- if (n !== undefined && hasActiveOverride(n)) r = !!unwrapOverride(n.o?.be);
1712
+ if (n !== undefined && hasActiveOverride(n)) r = !!unwrapOverride(n.o?.Ce);
1660
1713
  }
1661
1714
  return r;
1662
1715
  },
@@ -1668,13 +1721,30 @@ const traps = {
1668
1721
  },
1669
1722
  getOwnPropertyDescriptor(e, t) {
1670
1723
  if (t === $OWNER) return undefined;
1671
- const n = visibleDescriptor(e, readSource(e), t);
1672
- if (n === undefined) return undefined;
1724
+ // A descriptor read is a PRESENCE read: it subscribes to the key's
1725
+ // presence node and witnesses affects()/isPending() exactly as `in` does
1726
+ // (structural oracle, 2026-09-17 — the trap read no node before, so a
1727
+ // render effect inspecting a key through getOwnPropertyDescriptor never
1728
+ // re-ran for an optimistic add or delete, and an isPending() probe over
1729
+ // it witnessed nothing). The value it reports rides the value node's
1730
+ // view through visibleDescriptor.
1731
+ if (pendingCheckActive) witnessAffectsMark(e, t);
1732
+ if (e.fam !== null && getObserver() === null && !inDraft(e)) firewallGate(e);
1733
+ const n = readSource(e);
1734
+ const r = visibleDescriptor(e, n, t);
1735
+ if (!inDraft(e) && getObserver() !== null) {
1736
+ // The node is born from the source's presence (as `has` births it),
1737
+ // not the override-adjusted answer.
1738
+ let r = t in n;
1739
+ if (r && e.del !== null && n === e.pb && e.del.has(t)) r = false;
1740
+ read(getHasNode(e, t, r));
1741
+ }
1742
+ if (r === undefined) return undefined;
1673
1743
  // Array targets carry a real non-configurable `length` the proxy
1674
1744
  // invariant forces us to report faithfully; everything else reports
1675
1745
  // configurable via target indirection (core R51).
1676
- if (!(t === "length" && Array.isArray(e))) n.configurable = true;
1677
- return n;
1746
+ if (!(t === "length" && Array.isArray(e))) r.configurable = true;
1747
+ return r;
1678
1748
  },
1679
1749
  set(e, t, n) {
1680
1750
  // Writes require the target's draft scope OR the projection write
@@ -1691,13 +1761,13 @@ const traps = {
1691
1761
  // Shallow slots store what was written VERBATIM — another store's proxy
1692
1762
  // passes through by reference (#2932; markRawOne skips proxies), while
1693
1763
  // deep stores unwrap to raw backings.
1694
- const o = e.s ? n : unwrapValue(n);
1695
- const l = ensurePB(e);
1764
+ const l = e.s ? n : unwrapValue(n);
1765
+ const o = ensurePB(e);
1696
1766
  pendingNotify.add(e);
1697
1767
  // Array length writes implicitly delete indices — the written-keys bound
1698
1768
  // can't see them, so poison to the full scan for this batch. Index
1699
1769
  // writes implicitly GROW length, so arrays always record it alongside.
1700
- if (Array.isArray(l)) {
1770
+ if (Array.isArray(o)) {
1701
1771
  if (t === "length") e.wk = WK_ALL; else if (e.wk !== WK_ALL) {
1702
1772
  const n = e.wk ??= new Set;
1703
1773
  n.add(t);
@@ -1709,13 +1779,13 @@ const traps = {
1709
1779
  // sees through an overlay to the committed keys, so this counts keys
1710
1780
  // NEW to the container. Deletes are not un-counted (a stale high
1711
1781
  // count only picks the overlay a little early).
1712
- if (!(t in l)) e.kc++;
1782
+ if (!(t in o)) e.kc++;
1713
1783
  }
1714
1784
  // Own data keys literally named "prototype"/"constructor" land as data —
1715
1785
  // defineProperty sidesteps a proto-chain setter named the same.
1716
1786
  if (UNSAFE_KEYS.has(t)) {
1717
- Object.defineProperty(l, t, {
1718
- value: o,
1787
+ Object.defineProperty(o, t, {
1788
+ value: l,
1719
1789
  writable: true,
1720
1790
  enumerable: true,
1721
1791
  configurable: true
@@ -1728,18 +1798,18 @@ const traps = {
1728
1798
  // path normalized descriptors for exactly this — R51 parity). A
1729
1799
  // plain-data-graded backing (sc 2, owned: every slot writable) takes the
1730
1800
  // bare assignment — it lands as an own key on the overlay all the same.
1731
- if (e.ovl && e.sc !== 2 && !hasOwn.call(l, t)) {
1732
- Object.defineProperty(l, t, {
1733
- value: o,
1801
+ if (e.ovl && e.sc !== 2 && !hasOwn.call(o, t)) {
1802
+ Object.defineProperty(o, t, {
1803
+ value: l,
1734
1804
  writable: true,
1735
1805
  enumerable: true,
1736
1806
  configurable: true
1737
1807
  });
1738
- } else l[t] = o;
1808
+ } else o[t] = l;
1739
1809
  if (e.del !== null) e.del.delete(t);
1740
1810
  // Shallow ingest: written records are sticky raw-marked (one entity is
1741
1811
  // never both deep-wrapped and raw — R41/#2932, shared invariant).
1742
- if (e.s && o !== null && typeof o === "object") markRawOne(o);
1812
+ if (e.s && l !== null && typeof l === "object") markRawOne(l);
1743
1813
  // Override-mode (post-await draft) writes have no setter exit — notify
1744
1814
  // per-op (setSignal equality-gates repeats).
1745
1815
  if (i) notifyWrites(e);
@@ -1756,14 +1826,14 @@ const traps = {
1756
1826
  ...n,
1757
1827
  value: unwrapValue(n.value)
1758
1828
  };
1759
- const o = ensurePB(e);
1829
+ const l = ensurePB(e);
1760
1830
  // A non-default data descriptor (or an accessor) leaves the plain-data
1761
1831
  // grade: the key reaches the committed backing as defined, so the spread
1762
1832
  // clone and bare-assignment paths no longer describe it.
1763
1833
  if (e.a || !(n.enumerable && n.writable && n.configurable)) e.sc = 1;
1764
1834
  pendingNotify.add(e);
1765
1835
  if (e.wk !== WK_ALL) (e.wk ??= new Set).add(t);
1766
- Object.defineProperty(o, t, n);
1836
+ Object.defineProperty(l, t, n);
1767
1837
  if (e.del !== null) e.del.delete(t);
1768
1838
  if (i) notifyWrites(e);
1769
1839
  return true;
@@ -1786,20 +1856,22 @@ const traps = {
1786
1856
 
1787
1857
  /** Low-level setter primitive: opens write mode on a next proxy, runs `fn`,
1788
1858
  * emits write-time notifications at outermost exit, applies returned
1789
- * replacements as adoptions. `guard=false` skips the owned-scope dev guard —
1790
- * projection recomputes legitimately write from inside their computed. */ function storeSetterNext(e, t, n = true) {
1859
+ * replacements as adoptions. `guard=false` skips the dev guards (owned-scope
1860
+ * write, thenable result) — projection recomputes legitimately write from
1861
+ * inside their computed, and their async derive is handled by the recompute,
1862
+ * not returned through here. */ function storeSetterNext(e, t, n = true) {
1791
1863
  const r = e[$TARGET];
1792
1864
  const i = writeScopes;
1793
1865
  writeScopes = new Set;
1794
1866
  writeScopes.add(scopeKey(r));
1795
1867
  writing++;
1796
- let o;
1868
+ let l;
1797
1869
  try {
1798
1870
  // No untrack: the writing flag already disables store-node linking
1799
1871
  // (draft reads never self-track, proj R2), while EXTERNAL reads (signals
1800
1872
  // inside a projection derive) must keep tracking — they are the derive's
1801
1873
  // dependencies.
1802
- o = t(e);
1874
+ l = t(e);
1803
1875
  } finally {
1804
1876
  writing--;
1805
1877
  writeScopes = i;
@@ -1811,15 +1883,15 @@ const traps = {
1811
1883
  for (const t of e) notifyWrites(t);
1812
1884
  }
1813
1885
  }
1814
- if (o !== undefined && o !== e && isWrappable(o)) {
1886
+ if (l !== undefined && l !== e && isWrappable(l)) {
1815
1887
  // Returned replacement: on an optimistic family (outside authoritative
1816
1888
  // writes) the replacement is itself an optimistic edit — diff it against
1817
1889
  // the visible view as engine writes (reverts at settle). Otherwise it is
1818
1890
  // an adoption of the incoming object (unowned).
1819
1891
  if (r.fam?.opt && !projectionWriteActive && !getWriteOverride()) {
1820
- optHooks.notifyOptimisticWrites(r, unwrapValue(o));
1892
+ optHooks.notifyOptimisticWrites(r, unwrapValue(l));
1821
1893
  } else {
1822
- adoptPB(r, unwrapValue(o));
1894
+ adoptPB(r, unwrapValue(l));
1823
1895
  }
1824
1896
  }
1825
1897
  if (writing === 0 && heldAdoptions.size) stageHeldAdoptions();
@@ -1906,11 +1978,12 @@ function createStoreNext(e, t = false) {
1906
1978
  }
1907
1979
  if (!authoritativeServe() && e.fam?.opt && e.h !== null && (!inDraft(e) || draftSeesOverrides(e))) {
1908
1980
  let t = null;
1909
- for (const r of Reflect.ownKeys(e.h)) {
1910
- const i = e.h[r];
1911
- if (!hasActiveOverride(i)) continue;
1981
+ const r = inDraft(e);
1982
+ for (const i of Reflect.ownKeys(e.h)) {
1983
+ const l = e.h[i];
1984
+ if (!(r ? hasActiveOverride(l) : visibleOverride(l))) continue;
1912
1985
  t ??= new Set(n);
1913
- if (unwrapOverride(i.o?.be)) t.add(r); else t.delete(r);
1986
+ if (unwrapOverride(l.o?.Ce)) t.add(i); else t.delete(i);
1914
1987
  }
1915
1988
  if (t !== null) return [ ...t ];
1916
1989
  }
@@ -1929,8 +2002,8 @@ function createStoreNext(e, t = false) {
1929
2002
  }
1930
2003
  if (!authoritativeServe() && e.fam?.opt && !inDraft(e)) {
1931
2004
  const t = e.h?.[n];
1932
- if (t !== undefined && hasActiveOverride(t)) {
1933
- if (!unwrapOverride(t.o?.be)) return undefined;
2005
+ if (t !== undefined && visibleOverride(t)) {
2006
+ if (!unwrapOverride(t.o?.Ce)) return undefined;
1934
2007
  // opt delete
1935
2008
  if (r === undefined) {
1936
2009
  const t = e.n?.[n];
@@ -2003,23 +2076,23 @@ function createStoreNext(e, t = false) {
2003
2076
  continue;
2004
2077
  // accessors track through their own reads when invoked
2005
2078
  }
2006
- let o = i.value;
2079
+ let l = i.value;
2007
2080
  // An active value override on an optimistic node shadows the backing's
2008
2081
  // child (an optimistic replacement `d[i] = {...}`) — follow what reads
2009
2082
  // serve, as nodeValue's leading arm does.
2010
2083
  if (r) {
2011
2084
  const t = e.n?.[n];
2012
- if (t !== undefined && hasActiveOverride(t) && !authoritativeServe()) o = unwrapOverride(t.o.be);
2085
+ if (t !== undefined && hasActiveOverride(t) && !authoritativeServe()) l = unwrapOverride(t.o.Ce);
2013
2086
  }
2014
- if (o === null || typeof o !== "object") continue;
2087
+ if (l === null || typeof l !== "object") continue;
2015
2088
  // Through a chain the descriptor yields the INNERMOST raw: resolve it to
2016
2089
  // the inner family's proxy so this level wraps the chained `view[i]` the
2017
2090
  // get trap serves, never a fresh non-chained wrapper of the base raw.
2018
- if (e.ch && o[$TARGET] === undefined) o = resolveChainedRaw(e, n, o);
2019
- const l = childTarget(e, o, n);
2020
- if (l === undefined) continue;
2091
+ if (e.ch && l[$TARGET] === undefined) l = resolveChainedRaw(e, n, l);
2092
+ const o = childTarget(e, l, n);
2093
+ if (o === undefined) continue;
2021
2094
  // raw-marked: leaf by contract
2022
- walkT(l);
2095
+ walkT(o);
2023
2096
  }
2024
2097
  };
2025
2098
  walkT(t);
@@ -2051,31 +2124,31 @@ function snapshotWalk(e, t, n) {
2051
2124
  let i = null;
2052
2125
  for (let e = true; ;e = false) {
2053
2126
  const t = r?.[$TARGET]?.v !== undefined;
2054
- let o = t ? r[$TARGET] : undefined;
2055
- if (o === undefined && n !== null) o = lookupTarget(r, n);
2056
- if (o === undefined) o = lookupTarget(r, null);
2057
- if (o === undefined) break;
2127
+ let l = t ? r[$TARGET] : undefined;
2128
+ if (l === undefined && n !== null) l = lookupTarget(r, n);
2129
+ if (l === undefined) l = lookupTarget(r, null);
2130
+ if (l === undefined) break;
2058
2131
  // Entering a level from a RAW below a chained family: the raw resolves to
2059
2132
  // the INNER store's target, but this family's wrapper for it — keyed by
2060
2133
  // the inner proxy, §7b — is where the outer overrides live. Start from
2061
2134
  // the wrapper so they compose (#3323: a nested optimistic write on a view
2062
2135
  // row was served by reads but missing from deep()/snapshot()). Entry only:
2063
2136
  // the descent then runs wrapper → inner → raw and terminates.
2064
- if (e && !t && n !== null && o.fam !== n) {
2065
- const e = n.map.get(o.px);
2066
- if (e !== undefined) o = e;
2137
+ if (e && !t && n !== null && l.fam !== n) {
2138
+ const e = n.map.get(l.px);
2139
+ if (e !== undefined) l = e;
2067
2140
  }
2068
- if (o.fam !== null) n = o.fam;
2069
- if (o.fam?.opt === true) (i ??= []).push(o);
2141
+ if (l.fam !== null) n = l.fam;
2142
+ if (l.fam?.opt === true) (i ??= []).push(l);
2070
2143
  // The shared visibility decision (#3147): the speculative peek serves
2071
2144
  // pending staging, but a HELD landing is masked to committed exactly as
2072
2145
  // it is for per-key readers — the two families must answer alike while
2073
2146
  // a transaction holds store landings.
2074
- const l = pendingBackingVisible(o, true);
2147
+ const o = pendingBackingVisible(l, true);
2075
2148
  // Snapshot runs mid-flush (tracked memos execute before commit), so a
2076
2149
  // pending prototype overlay must present as a REAL merged container.
2077
- if (l && o.ovl) materializePB(o);
2078
- const f = l ? o.pb : o.v;
2150
+ if (o && l.ovl) materializePB(l);
2151
+ const f = o ? l.pb : l.v;
2079
2152
  if (f === r) break;
2080
2153
  r = f;
2081
2154
  }
@@ -2088,20 +2161,20 @@ function snapshotWalk(e, t, n) {
2088
2161
  if (e !== r) {
2089
2162
  const i = t.get(r);
2090
2163
  if (i !== undefined) return i;
2091
- const o = Array.isArray(e);
2092
- const l = o ? [] : Object.create(Object.getPrototypeOf(e));
2093
- t.set(r, l);
2164
+ const l = Array.isArray(e);
2165
+ const o = l ? [] : Object.create(Object.getPrototypeOf(e));
2166
+ t.set(r, o);
2094
2167
  for (const r of Reflect.ownKeys(e)) {
2095
- if (o && r === "length" || r === $OWNER) continue;
2168
+ if (l && r === "length" || r === $OWNER) continue;
2096
2169
  const i = e[r];
2097
- l[r] = i !== null && typeof i === "object" ? snapshotWalk(i, t, n) : i;
2170
+ o[r] = i !== null && typeof i === "object" ? snapshotWalk(i, t, n) : i;
2098
2171
  }
2099
- if (o) l.length = e.length;
2100
- return l;
2172
+ if (l) o.length = e.length;
2173
+ return o;
2101
2174
  }
2102
2175
  }
2103
- const o = t.get(r);
2104
- if (o !== undefined) return o;
2176
+ const l = t.get(r);
2177
+ if (l !== undefined) return l;
2105
2178
  // OWNED (written) subtrees snapshot as copies (§7b: identity is only for
2106
2179
  // subtrees "unmodified relative to source"): non-enumerable symbols are
2107
2180
  // excluded (recon-snap R29), and the copy registers BEFORE descent so
@@ -2110,18 +2183,18 @@ function snapshotWalk(e, t, n) {
2110
2183
  const e = Array.isArray(r);
2111
2184
  const i = e ? [] : Object.create(Object.getPrototypeOf(r));
2112
2185
  t.set(r, i);
2113
- for (const o of Reflect.ownKeys(r)) {
2114
- if (e && o === "length" || o === $OWNER) continue;
2115
- const l = Object.getOwnPropertyDescriptor(r, o);
2116
- if (typeof o === "symbol" && !l.enumerable) continue;
2117
- if (l.get || l.set) {
2118
- Object.defineProperty(i, o, l);
2186
+ for (const l of Reflect.ownKeys(r)) {
2187
+ if (e && l === "length" || l === $OWNER) continue;
2188
+ const o = Object.getOwnPropertyDescriptor(r, l);
2189
+ if (typeof l === "symbol" && !o.enumerable) continue;
2190
+ if (o.get || o.set) {
2191
+ Object.defineProperty(i, l, o);
2119
2192
  continue;
2120
2193
  }
2121
- const f = l.value;
2194
+ const f = o.value;
2122
2195
  const s = f !== null && typeof f === "object" ? snapshotWalk(f, t, n) : f;
2123
- if (l.enumerable && l.writable && l.configurable) i[o] = s; else Object.defineProperty(i, o, {
2124
- ...l,
2196
+ if (o.enumerable && o.writable && o.configurable) i[l] = s; else Object.defineProperty(i, l, {
2197
+ ...o,
2125
2198
  value: s
2126
2199
  });
2127
2200
  }
@@ -2131,22 +2204,22 @@ function snapshotWalk(e, t, n) {
2131
2204
  // UNOWNED (shared/user) subtrees keep identity unless a descendant
2132
2205
  // substituted; copy-on-substitution preserves the documented CoW contract.
2133
2206
  t.set(r, r);
2134
- let l = null;
2207
+ let o = null;
2135
2208
  for (const e of Reflect.ownKeys(r)) {
2136
2209
  const i = Object.getOwnPropertyDescriptor(r, e);
2137
2210
  if (!i || i.get || i.set) continue;
2138
- const o = i.value;
2139
- if (o === null || typeof o !== "object") continue;
2140
- const f = snapshotWalk(o, t, n);
2141
- if (f !== o) {
2142
- if (l === null) {
2143
- l = Array.isArray(r) ? [ ...r ] : Object.create(Object.getPrototypeOf(r), Object.getOwnPropertyDescriptors(r));
2144
- t.set(r, l);
2211
+ const l = i.value;
2212
+ if (l === null || typeof l !== "object") continue;
2213
+ const f = snapshotWalk(l, t, n);
2214
+ if (f !== l) {
2215
+ if (o === null) {
2216
+ o = Array.isArray(r) ? [ ...r ] : Object.create(Object.getPrototypeOf(r), Object.getOwnPropertyDescriptors(r));
2217
+ t.set(r, o);
2145
2218
  }
2146
- l[e] = f;
2219
+ o[e] = f;
2147
2220
  }
2148
2221
  }
2149
- return l ?? r;
2222
+ return o ?? r;
2150
2223
  }
2151
2224
 
2152
- export { adoptPB, arrayStructureChanged, authoritativeRead, authoritativeServe, bumpDeep, createStoreNext, deepNext, getHasNode, getKeySetNode, getNode, hasAccessorFlag, hasActiveOverride, heldMaskView, materializePB, membershipChanged, notifyFold, notifyFoldTail, notifyKeyDiff, notifyKeyValue, runAuthoritative, snapshotNext, stagedTruthPB, storeHasFamily, storeHasOptimisticFamily, storeIsShallow, storeSetterNext, targetsEqual, unwrapValue, wrapNext };
2225
+ export { adoptPB, arrayStructureChanged, authoritativeRead, authoritativeServe, bumpDeep, createStoreNext, deepNext, getHasNode, getKeySetNode, getNode, hasAccessorFlag, hasActiveOverride, heldMaskView, materializePB, membershipChanged, notifyFold, notifyFoldTail, notifyKeyDiff, notifyKeyValue, runAuthoritative, snapshotNext, stagedTruthPB, storeHasFamily, storeHasOptimisticFamily, storeIsShallow, storeSetterNext, targetsEqual, unwrapValue, visibleOverride, wrapNext };