@solidjs/signals 2.0.0-rc.6 → 2.0.0-rc.7

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 (65) hide show
  1. package/dist/dev.js +2033 -1291
  2. package/dist/node.cjs +1821 -2401
  3. package/dist/node.dev.cjs +13724 -0
  4. package/dist/prod/boundaries.js +3 -1
  5. package/dist/prod/core/async.js +6 -1
  6. package/dist/prod/core/attribution-hooks.js +3 -0
  7. package/dist/prod/core/constants.js +9 -1
  8. package/dist/prod/core/context.js +10 -16
  9. package/dist/prod/core/core.js +215 -139
  10. package/dist/prod/core/dev.js +2 -0
  11. package/dist/prod/core/effect.js +37 -28
  12. package/dist/prod/core/external.js +2 -2
  13. package/dist/prod/core/graph.js +35 -31
  14. package/dist/prod/core/heap.js +34 -40
  15. package/dist/prod/core/lanes.js +41 -27
  16. package/dist/prod/core/optimistic.js +42 -32
  17. package/dist/prod/core/owner.js +32 -32
  18. package/dist/prod/core/scheduler.js +140 -154
  19. package/dist/prod/core/verdict.js +31 -34
  20. package/dist/prod/index.js +0 -2
  21. package/dist/prod/map.js +104 -94
  22. package/dist/prod/signals.js +51 -34
  23. package/dist/prod/store/next/optimistic.js +153 -172
  24. package/dist/prod/store/next/reconcile.js +140 -288
  25. package/dist/prod/store/next/store.js +307 -237
  26. package/dist/prod/store/store.js +2 -2
  27. package/dist/types/core/attribution-hooks.d.ts +64 -0
  28. package/dist/types/core/attribution.d.ts +265 -9
  29. package/dist/types/core/constants.d.ts +8 -0
  30. package/dist/types/core/core.d.ts +12 -3
  31. package/dist/types/core/dev.d.ts +49 -8
  32. package/dist/types/core/heap.d.ts +5 -3
  33. package/dist/types/core/invariants.d.ts +1 -1
  34. package/dist/types/core/lanes.d.ts +10 -0
  35. package/dist/types/core/scheduler.d.ts +9 -4
  36. package/dist/types/signals.d.ts +10 -0
  37. package/dist/types/store/index.d.ts +3 -6
  38. package/dist/types/store/next/optimistic.d.ts +4 -2
  39. package/dist/types/store/next/reconcile.d.ts +5 -12
  40. package/dist/types/store/next/store.d.ts +3 -9
  41. package/dist/types/store/next/target.d.ts +20 -46
  42. package/dist/types/store/store.d.ts +14 -9
  43. package/dist/types-cjs/core/attribution-hooks.d.cts +64 -0
  44. package/dist/types-cjs/core/attribution.d.cts +265 -9
  45. package/dist/types-cjs/core/constants.d.cts +8 -0
  46. package/dist/types-cjs/core/core.d.cts +12 -3
  47. package/dist/types-cjs/core/dev.d.cts +49 -8
  48. package/dist/types-cjs/core/heap.d.cts +5 -3
  49. package/dist/types-cjs/core/invariants.d.cts +1 -1
  50. package/dist/types-cjs/core/lanes.d.cts +10 -0
  51. package/dist/types-cjs/core/scheduler.d.cts +9 -4
  52. package/dist/types-cjs/signals.d.cts +10 -0
  53. package/dist/types-cjs/store/index.d.cts +3 -6
  54. package/dist/types-cjs/store/next/optimistic.d.cts +4 -2
  55. package/dist/types-cjs/store/next/reconcile.d.cts +5 -12
  56. package/dist/types-cjs/store/next/store.d.cts +3 -9
  57. package/dist/types-cjs/store/next/target.d.cts +20 -46
  58. package/dist/types-cjs/store/store.d.cts +14 -9
  59. package/package.json +3 -2
  60. package/dist/prod/store/next/patch-hooks.js +0 -13
  61. package/dist/prod/store/next/patch.js +0 -614
  62. package/dist/types/store/next/patch-hooks.d.ts +0 -41
  63. package/dist/types/store/next/patch.d.ts +0 -91
  64. package/dist/types-cjs/store/next/patch-hooks.d.cts +0 -41
  65. package/dist/types-cjs/store/next/patch.d.cts +0 -91
@@ -1,4 +1,4 @@
1
- import { CONFIG_AUTO_DISPOSE, STATUS_PENDING, NOT_PENDING, CONFIG_OPTIMISTIC, CONFIG_HELD_TRUTH, unwrapOverride } from "../../core/constants.js";
1
+ import { CONFIG_AUTO_DISPOSE, STATUS_PENDING, STATUS_UNINITIALIZED, NOT_PENDING, CONFIG_OPTIMISTIC, CONFIG_HELD_TRUTH, unwrapOverride } from "../../core/constants.js";
2
2
 
3
3
  import { computed, setSignal, isEqual } from "../../core/core.js";
4
4
 
@@ -20,9 +20,7 @@ import { runProjectionComputedNext } from "./projection.js";
20
20
 
21
21
  import { wrapNext, runAuthoritative, storeSetterNext, hasActiveOverride, unwrapValue, targetsEqual, getNode, getHasNode, getKeySetNode, bumpDeep, stagedTruthPB, authoritativeRead } from "./store.js";
22
22
 
23
- import { patchHooks, rowHooks } from "./patch-hooks.js";
24
-
25
- import { sameKey, buildIdentityRowOps } from "./reconcile.js";
23
+ import { sameKey } from "./reconcile.js";
26
24
 
27
25
  import { setOptHooks } from "./target.js";
28
26
 
@@ -57,7 +55,7 @@ import { setOptHooks } from "./target.js";
57
55
  * staged values to converge (normal speculation). Resolves merges first:
58
56
  * merge unions optimistic nodes/stores into the target. */ function transitionHoldsOptimism(e) {
59
57
  const t = currentTransition(e);
60
- return t.sn !== true && (t.ze.length !== 0 || t.En.size !== 0);
58
+ return t.sn !== true && (t.Xe.length !== 0 || t.En.size !== 0);
61
59
  }
62
60
 
63
61
  let blockedInstalled = false;
@@ -78,29 +76,20 @@ function installNextBlockedHalf() {
78
76
  // Scheduler flush tails call _clearOptimisticStores whenever tracked
79
77
  // stores exist; next has no layer to clear — reverts are engine-native —
80
78
  // so the hook only empties the batch set.
81
- if (!GlobalQueue.qt) {
82
- GlobalQueue.qt = e => {
83
- // Patch channel (revert site): engine-native reverts flip node values
84
- // back to committed; patched records need a forced DOM re-apply from
85
- // the post-revert view. Emission only — next keeps no layer to clear.
79
+ if (!GlobalQueue.Wt) {
80
+ GlobalQueue.Wt = e => {
86
81
  for (const t of e) {
87
82
  const e = t?.[$TARGET];
88
83
  const n = e?.fam?.overlaid;
89
84
  if (n !== undefined) {
90
85
  for (const e of n) {
91
- if (e.pc !== null && e.pc.p !== null) patchHooks.emitPatchOptimistic(e, null, null);
92
- // Row-ops resync (family increment 2): reverts flip node values
93
- // back engine-natively; a driven list must rebuild retention by
94
- // row identity against the post-revert view (resolved from the
95
- // target at drain — overrides are gone by then).
96
- if (e.pc !== null && e.pc.ro !== null) rowHooks.emitRowOpsOptimistic(e, null, null);
97
86
  // Keyset resync (classic channel twin): the keyset node's own
98
87
  // revert can compare EQUAL (a landing's bump matched the
99
88
  // tentative bump) while the arrangement underneath changed —
100
89
  // mapArray/ownKeys subscribers must re-read the post-revert
101
90
  // view. Authoritative bump: never re-arm the node we are
102
91
  // clearing.
103
- if (e.k !== null) runAuthoritative(() => setSignal(e.k, e => e + 1));
92
+ if (e.k !== null) runAuthoritative(() => setSignal(e.k, e => e + 1));
104
93
  }
105
94
  }
106
95
  }
@@ -112,19 +101,19 @@ function installNextBlockedHalf() {
112
101
  for (const e of t.En) {
113
102
  const n = e?.[$TARGET];
114
103
  const i = n?.fam;
115
- const o = i?.node;
104
+ const r = i?.node;
116
105
  // The hold exists to keep optimistic state alive until the store's own
117
106
  // truth lands (#2951). Once the family carries NO live overrides (a
118
107
  // landing consumed them, or they never existed), a pending firewall is
119
108
  // no reason to park the transaction — blocking then leaks it forever
120
109
  // when the in-flight question is never answered (undisposed fixtures).
121
- if (o == null || !(o.S & STATUS_PENDING)) continue;
110
+ if (r == null || !(r.S & STATUS_PENDING)) continue;
122
111
  // Ownership is declared (#3146): only the flight's OWN transaction
123
112
  // parks on the flight (the #2951 anchor routed the bare write there).
124
113
  // A transaction that merely brushed the store never waits for truth
125
114
  // it does not carry.
126
- const r = i.ft != null ? liveTransition(i.ft) : null;
127
- if (r !== null && r !== currentTransition(t)) continue;
115
+ const o = i.ft != null ? liveTransition(i.ft) : null;
116
+ if (o !== null && o !== currentTransition(t)) continue;
128
117
  if (familyHasLiveOverrides(i)) return true;
129
118
  }
130
119
  return e(t);
@@ -155,21 +144,21 @@ function createOptimisticStoreNext(e, t, n) {
155
144
  installOptimisticEngine();
156
145
  installNextBlockedHalf();
157
146
  const i = typeof e === "function";
158
- if (!i && n === undefined) n = t;
147
+ const r = i ? n : t;
159
148
  const o = i ? t : e;
160
- const r = {
149
+ const s = {
161
150
  map: new WeakMap,
162
151
  node: null,
163
- shallow: !!n?.shallow,
152
+ shallow: !!r?.shallow,
164
153
  opt: true
165
154
  };
166
- const l = wrapNext(o, null, null, r);
167
- r.px = l;
155
+ const l = wrapNext(o, null, null, s);
156
+ s.px = l;
168
157
  // Same key resolution the projection channels use ("id" default) — replay's
169
158
  // satisfaction rule reads it off the family.
170
- const s = n?.key === undefined ? "id" : n.key;
171
- r.key = typeof s === "function" ? s : s === null ? null : e => isWrappable(e) ? e[s] : undefined;
172
- if (r.shallow) {
159
+ const u = r?.key === undefined ? "id" : r.key;
160
+ s.key = typeof u === "function" ? u : u === null ? null : e => isWrappable(e) ? e[u] : undefined;
161
+ if (s.shallow) {
173
162
  l[$TARGET].s = true;
174
163
  markRawIngest(o);
175
164
  }
@@ -185,11 +174,11 @@ function createOptimisticStoreNext(e, t, n) {
185
174
  // flight (loading window) keeps the ambient reveal (#2933: the loading
186
175
  // rail is transaction-invisible).
187
176
  const enterFlightTransition = () => {
188
- const e = r.ft;
177
+ const e = s.ft;
189
178
  if (e == null) return;
190
179
  let t = liveTransition(e);
191
- if (t === null) r.ft = t = createTransition();
192
- r.node.Ae = t;
180
+ if (t === null) s.ft = t = createTransition();
181
+ s.node.Ae = t;
193
182
  globalQueue.initTransition(t);
194
183
  };
195
184
  // Landing router (#3164 fold ruling): while a transaction retains
@@ -200,9 +189,9 @@ function createOptimisticStoreNext(e, t, n) {
200
189
  // flight-owned transaction (#3146). Sync commits (the derive's body,
201
190
  // owner is the firewall itself) reveal with their own recompute's flush.
202
191
  const wrapCommit = (e, t) => {
203
- const n = retainingTransition(r);
204
- if (n !== null) return void stageLanding(r, n, t);
205
- if (getOwner() !== r.node) enterFlightTransition();
192
+ const n = retainingTransition(s);
193
+ if (n !== null) return void stageLanding(s, n, t);
194
+ if (getOwner() !== s.node) enterFlightTransition();
206
195
  runAuthoritative(e);
207
196
  };
208
197
  // Draft writes (the derive mutating its draft, sync body and post-await
@@ -212,7 +201,7 @@ function createOptimisticStoreNext(e, t, n) {
212
201
  // batch; the write-override eager-commit branch in notifyWrites yields
213
202
  // to any active transaction).
214
203
  const aroundDraftWrite = e => {
215
- const t = retainingTransition(r);
204
+ const t = retainingTransition(s);
216
205
  if (t === null) e(); else runFolded(t, e);
217
206
  };
218
207
  // Flight declaration (#3146): a recompute that registered a truth-flight
@@ -235,32 +224,41 @@ function createOptimisticStoreNext(e, t, n) {
235
224
  // the declaration.
236
225
  const declareFlight = e => {
237
226
  if (e.o?.Ie == null) {
238
- if (!e.Ne) r.ft = null;
227
+ if (!e.Ne) s.ft = null;
239
228
  return;
240
229
  }
241
- if (e.Ne) return;
230
+ // First flight (#3146 carve-out): nothing has ever committed, so there
231
+ // is no truth to keep on screen and no optimistic state to protect. An
232
+ // uninitialized ask suspends its readers into their Loading boundary
233
+ // exactly like a plain derived store's first flight — declaring a
234
+ // transaction here instead held the ROOT MOUNT (render()'s scheduled
235
+ // insert rides transitions) until the fetch landed, so the boundary's
236
+ // fallback never showed and the whole page stayed blank. The loading
237
+ // window (#2933) already declares nothing for the same reason; once
238
+ // the first truth lands, every refetch flight declares as before.
239
+ if (e.Ne || e.S & STATUS_UNINITIALIZED) return;
242
240
  let t = activeTransition;
243
241
  if (t === null) globalQueue.initTransition(t = createTransition());
244
- r.ft = t;
242
+ s.ft = t;
245
243
  e.Ae = t;
246
244
  let n = t._e.get(e);
247
245
  if (n === undefined) t._e.set(e, n = new Set);
248
246
  n.add(e);
249
247
  };
250
- let i;
251
- if (n?.seedLoadingValue) i = {
248
+ let n;
249
+ if (r?.seedLoadingValue) n = {
252
250
  loadingValue: undefined
253
251
  };
254
- const o = computed(() => {
252
+ const i = computed(() => {
255
253
  const e = getOwner();
256
254
  try {
257
- runAuthoritative(() => runProjectionComputedNext(l, t, n?.key === undefined ? "id" : n.key, wrapCommit, aroundDraftWrite));
255
+ runAuthoritative(() => runProjectionComputedNext(l, t, r?.key === undefined ? "id" : r.key, wrapCommit, aroundDraftWrite));
258
256
  } finally {
259
257
  declareFlight(e);
260
258
  }
261
- }, i);
262
- o.T &= ~CONFIG_AUTO_DISPOSE;
263
- r.node = o;
259
+ }, n);
260
+ i.T &= ~CONFIG_AUTO_DISPOSE;
261
+ s.node = i;
264
262
  }
265
263
  return [ l, e => {
266
264
  // Retention ledger (#3164): record the owning transaction so landings
@@ -270,7 +268,7 @@ function createOptimisticStoreNext(e, t, n) {
270
268
  // per #2951 and dies with it).
271
269
  const t = activeTransition;
272
270
  storeSetterNext(l, e);
273
- if (t !== null) (r.rt ??= new Set).add(t);
271
+ if (t !== null) (s.rt ??= new Set).add(t);
274
272
  } ];
275
273
  }
276
274
 
@@ -322,7 +320,7 @@ function createOptimisticStoreNext(e, t, n) {
322
320
  * stay unarmed — the override is their display and its revert their
323
321
  * notification, A17). */ function runFolded(e, t) {
324
322
  runAsTransitionBatch(e, t);
325
- const n = e.Lt;
323
+ const n = e.Mt;
326
324
  for (let t = 0; t < n.length; t++) {
327
325
  const i = n[t];
328
326
  i.Ae = e;
@@ -342,15 +340,15 @@ function createOptimisticStoreNext(e, t, n) {
342
340
  if (n !== null) {
343
341
  // Occurrence-aware key queues (parity with the adoption window):
344
342
  // duplicate keys match per occurrence, each current row consumed once.
345
- let o = null;
346
- const r = e.length;
347
- for (let t = 0; t < r; t++) {
343
+ let r = null;
344
+ const o = e.length;
345
+ for (let t = 0; t < o; t++) {
348
346
  const i = unwrapValue(e[t]);
349
347
  if (!isWrappable(i)) continue;
350
- const r = n(i);
351
- if (r === undefined) continue;
352
- const l = (o ??= new Map).get(r);
353
- if (l === undefined) o.set(r, [ i ]); else l.push(i);
348
+ const o = n(i);
349
+ if (o === undefined) continue;
350
+ const s = (r ??= new Map).get(o);
351
+ if (s === undefined) r.set(o, [ i ]); else s.push(i);
354
352
  }
355
353
  // Echo adoption: an optimistic structural add whose key the landing
356
354
  // confirms must keep its raw (and so its proxy — list drivers keep the
@@ -360,47 +358,47 @@ function createOptimisticStoreNext(e, t, n) {
360
358
  // keys; overlay rows only extend coverage. Adopted raws enter staged
361
359
  // truth; at settle the override reverts and the reveal re-seats the
362
360
  // same raw.
363
- const l = e[$TARGET]?.n;
364
- if (l != null) {
365
- for (const e of Reflect.ownKeys(l)) {
366
- const t = l[e];
361
+ const s = e[$TARGET]?.n;
362
+ if (s != null) {
363
+ for (const e of Reflect.ownKeys(s)) {
364
+ const t = s[e];
367
365
  if (!hasActiveOverride(t)) continue;
368
366
  const i = unwrapValue(unwrapOverride(t.o.Pe));
369
367
  if (!isWrappable(i)) continue;
370
- const r = n(i);
371
- if (r === undefined) continue;
372
- const s = (o ??= new Map).get(r);
373
- if (s === undefined) o.set(r, [ i ]); else s.push(i);
368
+ const o = n(i);
369
+ if (o === undefined) continue;
370
+ const l = (r ??= new Map).get(o);
371
+ if (l === undefined) r.set(o, [ i ]); else l.push(i);
374
372
  }
375
373
  }
376
- for (let r = 0; r < i; r++) {
377
- const i = t[r];
378
- let l;
379
- if (isWrappable(i) && o !== null) {
374
+ for (let o = 0; o < i; o++) {
375
+ const i = t[o];
376
+ let s;
377
+ if (isWrappable(i) && r !== null) {
380
378
  const e = n(i);
381
379
  if (e !== undefined) {
382
- for (const [t, n] of o) {
380
+ for (const [t, n] of r) {
383
381
  if (!sameKey(t, e)) continue;
384
- l = n.shift();
385
- if (n.length === 0) o.delete(t);
382
+ s = n.shift();
383
+ if (n.length === 0) r.delete(t);
386
384
  break;
387
385
  }
388
386
  }
389
387
  }
390
- if (l !== undefined) {
391
- if (unwrapValue(e[r]) !== l) e[r] = l;
392
- stagedApply(e[r], i, n);
388
+ if (s !== undefined) {
389
+ if (unwrapValue(e[o]) !== s) e[o] = s;
390
+ stagedApply(e[o], i, n);
393
391
  } else {
394
- const t = unwrapValue(e[r]);
395
- if (!isEqual(t, i) && !targetsEqual(t, i)) e[r] = i;
392
+ const t = unwrapValue(e[o]);
393
+ if (!isEqual(t, i) && !targetsEqual(t, i)) e[o] = i;
396
394
  }
397
395
  }
398
396
  } else {
399
- for (let o = 0; o < i; o++) {
400
- const i = t[o];
401
- const r = unwrapValue(e[o]);
402
- if (r === i) continue;
403
- 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;
397
+ for (let r = 0; r < i; r++) {
398
+ const i = t[r];
399
+ const o = unwrapValue(e[r]);
400
+ if (o === i) continue;
401
+ 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;
404
402
  }
405
403
  }
406
404
  if (e.length !== i) e.length = i;
@@ -408,25 +406,25 @@ function createOptimisticStoreNext(e, t, n) {
408
406
  }
409
407
  // Object merge; also the degenerate root-kind-change shape (arrays accept
410
408
  // keyed writes/deletes, so a wholesale restatement still lands staged).
411
- for (const o of Reflect.ownKeys(t)) {
412
- if (i && o === "length") continue;
413
- const r = t[o];
414
- const l = unwrapValue(e[o]);
415
- if (l === r) continue;
416
- if (isWrappable(r) && isWrappable(l) && Array.isArray(r) === Array.isArray(l)) {
409
+ for (const r of Reflect.ownKeys(t)) {
410
+ if (i && r === "length") continue;
411
+ const o = t[r];
412
+ const s = unwrapValue(e[r]);
413
+ if (s === o) continue;
414
+ if (isWrappable(o) && isWrappable(s) && Array.isArray(o) === Array.isArray(s)) {
417
415
  // Different-keyed entities never merge (tentative-channel parity):
418
416
  // the incoming object replaces the slot wholesale.
419
417
  if (n !== null) {
420
- const t = n(l);
421
- const i = n(r);
418
+ const t = n(s);
419
+ const i = n(o);
422
420
  if (t !== undefined && i !== undefined && !sameKey(t, i)) {
423
- e[o] = r;
421
+ e[r] = o;
424
422
  continue;
425
423
  }
426
424
  }
427
- stagedApply(e[o], r, n);
428
- } else if (!isEqual(l, r) && !targetsEqual(l, r)) {
429
- e[o] = r;
425
+ stagedApply(e[r], o, n);
426
+ } else if (!isEqual(s, o) && !targetsEqual(s, o)) {
427
+ e[r] = o;
430
428
  }
431
429
  }
432
430
  for (const n of Reflect.ownKeys(e)) {
@@ -454,21 +452,6 @@ function createOptimisticStoreNext(e, t, n) {
454
452
  if (e !== null) globalQueue.initTransition(e);
455
453
  }
456
454
  const i = e.v;
457
- // Patch channel (override-application site): the draft IS the intended
458
- // visible state; prev is the view before these overrides apply. Bypasses
459
- // the transition stash — optimism is visible in flight.
460
- if (e.pc !== null && e.pc.p !== null) patchHooks.emitPatchOptimistic(e, t, optimisticView(e, i));
461
- // Row-ops channel (family increment 2): optimistic STRUCTURE on an array
462
- // rides node overrides — it never enters the reconcile walk — so a driven
463
- // list must get its structural ops here, lane-timed. Identity diff of the
464
- // pre-write optimistic view against the draft; aligned writes emit nothing.
465
- if (e.pc !== null && e.pc.ro !== null && Array.isArray(t)) {
466
- const n = optimisticView(e, i);
467
- if (Array.isArray(n)) {
468
- const i = buildIdentityRowOps(n, t);
469
- if (i !== null) rowHooks.emitRowOpsOptimistic(e, t, i);
470
- }
471
- }
472
455
  const visible = (t, n) => {
473
456
  const i = e.n?.[t];
474
457
  return i !== undefined && hasActiveOverride(i) ? unwrapOverride(i.o?.Pe) : n;
@@ -477,40 +460,40 @@ function createOptimisticStoreNext(e, t, n) {
477
460
  const n = e.h?.[t];
478
461
  return n !== undefined && hasActiveOverride(n) ? !!unwrapOverride(n.o?.Pe) : t in i;
479
462
  };
480
- let o = false;
481
- const r = Array.isArray(t);
463
+ let r = false;
464
+ const o = Array.isArray(t);
482
465
  for (const n of Reflect.ownKeys(t)) {
483
- if (r && n === "length") continue;
484
- const l = unwrapValue(t[n]);
466
+ if (o && n === "length") continue;
467
+ const s = unwrapValue(t[n]);
485
468
  if (!visiblePresent(n)) {
486
469
  // Optimistic add: value node + presence node + membership bump.
487
- setSignal(getNode(e, n, i[n]), () => l);
470
+ setSignal(getNode(e, n, i[n]), () => s);
488
471
  setSignal(getHasNode(e, n, n in i), true);
489
- o = true;
472
+ r = true;
490
473
  } else {
491
474
  const t = visible(n, i[n]);
492
- if (!isEqual(t, l) && !targetsEqual(t, l)) {
493
- setSignal(getNode(e, n, t), () => l);
494
- if (r) o = true;
475
+ if (!isEqual(t, s) && !targetsEqual(t, s)) {
476
+ setSignal(getNode(e, n, t), () => s);
477
+ if (o) r = true;
495
478
  }
496
479
  }
497
480
  }
498
481
  for (const n of Reflect.ownKeys(i)) {
499
- if (r && n === "length") continue;
482
+ if (o && n === "length") continue;
500
483
  if (n in t || !visiblePresent(n)) continue;
501
484
  // Optimistic delete: node reads undefined, presence flips, membership bumps.
502
485
  setSignal(getNode(e, n, i[n]), () => undefined);
503
486
  setSignal(getHasNode(e, n, true), false);
504
- o = true;
487
+ r = true;
505
488
  }
506
- if (r) {
489
+ if (o) {
507
490
  const n = visible("length", i.length);
508
491
  if (n !== t.length) {
509
492
  setSignal(getNode(e, "length", n), () => t.length);
510
- o = true;
493
+ r = true;
511
494
  }
512
495
  }
513
- if (o) setSignal(getKeySetNode(e), e => e + 1);
496
+ if (r) setSignal(getKeySetNode(e), e => e + 1);
514
497
  // Deep-witness: optimistic value writes notify deep() subscribers too
515
498
  // (structural ones already ride the key-set bump above).
516
499
  bumpDeep(e);
@@ -540,19 +523,19 @@ function createOptimisticStoreNext(e, t, n) {
540
523
  for (const e of Reflect.ownKeys(i)) {
541
524
  const n = i[e];
542
525
  if (!hasActiveOverride(n)) continue;
543
- const o = unwrapOverride(n.o?.Pe);
526
+ const r = unwrapOverride(n.o?.Pe);
544
527
  if (e === "length" && Array.isArray(t)) {
545
- if (t.length !== o) ensure().length = o;
546
- } else if (!isEqual(t[e], o)) ensure()[e] = o;
528
+ if (t.length !== r) ensure().length = r;
529
+ } else if (!isEqual(t[e], r)) ensure()[e] = r;
547
530
  }
548
531
  }
549
- const o = e.h;
550
- if (o !== null) {
551
- for (const e of Reflect.ownKeys(o)) {
552
- const i = o[e];
532
+ const r = e.h;
533
+ if (r !== null) {
534
+ for (const e of Reflect.ownKeys(r)) {
535
+ const i = r[e];
553
536
  if (!hasActiveOverride(i)) continue;
554
- const r = !!unwrapOverride(i.o?.Pe);
555
- if (!r && e in (n ?? t)) delete ensure()[e];
537
+ const o = !!unwrapOverride(i.o?.Pe);
538
+ if (!o && e in (n ?? t)) delete ensure()[e];
556
539
  }
557
540
  }
558
541
  return n ?? t;
@@ -560,35 +543,39 @@ function createOptimisticStoreNext(e, t, n) {
560
543
 
561
544
  function applyTentative(e, t, n) {
562
545
  const i = e.pb ?? e.v;
563
- const o = optimisticView(e, i);
564
- const r = e.fam.map;
565
- const l = Array.isArray(t);
566
- if (Array.isArray(o) !== l) return;
546
+ const r = optimisticView(e, i);
547
+ const o = e.fam.map;
548
+ const s = Array.isArray(t);
549
+ if (Array.isArray(r) !== s) return;
567
550
  // kind change at root: flat overrides below
568
- const s = [];
569
- const u = l ? [ ...t ] : shallowWithSymbols(t);
551
+ const l = [];
552
+ let u;
553
+ if (s) u = [ ...t ]; else {
554
+ u = {};
555
+ for (const e of Reflect.ownKeys(t)) u[e] = t[e];
556
+ }
570
557
  const match = (e, t) => {
571
558
  if (!isWrappable(e) || !isWrappable(t)) return null;
572
559
  if (rawValuesUsed && (isRawValue(e) || isRawValue(t))) return null;
573
560
  if (Array.isArray(e) !== Array.isArray(t)) return null;
574
561
  if (n) {
575
562
  const i = n(e);
576
- const o = n(t);
563
+ const r = n(t);
577
564
  // SameValueZero (re-audit 3, P1-3): parity with the plain reconcile
578
565
  // channel — NaN keys are self-equal.
579
- if (i !== undefined && o !== undefined && !sameKey(i, o)) return null;
566
+ if (i !== undefined && r !== undefined && !sameKey(i, r)) return null;
580
567
  }
581
- return r.get(unwrapValue(e)) ?? null;
568
+ return o.get(unwrapValue(e)) ?? null;
582
569
  };
583
- if (l) {
584
- const e = o;
570
+ if (s) {
571
+ const e = r;
585
572
  let i = null;
586
- for (let o = 0; o < t.length; o++) {
587
- const r = t[o];
588
- if (!isWrappable(r)) continue;
589
- let l;
573
+ for (let r = 0; r < t.length; r++) {
574
+ const o = t[r];
575
+ if (!isWrappable(o)) continue;
576
+ let s;
590
577
  if (n) {
591
- const t = n(r);
578
+ const t = n(o);
592
579
  if (t !== undefined) {
593
580
  if (i === null) {
594
581
  // Occurrence-aware index queues (re-audit 3, P1-3): parity with
@@ -596,40 +583,40 @@ function applyTentative(e, t, n) {
596
583
  // occurrence, each view row consumed once.
597
584
  i = new Map;
598
585
  for (let t = 0; t < e.length; t++) {
599
- const o = unwrapValue(e[t]);
600
- if (isWrappable(o)) {
601
- const e = n(o);
586
+ const r = unwrapValue(e[t]);
587
+ if (isWrappable(r)) {
588
+ const e = n(r);
602
589
  if (e === undefined) continue;
603
- const r = i.get(e);
604
- if (r === undefined) i.set(e, t); else if (Array.isArray(r)) r.push(t); else i.set(e, [ r, t ]);
590
+ const o = i.get(e);
591
+ if (o === undefined) i.set(e, t); else if (Array.isArray(o)) o.push(t); else i.set(e, [ o, t ]);
605
592
  }
606
593
  }
607
594
  }
608
- const o = i.get(t);
609
- if (o === undefined) l = undefined; else if (Array.isArray(o)) {
610
- l = unwrapValue(e[o.shift()]);
611
- if (o.length === 1) i.set(t, o[0]);
595
+ const r = i.get(t);
596
+ if (r === undefined) s = undefined; else if (Array.isArray(r)) {
597
+ s = unwrapValue(e[r.shift()]);
598
+ if (r.length === 1) i.set(t, r[0]);
612
599
  } else {
613
- l = unwrapValue(e[o]);
600
+ s = unwrapValue(e[r]);
614
601
  i.delete(t);
615
602
  }
616
- } else l = unwrapValue(e[o]);
617
- } else l = unwrapValue(e[o]);
618
- const a = match(l, r);
603
+ } else s = unwrapValue(e[r]);
604
+ } else s = unwrapValue(e[r]);
605
+ const a = match(s, o);
619
606
  if (a !== null) {
620
607
  // Keep the existing row in the slot (identity preserved); recurse.
621
- u[o] = unwrapValue(l);
622
- s.push([ a, r ]);
608
+ u[r] = unwrapValue(s);
609
+ l.push([ a, o ]);
623
610
  }
624
611
  }
625
612
  } else {
626
613
  for (const e of Reflect.ownKeys(t)) {
627
- const n = unwrapValue(o[e]);
614
+ const n = unwrapValue(r[e]);
628
615
  const i = t[e];
629
- const r = match(n, i);
630
- if (r !== null) {
616
+ const o = match(n, i);
617
+ if (o !== null) {
631
618
  u[e] = n;
632
- s.push([ r, i ]);
619
+ l.push([ o, i ]);
633
620
  }
634
621
  }
635
622
  }
@@ -639,13 +626,7 @@ function applyTentative(e, t, n) {
639
626
  e.pb = null;
640
627
  notifyOptimisticWrites(e, u);
641
628
  e.pb = a;
642
- for (let e = 0; e < s.length; e++) applyTentative(s[e][0], unwrapValue(s[e][1]), n);
643
- }
644
-
645
- function shallowWithSymbols(e) {
646
- const t = {};
647
- for (const n of Reflect.ownKeys(e)) t[n] = e[n];
648
- return t;
629
+ for (let e = 0; e < l.length; e++) applyTentative(l[e][0], unwrapValue(l[e][1]), n);
649
630
  }
650
631
 
651
632
  export { createOptimisticStoreNext, notifyOptimisticWrites, optimisticView, transitionHoldsOptimism };