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