@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,6 +1,6 @@
1
1
  import { CONFIG_AUTO_DISPOSE, STATUS_PENDING, STATUS_UNINITIALIZED, NOT_PENDING, CONFIG_OPTIMISTIC, CONFIG_HELD_TRUTH, unwrapOverride } from "../../core/constants.js";
2
2
 
3
- import { computed, setSignal, isEqual } from "../../core/core.js";
3
+ import { computed, hasActiveOverride, setSignal, isEqual, visibleOverride } from "../../core/core.js";
4
4
 
5
5
  import { getOwner } from "../../core/owner.js";
6
6
 
@@ -18,7 +18,7 @@ import { $TARGET, markRawIngest, setNextOptimisticViewResolver, isWrappable, raw
18
18
 
19
19
  import { runProjectionComputedNext } from "./projection.js";
20
20
 
21
- import { wrapNext, runAuthoritative, storeSetterNext, hasActiveOverride, unwrapValue, targetsEqual, heldMaskView, getNode, getHasNode, getKeySetNode, bumpDeep, stagedTruthPB, authoritativeRead } from "./store.js";
21
+ import { wrapNext, runAuthoritative, storeSetterNext, unwrapValue, targetsEqual, heldMaskView, getNode, getHasNode, getKeySetNode, bumpDeep, stagedTruthPB, authoritativeRead } from "./store.js";
22
22
 
23
23
  import { sameKey } from "./reconcile.js";
24
24
 
@@ -55,7 +55,7 @@ import { setOptHooks, $OWNER, lookupTarget } from "./target.js";
55
55
  * staged values to converge (normal speculation). Resolves merges first:
56
56
  * merge unions optimistic nodes/stores into the target. */ function transitionHoldsOptimism(e) {
57
57
  const t = currentTransition(e);
58
- return t.ft !== true && (t.it.length !== 0 || t.En.size !== 0);
58
+ return t.Tt !== true && (t.rt.length !== 0 || t.dn.size !== 0);
59
59
  }
60
60
 
61
61
  let blockedInstalled = false;
@@ -72,12 +72,15 @@ function installNextBlockedHalf() {
72
72
  applyTentative: applyTentative,
73
73
  retainsOptimism: transitionHoldsOptimism
74
74
  });
75
- setNextOptimisticViewResolver((e, t) => optimisticView(e, t));
75
+ // The affects() declaration walk is a WRITER channel (A28 (5)): tagging a
76
+ // parent covers the record as the writer sees it, this tick's optimistic
77
+ // writes included — the draft view, not the reader view.
78
+ setNextOptimisticViewResolver((e, t) => optimisticView(e, t, true));
76
79
  // Scheduler flush tails call _clearOptimisticStores whenever tracked
77
80
  // stores exist; next has no layer to clear — reverts are engine-native —
78
81
  // so the hook only empties the batch set.
79
- if (!GlobalQueue.ni) {
80
- GlobalQueue.ni = e => {
82
+ if (!GlobalQueue.Cn) {
83
+ GlobalQueue.Cn = e => {
81
84
  for (const t of e) {
82
85
  const e = t?.[$TARGET];
83
86
  const n = e?.fam?.overlaid;
@@ -96,24 +99,24 @@ function installNextBlockedHalf() {
96
99
  e.clear();
97
100
  };
98
101
  }
99
- const e = GlobalQueue.cn;
100
- GlobalQueue.cn = t => {
101
- for (const e of t.En) {
102
+ const e = GlobalQueue.Nn;
103
+ GlobalQueue.Nn = t => {
104
+ for (const e of t.dn) {
102
105
  const n = e?.[$TARGET];
103
106
  const i = n?.fam;
104
- const o = i?.node;
107
+ const r = i?.node;
105
108
  // The hold exists to keep optimistic state alive until the store's own
106
109
  // truth lands (#2951). Once the family carries NO live overrides (a
107
110
  // landing consumed them, or they never existed), a pending firewall is
108
111
  // no reason to park the transaction — blocking then leaks it forever
109
112
  // when the in-flight question is never answered (undisposed fixtures).
110
- if (o == null || !(o.S & STATUS_PENDING)) continue;
113
+ if (r == null || !(r.S & STATUS_PENDING)) continue;
111
114
  // Ownership is declared (#3146): only the flight's OWN transaction
112
115
  // parks on the flight (the #2951 anchor routed the bare write there).
113
116
  // A transaction that merely brushed the store never waits for truth
114
117
  // it does not carry.
115
- const r = i.ft != null ? liveTransition(i.ft) : null;
116
- if (r !== null && r !== currentTransition(t)) continue;
118
+ const o = i.ft != null ? liveTransition(i.ft) : null;
119
+ if (o !== null && o !== currentTransition(t)) continue;
117
120
  if (familyHasLiveOverrides(i)) return true;
118
121
  }
119
122
  return e(t);
@@ -128,10 +131,10 @@ function familyHasLiveOverrides(e) {
128
131
  if (t === null) continue;
129
132
  for (const e of Reflect.ownKeys(t)) {
130
133
  const n = t[e];
131
- if (n.o?.be !== undefined && n.o?.be !== NOT_PENDING) return true;
134
+ if (n.o?.Ce !== undefined && n.o?.Ce !== NOT_PENDING) return true;
132
135
  }
133
136
  }
134
- if (e.k !== null && e.k.o?.be !== undefined && e.k.o?.be !== NOT_PENDING) return true;
137
+ if (e.k !== null && e.k.o?.Ce !== undefined && e.k.o?.Ce !== NOT_PENDING) return true;
135
138
  }
136
139
  t.clear();
137
140
  // nothing live — drop the bookkeeping
@@ -144,23 +147,23 @@ function createOptimisticStoreNext(e, t, n) {
144
147
  installOptimisticEngine();
145
148
  installNextBlockedHalf();
146
149
  const i = typeof e === "function";
147
- const o = i ? n : t;
148
- const r = i ? t : e;
150
+ const r = i ? n : t;
151
+ const o = i ? t : e;
149
152
  const s = {
150
153
  map: new WeakMap,
151
154
  node: null,
152
- shallow: !!o?.shallow,
155
+ shallow: !!r?.shallow,
153
156
  opt: true
154
157
  };
155
- const l = wrapNext(r, null, null, s);
158
+ const l = wrapNext(o, null, null, s);
156
159
  s.px = l;
157
160
  // Same key resolution the projection channels use ("id" default) — replay's
158
161
  // satisfaction rule reads it off the family.
159
- const u = o?.key === undefined ? "id" : o.key;
162
+ const u = r?.key === undefined ? "id" : r.key;
160
163
  s.key = typeof u === "function" ? u : u === null ? null : e => isWrappable(e) ? e[u] : undefined;
161
164
  if (s.shallow) {
162
165
  l[$TARGET].s = true;
163
- markRawIngest(r);
166
+ markRawIngest(o);
164
167
  }
165
168
  if (i) {
166
169
  const t = e;
@@ -178,7 +181,7 @@ function createOptimisticStoreNext(e, t, n) {
178
181
  if (e == null) return;
179
182
  let t = liveTransition(e);
180
183
  if (t === null) s.ft = t = createTransition();
181
- s.node.ge = t;
184
+ s.node.Ge = t;
182
185
  globalQueue.initTransition(t);
183
186
  };
184
187
  // Landing router (#3164 fold ruling): while a transaction retains
@@ -223,8 +226,8 @@ function createOptimisticStoreNext(e, t, n) {
223
226
  // (#2933: the loading rail is transaction-invisible); a sync run clears
224
227
  // the declaration.
225
228
  const declareFlight = e => {
226
- if (e.o?.Pe == null) {
227
- if (!e.Ae) s.ft = null;
229
+ if (e.o?.Re == null) {
230
+ if (!e.ge) s.ft = null;
228
231
  return;
229
232
  }
230
233
  // First flight (#3146 carve-out): nothing has ever committed, so there
@@ -236,23 +239,23 @@ function createOptimisticStoreNext(e, t, n) {
236
239
  // fallback never showed and the whole page stayed blank. The loading
237
240
  // window (#2933) already declares nothing for the same reason; once
238
241
  // the first truth lands, every refetch flight declares as before.
239
- if (e.Ae || e.S & STATUS_UNINITIALIZED) return;
242
+ if (e.ge || e.S & STATUS_UNINITIALIZED) return;
240
243
  let t = activeTransition;
241
244
  if (t === null) globalQueue.initTransition(t = createTransition());
242
245
  s.ft = t;
243
- e.ge = t;
244
- let n = t.Re.get(e);
245
- if (n === undefined) t.Re.set(e, n = new Set);
246
+ e.Ge = t;
247
+ let n = t.oe.get(e);
248
+ if (n === undefined) t.oe.set(e, n = new Set);
246
249
  n.add(e);
247
250
  };
248
251
  let n;
249
- if (o?.seedLoadingValue) n = {
252
+ if (r?.seedLoadingValue) n = {
250
253
  loadingValue: undefined
251
254
  };
252
255
  const i = computed(() => {
253
256
  const e = getOwner();
254
257
  try {
255
- runAuthoritative(() => runProjectionComputedNext(l, t, o?.key === undefined ? "id" : o.key, wrapCommit, aroundDraftWrite));
258
+ runAuthoritative(() => runProjectionComputedNext(l, t, r?.key === undefined ? "id" : r.key, wrapCommit, aroundDraftWrite));
256
259
  } finally {
257
260
  declareFlight(e);
258
261
  }
@@ -274,8 +277,8 @@ function createOptimisticStoreNext(e, t, n) {
274
277
 
275
278
  /** Resolve a retained transition through its merge chain (`_done` holds the
276
279
  * merge target while merged, `true` once settled). Null = dead. */ function liveTransition(e) {
277
- while (typeof e.ft === "object") e = e.ft;
278
- return e.ft === true ? null : e;
280
+ while (typeof e.Tt === "object") e = e.Tt;
281
+ return e.Tt === true ? null : e;
279
282
  }
280
283
 
281
284
  /** The transaction truth landings fold into: the first live member of the
@@ -320,10 +323,10 @@ function createOptimisticStoreNext(e, t, n) {
320
323
  * stay unarmed — the override is their display and its revert their
321
324
  * notification, A17). */ function runFolded(e, t) {
322
325
  runAsTransitionBatch(e, t);
323
- const n = e.$t;
326
+ const n = e.Ot;
324
327
  for (let t = 0; t < n.length; t++) {
325
328
  const i = n[t];
326
- i.ge = e;
329
+ i.Ge = e;
327
330
  if (i.T & CONFIG_OPTIMISTIC && !hasActiveOverride(i)) i.T |= CONFIG_HELD_TRUTH;
328
331
  }
329
332
  }
@@ -340,15 +343,15 @@ function createOptimisticStoreNext(e, t, n) {
340
343
  if (n !== null) {
341
344
  // Occurrence-aware key queues (parity with the adoption window):
342
345
  // duplicate keys match per occurrence, each current row consumed once.
343
- let o = null;
344
- const r = e.length;
345
- for (let t = 0; t < r; t++) {
346
+ let r = null;
347
+ const o = e.length;
348
+ for (let t = 0; t < o; t++) {
346
349
  const i = unwrapValue(e[t]);
347
350
  if (!isWrappable(i)) continue;
348
- const r = n(i);
349
- if (r === undefined) continue;
350
- const s = (o ??= new Map).get(r);
351
- if (s === undefined) o.set(r, [ i ]); else s.push(i);
351
+ const o = n(i);
352
+ if (o === undefined) continue;
353
+ const s = (r ??= new Map).get(o);
354
+ if (s === undefined) r.set(o, [ i ]); else s.push(i);
352
355
  }
353
356
  // Echo adoption: an optimistic structural add whose key the landing
354
357
  // confirms must keep its raw (and so its proxy — list drivers keep the
@@ -363,42 +366,42 @@ function createOptimisticStoreNext(e, t, n) {
363
366
  for (const e of Reflect.ownKeys(s)) {
364
367
  const t = s[e];
365
368
  if (!hasActiveOverride(t)) continue;
366
- const i = unwrapValue(unwrapOverride(t.o.be));
369
+ const i = unwrapValue(unwrapOverride(t.o.Ce));
367
370
  if (!isWrappable(i)) continue;
368
- const r = n(i);
369
- if (r === undefined) continue;
370
- const l = (o ??= new Map).get(r);
371
- if (l === undefined) o.set(r, [ i ]); else l.push(i);
371
+ const o = n(i);
372
+ if (o === undefined) continue;
373
+ const l = (r ??= new Map).get(o);
374
+ if (l === undefined) r.set(o, [ i ]); else l.push(i);
372
375
  }
373
376
  }
374
- for (let r = 0; r < i; r++) {
375
- const i = t[r];
377
+ for (let o = 0; o < i; o++) {
378
+ const i = t[o];
376
379
  let s;
377
- if (isWrappable(i) && o !== null) {
380
+ if (isWrappable(i) && r !== null) {
378
381
  const e = n(i);
379
382
  if (e !== undefined) {
380
- for (const [t, n] of o) {
383
+ for (const [t, n] of r) {
381
384
  if (!sameKey(t, e)) continue;
382
385
  s = n.shift();
383
- if (n.length === 0) o.delete(t);
386
+ if (n.length === 0) r.delete(t);
384
387
  break;
385
388
  }
386
389
  }
387
390
  }
388
391
  if (s !== undefined) {
389
- if (unwrapValue(e[r]) !== s) e[r] = s;
390
- stagedApply(e[r], i, n);
392
+ if (unwrapValue(e[o]) !== s) e[o] = s;
393
+ stagedApply(e[o], i, n);
391
394
  } else {
392
- const t = unwrapValue(e[r]);
393
- if (!isEqual(t, i) && !targetsEqual(t, i)) e[r] = i;
395
+ const t = unwrapValue(e[o]);
396
+ if (!isEqual(t, i) && !targetsEqual(t, i)) e[o] = i;
394
397
  }
395
398
  }
396
399
  } else {
397
- for (let o = 0; o < i; o++) {
398
- const i = t[o];
399
- const r = unwrapValue(e[o]);
400
- if (r === i) continue;
401
- if (isWrappable(i) && isWrappable(r) && Array.isArray(i) === Array.isArray(r)) stagedApply(e[o], i, n); else if (!isEqual(r, i) && !targetsEqual(r, i)) e[o] = i;
400
+ for (let r = 0; r < i; r++) {
401
+ const i = t[r];
402
+ const o = unwrapValue(e[r]);
403
+ if (o === i) continue;
404
+ if (isWrappable(i) && isWrappable(o) && Array.isArray(i) === Array.isArray(o)) stagedApply(e[r], i, n); else if (!isEqual(o, i) && !targetsEqual(o, i)) e[r] = i;
402
405
  }
403
406
  }
404
407
  if (e.length !== i) e.length = i;
@@ -406,25 +409,25 @@ function createOptimisticStoreNext(e, t, n) {
406
409
  }
407
410
  // Object merge; also the degenerate root-kind-change shape (arrays accept
408
411
  // keyed writes/deletes, so a wholesale restatement still lands staged).
409
- for (const o of Reflect.ownKeys(t)) {
410
- if (i && o === "length" || o === $OWNER) continue;
411
- const r = t[o];
412
- const s = unwrapValue(e[o]);
413
- if (s === r) continue;
414
- if (isWrappable(r) && isWrappable(s) && Array.isArray(r) === Array.isArray(s)) {
412
+ for (const r of Reflect.ownKeys(t)) {
413
+ if (i && r === "length" || r === $OWNER) continue;
414
+ const o = t[r];
415
+ const s = unwrapValue(e[r]);
416
+ if (s === o) continue;
417
+ if (isWrappable(o) && isWrappable(s) && Array.isArray(o) === Array.isArray(s)) {
415
418
  // Different-keyed entities never merge (tentative-channel parity):
416
419
  // the incoming object replaces the slot wholesale.
417
420
  if (n !== null) {
418
421
  const t = n(s);
419
- const i = n(r);
422
+ const i = n(o);
420
423
  if (t !== undefined && i !== undefined && !sameKey(t, i)) {
421
- e[o] = r;
424
+ e[r] = o;
422
425
  continue;
423
426
  }
424
427
  }
425
- stagedApply(e[o], r, n);
426
- } else if (!isEqual(s, r) && !targetsEqual(s, r)) {
427
- e[o] = r;
428
+ stagedApply(e[r], o, n);
429
+ } else if (!isEqual(s, o) && !targetsEqual(s, o)) {
430
+ e[r] = o;
428
431
  }
429
432
  }
430
433
  for (const n of Reflect.ownKeys(e)) {
@@ -468,46 +471,46 @@ function createOptimisticStoreNext(e, t, n) {
468
471
  // churned for the life of the action and snapped back at settle (#3323).
469
472
  const visible = (t, n) => {
470
473
  const i = e.n?.[t];
471
- return unwrapValue(i !== undefined && hasActiveOverride(i) ? unwrapOverride(i.o?.be) : n);
474
+ return unwrapValue(i !== undefined && hasActiveOverride(i) ? unwrapOverride(i.o?.Ce) : n);
472
475
  };
473
476
  const visiblePresent = t => {
474
477
  const n = e.h?.[t];
475
- return n !== undefined && hasActiveOverride(n) ? !!unwrapOverride(n.o?.be) : t in i;
478
+ return n !== undefined && hasActiveOverride(n) ? !!unwrapOverride(n.o?.Ce) : t in i;
476
479
  };
477
- let o = false;
478
- const r = Array.isArray(t);
480
+ let r = false;
481
+ const o = Array.isArray(t);
479
482
  for (const n of Reflect.ownKeys(t)) {
480
- if (r && n === "length" || n === $OWNER) continue;
483
+ if (o && n === "length" || n === $OWNER) continue;
481
484
  const s = unwrapValue(t[n]);
482
485
  if (!visiblePresent(n)) {
483
486
  // Optimistic add: value node + presence node + membership bump.
484
487
  setSignal(getNode(e, n, i[n]), () => s);
485
488
  setSignal(getHasNode(e, n, n in i), true);
486
- o = true;
489
+ r = true;
487
490
  } else {
488
491
  const t = visible(n, i[n]);
489
492
  if (!isEqual(t, s) && !targetsEqual(t, s)) {
490
493
  setSignal(getNode(e, n, t), () => s);
491
- if (r) o = true;
494
+ if (o) r = true;
492
495
  }
493
496
  }
494
497
  }
495
498
  for (const n of Reflect.ownKeys(i)) {
496
- if (r && n === "length" || n === $OWNER) continue;
499
+ if (o && n === "length" || n === $OWNER) continue;
497
500
  if (n in t || !visiblePresent(n)) continue;
498
501
  // Optimistic delete: node reads undefined, presence flips, membership bumps.
499
502
  setSignal(getNode(e, n, i[n]), () => undefined);
500
503
  setSignal(getHasNode(e, n, true), false);
501
- o = true;
504
+ r = true;
502
505
  }
503
- if (r) {
506
+ if (o) {
504
507
  const n = visible("length", i.length);
505
508
  if (n !== t.length) {
506
509
  setSignal(getNode(e, "length", n), () => t.length);
507
- o = true;
510
+ r = true;
508
511
  }
509
512
  }
510
- if (o) setSignal(getKeySetNode(e), e => e + 1);
513
+ if (r) setSignal(getKeySetNode(e), e => e + 1);
511
514
  // Deep-witness: optimistic value writes notify deep() subscribers too
512
515
  // (structural ones already ride the key-set bump above).
513
516
  bumpDeep(e);
@@ -518,7 +521,7 @@ function createOptimisticStoreNext(e, t, n) {
518
521
  e.pb = stagedTruthPB.get(e) ?? null;
519
522
  if (e.pb !== null) stagedTruthPB.delete(e);
520
523
  (e.fam.overlaid ??= new Set).add(e);
521
- GlobalQueue.Nn?.(e.fam.px ?? e.px);
524
+ GlobalQueue.Dn?.(e.fam.px ?? e.px);
522
525
  }
523
526
 
524
527
  /** Optimistic-view composition for snapshot/deep (O1: snapshot is the CURRENT
@@ -526,18 +529,20 @@ function createOptimisticStoreNext(e, t, n) {
526
529
  * RUL-12). Returns `src` untouched when no override is active on `t`.
527
530
  * Authoritative-view reads (until()'s predicate) skip composition entirely:
528
531
  * the predicate observes authoritative truth, never the caller's tentative
529
- * overlay. (Write-side emission callers never run under such a compute.) */ function optimisticView(e, t) {
532
+ * overlay. (Write-side emission callers never run under such a compute.) */ function optimisticView(e, t, n = false) {
530
533
  if (e.fam?.opt !== true || authoritativeRead()) return t;
531
- let n = null;
532
- const ensure = () => n ??= Array.isArray(t) ? [ ...t ] : {
534
+ let i = null;
535
+ const ensure = () => i ??= Array.isArray(t) ? [ ...t ] : {
533
536
  ...t
534
537
  };
535
- const i = e.n;
536
- if (i !== null) {
537
- for (const e of Reflect.ownKeys(i)) {
538
- const n = i[e];
539
- if (!hasActiveOverride(n)) continue;
540
- const o = unwrapOverride(n.o?.be);
538
+ const r = e.n;
539
+ if (r !== null) {
540
+ for (const e of Reflect.ownKeys(r)) {
541
+ const i = r[e];
542
+ // A28 (5): readers see an optimistic write once a flush carried it;
543
+ // the draft (writer channel) composes on it now.
544
+ if (!(n ? hasActiveOverride(i) : visibleOverride(i))) continue;
545
+ const o = unwrapOverride(i.o?.Ce);
541
546
  if (e === "length" && Array.isArray(t)) {
542
547
  if (t.length !== o) ensure().length = o;
543
548
  } else if (!isEqual(t[e], o)) ensure()[e] = o;
@@ -546,21 +551,23 @@ function createOptimisticStoreNext(e, t, n) {
546
551
  const o = e.h;
547
552
  if (o !== null) {
548
553
  for (const e of Reflect.ownKeys(o)) {
549
- const i = o[e];
550
- if (!hasActiveOverride(i)) continue;
551
- const r = !!unwrapOverride(i.o?.be);
552
- if (!r && e in (n ?? t)) delete ensure()[e];
554
+ const r = o[e];
555
+ // A28 (5): readers see an optimistic write once a flush carried it;
556
+ // the draft (writer channel) composes on it now.
557
+ if (!(n ? hasActiveOverride(r) : visibleOverride(r))) continue;
558
+ const s = !!unwrapOverride(r.o?.Ce);
559
+ if (!s && e in (i ?? t)) delete ensure()[e];
553
560
  }
554
561
  }
555
- return n ?? t;
562
+ return i ?? t;
556
563
  }
557
564
 
558
565
  function applyTentative(e, t, n) {
559
566
  const i = e.pb ?? e.v;
560
- const o = optimisticView(e, i);
561
- const r = e.fam;
567
+ const r = optimisticView(e, i);
568
+ const o = e.fam;
562
569
  const s = Array.isArray(t);
563
- if (Array.isArray(o) !== s) return;
570
+ if (Array.isArray(r) !== s) return;
564
571
  // kind change at root: flat overrides below
565
572
  const l = [];
566
573
  let u;
@@ -574,22 +581,22 @@ function applyTentative(e, t, n) {
574
581
  if (Array.isArray(e) !== Array.isArray(t)) return null;
575
582
  if (n) {
576
583
  const i = n(e);
577
- const o = n(t);
584
+ const r = n(t);
578
585
  // SameValueZero (re-audit 3, P1-3): parity with the plain reconcile
579
586
  // channel — NaN keys are self-equal.
580
- if (i !== undefined && o !== undefined && !sameKey(i, o)) return null;
587
+ if (i !== undefined && r !== undefined && !sameKey(i, r)) return null;
581
588
  }
582
- return lookupTarget(unwrapValue(e), r) ?? null;
589
+ return lookupTarget(unwrapValue(e), o) ?? null;
583
590
  };
584
591
  if (s) {
585
- const e = o;
592
+ const e = r;
586
593
  let i = null;
587
- for (let o = 0; o < t.length; o++) {
588
- const r = t[o];
589
- if (!isWrappable(r)) continue;
594
+ for (let r = 0; r < t.length; r++) {
595
+ const o = t[r];
596
+ if (!isWrappable(o)) continue;
590
597
  let s;
591
598
  if (n) {
592
- const t = n(r);
599
+ const t = n(o);
593
600
  if (t !== undefined) {
594
601
  if (i === null) {
595
602
  // Occurrence-aware index queues (re-audit 3, P1-3): parity with
@@ -597,41 +604,41 @@ function applyTentative(e, t, n) {
597
604
  // occurrence, each view row consumed once.
598
605
  i = new Map;
599
606
  for (let t = 0; t < e.length; t++) {
600
- const o = unwrapValue(e[t]);
601
- if (isWrappable(o)) {
602
- const e = n(o);
607
+ const r = unwrapValue(e[t]);
608
+ if (isWrappable(r)) {
609
+ const e = n(r);
603
610
  if (e === undefined) continue;
604
- const r = i.get(e);
605
- if (r === undefined) i.set(e, t); else if (Array.isArray(r)) r.push(t); else i.set(e, [ r, t ]);
611
+ const o = i.get(e);
612
+ if (o === undefined) i.set(e, t); else if (Array.isArray(o)) o.push(t); else i.set(e, [ o, t ]);
606
613
  }
607
614
  }
608
615
  }
609
- const o = i.get(t);
610
- if (o === undefined) s = undefined; else if (Array.isArray(o)) {
611
- s = unwrapValue(e[o.shift()]);
612
- if (o.length === 1) i.set(t, o[0]);
616
+ const r = i.get(t);
617
+ if (r === undefined) s = undefined; else if (Array.isArray(r)) {
618
+ s = unwrapValue(e[r.shift()]);
619
+ if (r.length === 1) i.set(t, r[0]);
613
620
  } else {
614
- s = unwrapValue(e[o]);
621
+ s = unwrapValue(e[r]);
615
622
  i.delete(t);
616
623
  }
617
- } else s = unwrapValue(e[o]);
618
- } else s = unwrapValue(e[o]);
619
- const a = match(s, r);
624
+ } else s = unwrapValue(e[r]);
625
+ } else s = unwrapValue(e[r]);
626
+ const a = match(s, o);
620
627
  if (a !== null) {
621
628
  // Keep the existing row in the slot (identity preserved); recurse.
622
- u[o] = unwrapValue(s);
623
- l.push([ a, r ]);
629
+ u[r] = unwrapValue(s);
630
+ l.push([ a, o ]);
624
631
  }
625
632
  }
626
633
  } else {
627
634
  for (const e of Reflect.ownKeys(t)) {
628
635
  if (e === $OWNER) continue;
629
- const n = unwrapValue(o[e]);
636
+ const n = unwrapValue(r[e]);
630
637
  const i = t[e];
631
- const r = match(n, i);
632
- if (r !== null) {
638
+ const o = match(n, i);
639
+ if (o !== null) {
633
640
  u[e] = n;
634
- l.push([ r, i ]);
641
+ l.push([ o, i ]);
635
642
  }
636
643
  }
637
644
  }
@@ -55,22 +55,23 @@ import { storeSetterNext, wrapNext } from "./store.js";
55
55
  * Save/restore projectionWriteActive, never hard-reset: the draft can be
56
56
  * driven from inside an enclosing authoritative-write scope (next-store
57
57
  * optimistic derives), and a hard `false` would clobber it mid-derive.
58
- */ function wrapDraft(e, t, r) {
58
+ */ function wrapDraft(e, t, r, i) {
59
59
  const write = e => r ? r(e) : e();
60
- const i = {
61
- get(i, o) {
62
- let n;
63
- const c = projectionWriteActive;
60
+ const o = {
61
+ get(o, n) {
62
+ let c;
63
+ const s = projectionWriteActive;
64
64
  setWriteOverride(true);
65
65
  setProjectionWriteActive(true);
66
66
  try {
67
- n = e[o];
67
+ c = e[n];
68
68
  } finally {
69
69
  setWriteOverride(false);
70
- setProjectionWriteActive(c);
70
+ setProjectionWriteActive(s);
71
71
  }
72
- if (o === $TARGET) return n;
73
- return typeof n === "object" && n !== null ? wrapDraft(n, t, r) : n;
72
+ // A shallow store's leaves are raw by contract (#3498): no draft proxy
73
+ // over them, so identity holds and a frozen leaf is never trapped.
74
+ return !i && typeof c === "object" && c !== null && n !== $TARGET ? wrapDraft(c, t, r) : c;
74
75
  },
75
76
  has(t, r) {
76
77
  let i;
@@ -160,7 +161,7 @@ import { storeSetterNext, wrapNext } from "./store.js";
160
161
  }
161
162
  };
162
163
  // Matching-kind dummy so Array.isArray(draft) answers like the store.
163
- return new Proxy(Array.isArray(e) ? [] : {}, i);
164
+ return new Proxy(Array.isArray(e) ? [] : {}, o);
164
165
  }
165
166
 
166
167
  function createProjectionNextInternal(e, t, r) {
@@ -207,30 +208,40 @@ function createProjectionNext(e, t, r) {
207
208
  } ];
208
209
  }
209
210
 
211
+ // A detached copy of projection state: the root container alone for a shallow
212
+ // store — its leaves are raw references by contract and stay so (#3498) — the
213
+ // whole tree otherwise.
214
+ function cloneState(e, t) {
215
+ return t ? Array.isArray(e) ? e.slice() : {
216
+ ...e
217
+ } : JSON.parse(JSON.stringify(e));
218
+ }
219
+
210
220
  function runProjectionComputedNext(e, t, r, i, o) {
211
221
  const n = getOwner();
212
- let c = false;
213
- let s;
222
+ const c = e[$TARGET];
223
+ let s = false;
224
+ let u;
214
225
  // Open loading window (seedLoadingValue): the observable store IS commit #0
215
226
  // for the whole first flight — the derive works a detached shadow of the
216
227
  // seed so draft writes cannot tear through to readers (#2988). Every commit
217
228
  // point reconciles the shadow through the normal commit path.
218
- const u = n.Ae ? JSON.parse(JSON.stringify(e[$TARGET][STORE_VALUE])) : null;
219
- const l = wrapDraft(e, () => !c || n.o?.Pe === s, o);
220
- storeSetterNext(l, o => {
221
- s = t(u ?? o);
222
- c = true;
229
+ const l = n.ge ? cloneState(c[STORE_VALUE], c.s) : null;
230
+ const a = wrapDraft(e, () => !s || n.o?.Re === u, o, c.s);
231
+ storeSetterNext(a, o => {
232
+ u = t(l ?? o);
233
+ s = true;
223
234
  const commit = t => {
224
235
  // Shadow run: commit a detached snapshot, never the shadow itself
225
236
  // (adoption takes the value by identity — handing it the live shadow
226
237
  // would fuse the draft to the observable store).
227
- if (u && (t === undefined || t === u)) t = JSON.parse(JSON.stringify(u));
238
+ if (l && (t === undefined || t === l)) t = cloneState(l, c.s);
228
239
  if (t === o || t === undefined) return;
229
240
  const write = () => storeSetterNext(e, e => reconcileNextState(t, e, r, true), false);
230
241
  i ? i(write, t) : write();
231
242
  };
232
- const l = handleAsync(n, s, commit);
233
- if (!n.Ae) commit(l);
243
+ const a = handleAsync(n, u, commit);
244
+ if (!n.ge) commit(a);
234
245
  }, false);
235
246
  return n;
236
247
  }