@solidjs/signals 2.0.0-rc.7 → 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 (134) hide show
  1. package/dist/dev-shared.js +6357 -0
  2. package/dist/dev.attribution.js +2485 -0
  3. package/dist/dev.js +2851 -7027
  4. package/dist/observe/affects.js +129 -0
  5. package/dist/observe/attribution.js +7 -0
  6. package/dist/observe/boundaries.js +641 -0
  7. package/dist/observe/core/action.js +177 -0
  8. package/dist/observe/core/async.js +846 -0
  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 +82 -0
  12. package/dist/observe/core/attribution-queries.js +28 -0
  13. package/dist/observe/core/attribution.js +2095 -0
  14. package/dist/observe/core/constants.js +248 -0
  15. package/dist/{types-cjs/core/context.d.cts → observe/core/context.js} +37 -12
  16. package/dist/observe/core/core.js +1789 -0
  17. package/dist/observe/core/dev.js +293 -0
  18. package/dist/observe/core/effect.js +195 -0
  19. package/dist/observe/core/error-hooks.js +71 -0
  20. package/dist/observe/core/error.js +80 -0
  21. package/dist/observe/core/external.js +98 -0
  22. package/dist/observe/core/graph.js +155 -0
  23. package/dist/observe/core/heap.js +147 -0
  24. package/dist/observe/core/invariants.js +45 -0
  25. package/dist/observe/core/lanes.js +205 -0
  26. package/dist/observe/core/optimistic.js +585 -0
  27. package/dist/observe/core/owner.js +322 -0
  28. package/dist/observe/core/scheduler.js +1428 -0
  29. package/dist/observe/core/verdict.js +669 -0
  30. package/dist/observe/index.js +59 -0
  31. package/dist/observe/map.js +484 -0
  32. package/dist/{types-cjs/signals.d.cts → observe/signals.js} +311 -317
  33. package/dist/observe/store/index.js +40 -0
  34. package/dist/observe/store/next/optimistic.js +660 -0
  35. package/dist/observe/store/next/projection.js +255 -0
  36. package/dist/observe/store/next/reconcile.js +375 -0
  37. package/dist/observe/store/next/store.js +2327 -0
  38. package/dist/observe/store/next/target.js +51 -0
  39. package/dist/observe/store/store.js +325 -0
  40. package/dist/observe/store/storePath.js +103 -0
  41. package/dist/observe/store/utils.js +1023 -0
  42. package/dist/prod/attribution.js +41 -0
  43. package/dist/prod/boundaries.js +128 -76
  44. package/dist/prod/core/action.js +65 -37
  45. package/dist/prod/core/async.js +324 -172
  46. package/dist/prod/core/constants.js +57 -1
  47. package/dist/prod/core/context.js +3 -3
  48. package/dist/prod/core/core.js +1003 -364
  49. package/dist/prod/core/dev.js +17 -1
  50. package/dist/prod/core/effect.js +76 -42
  51. package/dist/prod/core/error-hooks.js +71 -0
  52. package/dist/prod/core/external.js +4 -4
  53. package/dist/prod/core/graph.js +37 -37
  54. package/dist/prod/core/heap.js +61 -56
  55. package/dist/prod/core/lanes.js +107 -63
  56. package/dist/prod/core/optimistic.js +374 -93
  57. package/dist/prod/core/owner.js +62 -46
  58. package/dist/prod/core/scheduler.js +653 -286
  59. package/dist/prod/core/verdict.js +245 -127
  60. package/dist/prod/index.js +15 -5
  61. package/dist/prod/map.js +292 -141
  62. package/dist/prod/signals.js +29 -16
  63. package/dist/prod/store/next/optimistic.js +77 -55
  64. package/dist/prod/store/next/projection.js +31 -20
  65. package/dist/prod/store/next/reconcile.js +87 -84
  66. package/dist/prod/store/next/store.js +718 -401
  67. package/dist/prod/store/next/target.js +32 -10
  68. package/dist/prod/store/store.js +12 -14
  69. package/dist/prod/store/utils.js +950 -130
  70. package/dist/types/attribution.d.ts +20 -0
  71. package/dist/types/attribution.prod.d.ts +22 -0
  72. package/dist/types/boundaries.d.ts +10 -1
  73. package/dist/types/core/action.d.ts +12 -5
  74. package/dist/types/core/async.d.ts +1 -1
  75. package/dist/types/core/attribution-costs.d.ts +35 -0
  76. package/dist/types/core/attribution-feedback.d.ts +133 -0
  77. package/dist/types/core/attribution-hooks.d.ts +147 -9
  78. package/dist/types/core/attribution-queries.d.ts +10 -0
  79. package/dist/types/core/attribution.d.ts +216 -182
  80. package/dist/types/core/constants.d.ts +56 -0
  81. package/dist/types/core/core.d.ts +190 -2
  82. package/dist/types/core/dev.d.ts +246 -28
  83. package/dist/types/core/effect.d.ts +8 -1
  84. package/dist/types/core/error-hooks.d.ts +71 -0
  85. package/dist/types/core/index.d.ts +4 -1
  86. package/dist/types/core/invariants.d.ts +4 -0
  87. package/dist/types/core/lanes.d.ts +39 -5
  88. package/dist/types/core/scheduler.d.ts +153 -0
  89. package/dist/types/core/types.d.ts +32 -9
  90. package/dist/types/index.d.ts +10 -3
  91. package/dist/types/map.d.ts +2 -0
  92. package/dist/types/signals.d.ts +8 -0
  93. package/dist/types/store/index.d.ts +2 -0
  94. package/dist/types/store/next/optimistic.d.ts +1 -1
  95. package/dist/types/store/next/store.d.ts +11 -5
  96. package/dist/types/store/next/target.d.ts +37 -10
  97. package/dist/types/store/utils.d.ts +178 -0
  98. package/package.json +21 -17
  99. package/dist/node.cjs +0 -10541
  100. package/dist/node.dev.cjs +0 -13724
  101. package/dist/types-cjs/affects.d.cts +0 -47
  102. package/dist/types-cjs/boundaries.d.cts +0 -175
  103. package/dist/types-cjs/core/action.d.cts +0 -64
  104. package/dist/types-cjs/core/async.d.cts +0 -23
  105. package/dist/types-cjs/core/attribution-hooks.d.cts +0 -127
  106. package/dist/types-cjs/core/attribution.d.cts +0 -495
  107. package/dist/types-cjs/core/constants.d.cts +0 -159
  108. package/dist/types-cjs/core/core.d.cts +0 -185
  109. package/dist/types-cjs/core/dev.d.cts +0 -136
  110. package/dist/types-cjs/core/effect.d.cts +0 -30
  111. package/dist/types-cjs/core/error.d.cts +0 -56
  112. package/dist/types-cjs/core/external.d.cts +0 -15
  113. package/dist/types-cjs/core/graph.d.cts +0 -28
  114. package/dist/types-cjs/core/heap.d.cts +0 -24
  115. package/dist/types-cjs/core/index.d.cts +0 -13
  116. package/dist/types-cjs/core/invariants.d.cts +0 -59
  117. package/dist/types-cjs/core/lanes.d.cts +0 -54
  118. package/dist/types-cjs/core/optimistic.d.cts +0 -6
  119. package/dist/types-cjs/core/owner.d.cts +0 -124
  120. package/dist/types-cjs/core/scheduler.d.cts +0 -236
  121. package/dist/types-cjs/core/types.d.cts +0 -204
  122. package/dist/types-cjs/core/verdict.d.cts +0 -2
  123. package/dist/types-cjs/index.d.cts +0 -10
  124. package/dist/types-cjs/map.d.cts +0 -69
  125. package/dist/types-cjs/package.json +0 -3
  126. package/dist/types-cjs/store/index.d.cts +0 -18
  127. package/dist/types-cjs/store/next/optimistic.d.cts +0 -25
  128. package/dist/types-cjs/store/next/projection.d.cts +0 -8
  129. package/dist/types-cjs/store/next/reconcile.d.cts +0 -10
  130. package/dist/types-cjs/store/next/store.d.cts +0 -121
  131. package/dist/types-cjs/store/next/target.d.cts +0 -166
  132. package/dist/types-cjs/store/store.d.cts +0 -143
  133. package/dist/types-cjs/store/storePath.d.cts +0 -58
  134. package/dist/types-cjs/store/utils.d.cts +0 -74
@@ -4,7 +4,7 @@ import { computed, optimisticComputed, setSignal, optimisticSignal, runWithOwner
4
4
 
5
5
  import { cleanup, createRoot, getOwner, dispose, getObserver } from "./core/owner.js";
6
6
 
7
- import { globalQueue, Queue, entangleConfirmingTransitions, activeTransition } from "./core/scheduler.js";
7
+ import { globalQueue, dirtyQueue, Queue, entangleConfirmingTransitions, activeTransition } from "./core/scheduler.js";
8
8
 
9
9
  import { CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, EFFECT_USER, $REFRESH, CONFIG_DIRECT_COMMIT, CONFIG_AUTHORITATIVE_READ, CONFIG_FRESH_READ } from "./core/constants.js";
10
10
 
@@ -281,10 +281,7 @@ function createMemo(e, t) {
281
281
  n = r;
282
282
  dispose(t);
283
283
  }, e.error, {
284
- ...false ? {
285
- ...t,
286
- name: t?.name ?? "effect"
287
- } : t,
284
+ ...t,
288
285
  user: true,
289
286
  defer: true
290
287
  });
@@ -330,7 +327,7 @@ function createMemo(e, t) {
330
327
  // route through the inherited queue.
331
328
  const i = getOwner();
332
329
  const o = new MicrotaskQueue;
333
- o.Le = i.C;
330
+ o._parent = i.C;
334
331
  // notify() forwards up the normal chain
335
332
  i.C = o;
336
333
  // A user effect rather than a bare computed: computeds are pull-based and
@@ -352,7 +349,7 @@ function createMemo(e, t) {
352
349
  // stale mainline value and resolves with old data.
353
350
  {
354
351
  user: true,
355
- At: CONFIG_DIRECT_COMMIT
352
+ kt: CONFIG_DIRECT_COMMIT
356
353
  });
357
354
  });
358
355
  });
@@ -445,7 +442,7 @@ function createMemo(e, t) {
445
442
  if (i === null) {
446
443
  i = getOwner();
447
444
  const e = new MicrotaskQueue;
448
- e.Le = i.C;
445
+ e._parent = i.C;
449
446
  i.C = e;
450
447
  }
451
448
  return read(t);
@@ -457,7 +454,7 @@ function createMemo(e, t) {
457
454
  dispose(i);
458
455
  }, {
459
456
  user: true,
460
- At: CONFIG_DIRECT_COMMIT | CONFIG_AUTHORITATIVE_READ | CONFIG_FRESH_READ
457
+ kt: CONFIG_DIRECT_COMMIT | CONFIG_AUTHORITATIVE_READ | CONFIG_FRESH_READ
461
458
  });
462
459
  make();
463
460
  });
@@ -508,12 +505,20 @@ function createMemo(e, t) {
508
505
  *
509
506
  * Must be called *outside* a tracking scope.
510
507
  *
508
+ * Inside an action, call it from a step: after an `await`, put a bare `yield`
509
+ * before `yield until(...)`. The runtime cannot hook an async generator's
510
+ * `await` continuation, so the `until(...)` expression — which CREATES the
511
+ * predicate's reader — would otherwise run outside the transaction; created
512
+ * there it is born held (A29) and replays only at the commit its own promise
513
+ * holds open (#3482). See {@link action}.
514
+ *
511
515
  * @example
512
516
  * ```ts
513
517
  * const send = action(async function* (text: string) {
514
518
  * const clientId = crypto.randomUUID();
515
519
  * setMessages(m => { m.push({ clientId, text, pending: true }); }); // optimistic
516
520
  * await socket.send({ clientId, text }); // fire-and-forget transport
521
+ * yield; // re-enter the transaction after the await
517
522
  * // Hold until the live source echoes the write (authoritative view —
518
523
  * // the optimistic row above cannot satisfy this):
519
524
  * yield until(() => messages.some(m => m.clientId === clientId), { timeout: 10_000 });
@@ -540,10 +545,10 @@ function createMemo(e, t) {
540
545
  // microtask so the promise can settle while the transaction the caller
541
546
  // yielded it into is still open — that transaction being open is the
542
547
  // entire point of the hold.
543
- const s = getOwner();
544
- const u = new MicrotaskQueue;
545
- u.Le = s.C;
546
- s.C = u;
548
+ const u = getOwner();
549
+ const s = new MicrotaskQueue;
550
+ s._parent = u.C;
551
+ u.C = s;
547
552
  let f;
548
553
  let a;
549
554
  const settle = e => {
@@ -572,7 +577,7 @@ function createMemo(e, t) {
572
577
  // the hold itself is keeping uncommitted.
573
578
  {
574
579
  user: true,
575
- At: CONFIG_AUTHORITATIVE_READ | CONFIG_DIRECT_COMMIT
580
+ kt: CONFIG_AUTHORITATIVE_READ | CONFIG_DIRECT_COMMIT
576
581
  });
577
582
  if (t?.timeout !== undefined) f = setTimeout(() => settle(() => i(new TimeoutError)), t.timeout);
578
583
  if (o !== undefined) {
@@ -694,12 +699,20 @@ function createOptimistic(e, t) {
694
699
  * on owner disposal
695
700
  */ function onSettled(e) {
696
701
  const t = getOwner();
697
- t && !(t.T & CONFIG_CHILDREN_FORBIDDEN) ? trackedEffect(() => untrack(e), undefined) : globalQueue.enqueue(EFFECT_USER, () => {
702
+ t && !(t.T & CONFIG_CHILDREN_FORBIDDEN) ? trackedEffect(() => untrack(e), undefined) : globalQueue.enqueue(EFFECT_USER, function fire() {
703
+ // Settled means derived. A settle that reverts optimism (or replays
704
+ // gated reads) only enqueues the affected subscribers; the pass after
705
+ // the commit re-derives them. Fired in the commit pass, the callback
706
+ // read the optimistic source already reverted beside a sync memo of it
707
+ // still holding the optimistic value — reads do not pull (#3411). Fall
708
+ // to the next pass while the heap has work; `run` swapped the queue,
709
+ // so this lands there, and `enqueue` keeps the drain alive.
710
+ if (dirtyQueue.EE >= dirtyQueue.et) return globalQueue.enqueue(EFFECT_USER, fire);
698
711
  // Unowned, out-of-band fire (no owner, or a children-forbidden one this
699
712
  // one-shot must not bind to): a returned cleanup has no lifecycle to
700
713
  // attach to. Reject it in dev; in production the return is simply
701
714
  // dropped — never bound to an unrelated owner or run eagerly.
702
- e();
715
+ e();
703
716
  });
704
717
  }
705
718
 
@@ -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,11 +18,11 @@ 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, 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
 
25
- import { setOptHooks } from "./target.js";
25
+ import { setOptHooks, $OWNER, lookupTarget } from "./target.js";
26
26
 
27
27
  /**
28
28
  * Store rewrite — optimistic stores (§3/§7, RUL-3): no store-side layer, no
@@ -55,7 +55,7 @@ import { setOptHooks } 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.sn !== true && (t.Xe.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.Wt) {
80
- GlobalQueue.Wt = 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,9 +99,9 @@ function installNextBlockedHalf() {
96
99
  e.clear();
97
100
  };
98
101
  }
99
- const e = GlobalQueue.dn;
100
- GlobalQueue.dn = 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
107
  const r = i?.node;
@@ -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?.Pe !== undefined && n.o?.Pe !== 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?.Pe !== undefined && e.k.o?.Pe !== 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
@@ -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.Ae = 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?.Ie == null) {
227
- if (!e.Ne) 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,13 +239,13 @@ 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.Ne || 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.Ae = t;
244
- let n = t._e.get(e);
245
- if (n === undefined) t._e.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;
@@ -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.sn === "object") e = e.sn;
278
- return e.sn === 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.Mt;
326
+ const n = e.Ot;
324
327
  for (let t = 0; t < n.length; t++) {
325
328
  const i = n[t];
326
- i.Ae = e;
329
+ i.Ge = e;
327
330
  if (i.T & CONFIG_OPTIMISTIC && !hasActiveOverride(i)) i.T |= CONFIG_HELD_TRUTH;
328
331
  }
329
332
  }
@@ -363,7 +366,7 @@ 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.Pe));
369
+ const i = unwrapValue(unwrapOverride(t.o.Ce));
367
370
  if (!isWrappable(i)) continue;
368
371
  const o = n(i);
369
372
  if (o === undefined) continue;
@@ -407,7 +410,7 @@ function createOptimisticStoreNext(e, t, n) {
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
412
  for (const r of Reflect.ownKeys(t)) {
410
- if (i && r === "length") continue;
413
+ if (i && r === "length" || r === $OWNER) continue;
411
414
  const o = t[r];
412
415
  const s = unwrapValue(e[r]);
413
416
  if (s === o) continue;
@@ -428,7 +431,7 @@ function createOptimisticStoreNext(e, t, n) {
428
431
  }
429
432
  }
430
433
  for (const n of Reflect.ownKeys(e)) {
431
- if (i && n === "length" || n in t) continue;
434
+ if (i && n === "length" || n === $OWNER || n in t) continue;
432
435
  delete e[n];
433
436
  }
434
437
  }
@@ -451,19 +454,33 @@ function createOptimisticStoreNext(e, t, n) {
451
454
  const e = liveTransition(n);
452
455
  if (e !== null) globalQueue.initTransition(e);
453
456
  }
454
- const i = e.v;
455
- const visible = (t, n) => {
457
+ // The write is judged against what ordinary readers SEE, not against the
458
+ // committed backing slot: under an adoption hold (#3074) `t.v` is already
459
+ // the truth a live transaction is holding, and an optimistic write equal
460
+ // to it compared as a no-op — no override, no lane, and the screen kept
461
+ // the pre-hold value until the transaction committed (#3330's store
462
+ // twin: `s.v = 1` after a `yield` while the derive had already staged
463
+ // `v: 1`). Signal parity: the override reveals on its lane now, with its
464
+ // derivations; the held truth reveals on the transaction's schedule.
465
+ const i = heldMaskView(e) ?? e.v;
466
+ // Compare RAWS on both sides (`nv` below is unwrapped already). A chained
467
+ // target's `old` is the inner store's proxy, whose reads hand back inner
468
+ // child PROXIES; the draft's clone holds the inner raws. Comparing the two
469
+ // as-is marked every untouched row changed, and the overlay then served
470
+ // each from an override as a fresh non-chained target — row identities
471
+ // churned for the life of the action and snapped back at settle (#3323).
472
+ const visible = (t, n) => {
456
473
  const i = e.n?.[t];
457
- return i !== undefined && hasActiveOverride(i) ? unwrapOverride(i.o?.Pe) : n;
474
+ return unwrapValue(i !== undefined && hasActiveOverride(i) ? unwrapOverride(i.o?.Ce) : n);
458
475
  };
459
476
  const visiblePresent = t => {
460
477
  const n = e.h?.[t];
461
- return n !== undefined && hasActiveOverride(n) ? !!unwrapOverride(n.o?.Pe) : t in i;
478
+ return n !== undefined && hasActiveOverride(n) ? !!unwrapOverride(n.o?.Ce) : t in i;
462
479
  };
463
480
  let r = false;
464
481
  const o = Array.isArray(t);
465
482
  for (const n of Reflect.ownKeys(t)) {
466
- if (o && n === "length") continue;
483
+ if (o && n === "length" || n === $OWNER) continue;
467
484
  const s = unwrapValue(t[n]);
468
485
  if (!visiblePresent(n)) {
469
486
  // Optimistic add: value node + presence node + membership bump.
@@ -479,7 +496,7 @@ function createOptimisticStoreNext(e, t, n) {
479
496
  }
480
497
  }
481
498
  for (const n of Reflect.ownKeys(i)) {
482
- if (o && n === "length") continue;
499
+ if (o && n === "length" || n === $OWNER) continue;
483
500
  if (n in t || !visiblePresent(n)) continue;
484
501
  // Optimistic delete: node reads undefined, presence flips, membership bumps.
485
502
  setSignal(getNode(e, n, i[n]), () => undefined);
@@ -504,7 +521,7 @@ function createOptimisticStoreNext(e, t, n) {
504
521
  e.pb = stagedTruthPB.get(e) ?? null;
505
522
  if (e.pb !== null) stagedTruthPB.delete(e);
506
523
  (e.fam.overlaid ??= new Set).add(e);
507
- GlobalQueue._n?.(e.fam.px ?? e.px);
524
+ GlobalQueue.Dn?.(e.fam.px ?? e.px);
508
525
  }
509
526
 
510
527
  /** Optimistic-view composition for snapshot/deep (O1: snapshot is the CURRENT
@@ -512,39 +529,43 @@ function createOptimisticStoreNext(e, t, n) {
512
529
  * RUL-12). Returns `src` untouched when no override is active on `t`.
513
530
  * Authoritative-view reads (until()'s predicate) skip composition entirely:
514
531
  * the predicate observes authoritative truth, never the caller's tentative
515
- * 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) {
516
533
  if (e.fam?.opt !== true || authoritativeRead()) return t;
517
- let n = null;
518
- const ensure = () => n ??= Array.isArray(t) ? [ ...t ] : {
534
+ let i = null;
535
+ const ensure = () => i ??= Array.isArray(t) ? [ ...t ] : {
519
536
  ...t
520
537
  };
521
- const i = e.n;
522
- if (i !== null) {
523
- for (const e of Reflect.ownKeys(i)) {
524
- const n = i[e];
525
- if (!hasActiveOverride(n)) continue;
526
- const r = unwrapOverride(n.o?.Pe);
527
- if (e === "length" && Array.isArray(t)) {
528
- if (t.length !== r) ensure().length = r;
529
- } else if (!isEqual(t[e], r)) ensure()[e] = r;
530
- }
531
- }
532
- const r = e.h;
538
+ const r = e.n;
533
539
  if (r !== null) {
534
540
  for (const e of Reflect.ownKeys(r)) {
535
541
  const i = r[e];
536
- if (!hasActiveOverride(i)) continue;
537
- const o = !!unwrapOverride(i.o?.Pe);
538
- if (!o && e in (n ?? t)) delete ensure()[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);
546
+ if (e === "length" && Array.isArray(t)) {
547
+ if (t.length !== o) ensure().length = o;
548
+ } else if (!isEqual(t[e], o)) ensure()[e] = o;
549
+ }
550
+ }
551
+ const o = e.h;
552
+ if (o !== null) {
553
+ for (const e of Reflect.ownKeys(o)) {
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];
539
560
  }
540
561
  }
541
- return n ?? t;
562
+ return i ?? t;
542
563
  }
543
564
 
544
565
  function applyTentative(e, t, n) {
545
566
  const i = e.pb ?? e.v;
546
567
  const r = optimisticView(e, i);
547
- const o = e.fam.map;
568
+ const o = e.fam;
548
569
  const s = Array.isArray(t);
549
570
  if (Array.isArray(r) !== s) return;
550
571
  // kind change at root: flat overrides below
@@ -552,7 +573,7 @@ function applyTentative(e, t, n) {
552
573
  let u;
553
574
  if (s) u = [ ...t ]; else {
554
575
  u = {};
555
- for (const e of Reflect.ownKeys(t)) u[e] = t[e];
576
+ for (const e of Reflect.ownKeys(t)) if (e !== $OWNER) u[e] = t[e];
556
577
  }
557
578
  const match = (e, t) => {
558
579
  if (!isWrappable(e) || !isWrappable(t)) return null;
@@ -565,7 +586,7 @@ function applyTentative(e, t, n) {
565
586
  // channel — NaN keys are self-equal.
566
587
  if (i !== undefined && r !== undefined && !sameKey(i, r)) return null;
567
588
  }
568
- return o.get(unwrapValue(e)) ?? null;
589
+ return lookupTarget(unwrapValue(e), o) ?? null;
569
590
  };
570
591
  if (s) {
571
592
  const e = r;
@@ -611,6 +632,7 @@ function applyTentative(e, t, n) {
611
632
  }
612
633
  } else {
613
634
  for (const e of Reflect.ownKeys(t)) {
635
+ if (e === $OWNER) continue;
614
636
  const n = unwrapValue(r[e]);
615
637
  const i = t[e];
616
638
  const o = match(n, i);
@@ -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.Ne ? JSON.parse(JSON.stringify(e[$TARGET][STORE_VALUE])) : null;
219
- const l = wrapDraft(e, () => !c || n.o?.Ie === 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.Ne) commit(l);
243
+ const a = handleAsync(n, u, commit);
244
+ if (!n.ge) commit(a);
234
245
  }, false);
235
246
  return n;
236
247
  }