@solidjs/signals 2.0.0-rc.14 → 2.0.0-rc.15

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 (66) hide show
  1. package/dist/dev-shared.js +248 -189
  2. package/dist/dev.attribution.js +124 -41
  3. package/dist/dev.js +553 -322
  4. package/dist/observe/affects.js +34 -31
  5. package/dist/observe/boundaries.js +166 -132
  6. package/dist/observe/core/async.js +90 -83
  7. package/dist/observe/core/attribution-queries.js +1 -1
  8. package/dist/observe/core/attribution.js +281 -197
  9. package/dist/observe/core/constants.js +7 -2
  10. package/dist/observe/core/context.js +3 -3
  11. package/dist/observe/core/core.js +277 -245
  12. package/dist/observe/core/dev.js +3 -3
  13. package/dist/observe/core/effect.js +21 -21
  14. package/dist/observe/core/external.js +2 -2
  15. package/dist/observe/core/graph.js +25 -25
  16. package/dist/observe/core/heap.js +9 -9
  17. package/dist/observe/core/lanes.js +206 -160
  18. package/dist/observe/core/owner.js +27 -27
  19. package/dist/observe/core/scheduler.js +92 -86
  20. package/dist/observe/core/verdict.js +66 -54
  21. package/dist/observe/map.js +35 -35
  22. package/dist/observe/reveal.js +24 -24
  23. package/dist/observe/signals.js +38 -28
  24. package/dist/observe/store/affects.js +75 -68
  25. package/dist/observe/store/hooks.js +13 -0
  26. package/dist/observe/store/index.js +0 -2
  27. package/dist/observe/store/optimistic.js +67 -34
  28. package/dist/observe/store/projection.js +37 -31
  29. package/dist/observe/store/reconcile.js +32 -30
  30. package/dist/observe/store/store.js +128 -85
  31. package/dist/prod/affects.js +34 -31
  32. package/dist/prod/boundaries.js +143 -109
  33. package/dist/prod/core/async.js +64 -67
  34. package/dist/prod/core/constants.js +7 -2
  35. package/dist/prod/core/context.js +3 -3
  36. package/dist/prod/core/core.js +302 -270
  37. package/dist/prod/core/effect.js +38 -38
  38. package/dist/prod/core/external.js +4 -4
  39. package/dist/prod/core/graph.js +27 -27
  40. package/dist/prod/core/heap.js +30 -30
  41. package/dist/prod/core/lanes.js +257 -211
  42. package/dist/prod/core/owner.js +39 -39
  43. package/dist/prod/core/scheduler.js +125 -119
  44. package/dist/prod/core/verdict.js +70 -58
  45. package/dist/prod/map.js +73 -73
  46. package/dist/prod/reveal.js +38 -38
  47. package/dist/prod/signals.js +30 -20
  48. package/dist/prod/store/affects.js +75 -68
  49. package/dist/prod/store/hooks.js +13 -0
  50. package/dist/prod/store/index.js +0 -2
  51. package/dist/prod/store/optimistic.js +114 -81
  52. package/dist/prod/store/projection.js +30 -24
  53. package/dist/prod/store/reconcile.js +32 -30
  54. package/dist/prod/store/store.js +136 -93
  55. package/dist/types/boundaries.d.ts +4 -0
  56. package/dist/types/core/attribution.d.ts +6 -2
  57. package/dist/types/core/constants.d.ts +5 -0
  58. package/dist/types/core/core.d.ts +33 -3
  59. package/dist/types/core/lanes.d.ts +10 -0
  60. package/dist/types/core/scheduler.d.ts +12 -6
  61. package/dist/types/core/types.d.ts +0 -6
  62. package/dist/types/store/affects.d.ts +11 -0
  63. package/dist/types/store/hooks.d.ts +48 -0
  64. package/dist/types/store/index.d.ts +0 -1
  65. package/dist/types/store/store.d.ts +3 -38
  66. package/package.json +1 -1
@@ -1,20 +1,20 @@
1
- import { NotReadyError, unwrapStatusError, TimeoutError } from "./core/error.js";
1
+ import { TimeoutError, NotReadyError, unwrapStatusError } from "./core/error.js";
2
2
 
3
3
  import "./core/verdict.js";
4
4
 
5
- import { CONFIG_CHILDREN_FORBIDDEN, EFFECT_USER, $REFRESH, CONFIG_REDERIVE, REACTIVE_JOINED, CONFIG_AUTO_DISPOSE, CONFIG_FRESH_READ, CONFIG_AUTHORITATIVE } from "./core/constants.js";
5
+ import { CONFIG_CHILDREN_FORBIDDEN, EFFECT_USER, $REFRESH, CONFIG_AUTO_DISPOSE, CONFIG_AUTHORITATIVE, CONFIG_REDERIVE, REACTIVE_JOINED, CONFIG_FRESH_READ, CONFIG_GUESS, STATUS_PENDING, STATUS_ERROR } from "./core/constants.js";
6
6
 
7
7
  import { computed, signal, runWithOwner, setMemo, setSignal, markRefresh, read, untrack } from "./core/core.js";
8
8
 
9
9
  import { registerGraph } from "./core/dev.js";
10
10
 
11
- import { globalQueue, flushTransaction, resolveTx, passTx, staleReader, dirtyQueue, joinFuture } from "./core/scheduler.js";
11
+ import { globalQueue, flushTransaction, dirtyQueue, GlobalQueue, resolveTx, passTx, staleReader, joinFuture } from "./core/scheduler.js";
12
12
 
13
13
  import { cleanup, createRoot, getOwner, dispose } from "./core/owner.js";
14
14
 
15
15
  import { effect, trackedEffect } from "./core/effect.js";
16
16
 
17
- import { optimisticWrite } from "./core/lanes.js";
17
+ import { optimisticWrite, truthRead, truthConfirmed } from "./core/lanes.js";
18
18
 
19
19
  /**
20
20
  * Low-level reactive-cleanup primitive. Registers a callback that runs when
@@ -73,7 +73,7 @@ function accessor(e) {
73
73
  function createSignal(e, t) {
74
74
  if (typeof e === "function") {
75
75
  const r = computed(e, t);
76
- r.u &= ~CONFIG_AUTO_DISPOSE;
76
+ r.i &= ~CONFIG_AUTO_DISPOSE;
77
77
  return [ accessor(r), setMemo.bind(null, r) ];
78
78
  }
79
79
  const r = signal(e, t);
@@ -84,7 +84,7 @@ function createSignal(e, t) {
84
84
  function createOptimistic(e, t) {
85
85
  if (typeof e === "function") {
86
86
  const r = computed(e, t);
87
- r.u &= ~CONFIG_AUTO_DISPOSE;
87
+ r.i &= ~CONFIG_AUTO_DISPOSE;
88
88
  return [ accessor(r), optimisticWrite.bind(null, r) ];
89
89
  }
90
90
  const r = signal(e, t);
@@ -324,11 +324,15 @@ function createMemo(e, t) {
324
324
  * - a mainline reader whose pass read a frame another transaction holds
325
325
  * joined it (L2, born held — A29) and delivers at that commit: the
326
326
  * committed view, never a foreign hold's unrevealed frame. */ function watch(e, t, r, n = 0, o, c = flushTransaction) {
327
+ if (n & CONFIG_AUTHORITATIVE) {
328
+ GlobalQueue.Nn = truthRead;
329
+ GlobalQueue.rn = truthConfirmed;
330
+ }
327
331
  // `config` is on the node for its first pass too (the pass runs inside
328
332
  // `computed()`): `until`'s predicate is authoritative from its first read,
329
333
  // or an already-displayed guess would satisfy it in a later slice. (REDERIVE
330
334
  // after: the first pass's own error is the promise's rejection.)
331
- computed(() => {
335
+ computed(() => {
332
336
  let n;
333
337
  try {
334
338
  n = e();
@@ -337,18 +341,18 @@ function createMemo(e, t) {
337
341
  queueMicrotask(() => r(unwrapStatusError(e)));
338
342
  return;
339
343
  }
340
- const i = getOwner();
344
+ const u = getOwner();
341
345
  // (Outside a flush a mainline pass joins through `passTx`.)
342
- const u = flushTransaction ?? passTx;
343
- if (i.R & REACTIVE_JOINED && u !== null && (c === null || resolveTx(c) !== resolveTx(u))) {
344
- staleReader(i, resolveTx(u));
346
+ const i = flushTransaction ?? passTx;
347
+ if (u.R & REACTIVE_JOINED && i !== null && (c === null || resolveTx(c) !== resolveTx(i))) {
348
+ staleReader(u, resolveTx(i));
345
349
  return;
346
350
  }
347
351
  o?.(n);
348
352
  queueMicrotask(() => t(n));
349
353
  }, {
350
- dt: n
351
- }).u |= CONFIG_REDERIVE;
354
+ Ct: n
355
+ }).i |= CONFIG_REDERIVE;
352
356
  }
353
357
 
354
358
  /**
@@ -434,23 +438,29 @@ function createMemo(e, t) {
434
438
  const r = flushTransaction;
435
439
  const n = new Promise((n, o) => {
436
440
  queueMicrotask(() => {
437
- // No createRoot: the microtask has no ambient owner, so the watcher is
438
- // naturally detached, and settle disposes the node directly (dev keeps
439
- // a root husk purely to stay quiet about the missing owner). The
440
- // capture runs inside the watcher's own pass, where the ambient owner
441
- // IS the watcher — exactly what dispose() takes.
441
+ // The root is load-bearing: an ownerless computed is auto-disposing,
442
+ // and the settle walk releases an unobserved one instead of re-running
443
+ // it, so the waiter would never see the refetch land (#3888). Settle
444
+ // disposes the waiter directly; the capture runs inside the watcher's
445
+ // own pass, where the ambient owner IS the watcher — exactly what
446
+ // dispose() takes.
442
447
  let c = null;
443
448
  const make = () => watch(() => {
444
449
  if (c === null) c = getOwner();
445
- return read(t);
450
+ // Authoritative: the truth beneath a guess (`until`'s view). A
451
+ // guessed node's own re-ask is not answered by it (#3895): park
452
+ // on that flight, or reject with its error.
453
+ const e = read(t);
454
+ if (t.i & CONFIG_GUESS && t.S & (STATUS_PENDING | STATUS_ERROR)) throw t.m?.F;
455
+ return e;
446
456
  }, t => {
447
457
  n(typeof e === "function" ? t : e);
448
458
  dispose(c);
449
459
  }, e => {
450
460
  o(e);
451
461
  dispose(c);
452
- }, CONFIG_FRESH_READ, undefined, r);
453
- make();
462
+ }, CONFIG_FRESH_READ | CONFIG_AUTHORITATIVE, undefined, r);
463
+ createRoot(make);
454
464
  });
455
465
  });
456
466
  // Fire-and-forget refresh must not turn a failed refetch into an unhandled
@@ -499,14 +509,14 @@ function createMemo(e, t) {
499
509
  // (`saving=true` beside the confirmed `version=1` is a tear no timeline
500
510
  // contains). Updates that do not flip the predicate reveal freely.
501
511
  const c = flushTransaction;
502
- createRoot(i => {
503
- let u;
512
+ createRoot(u => {
513
+ let i;
504
514
  let s;
505
515
  const settle = e => {
506
- if (u !== undefined) clearTimeout(u);
516
+ if (i !== undefined) clearTimeout(i);
507
517
  if (s !== undefined) o.removeEventListener("abort", s);
508
518
  e();
509
- i();
519
+ u();
510
520
  };
511
521
  // Authoritative: the predicate sees the truth, never the action's own
512
522
  // optimistic guess — a guess cannot satisfy the wait for its own
@@ -519,9 +529,9 @@ function createMemo(e, t) {
519
529
  // The flip, inside the pass of the flush carrying it: the flush
520
530
  // joins the action's transaction, and what it staged is held and
521
531
  // revealed with the action.
522
- if (e && c !== null && globalQueue.ee && resolveTx(c).tt !== 0) joinFuture(c);
532
+ if (e && c !== null && globalQueue.X && resolveTx(c).tt !== 0) joinFuture(c);
523
533
  });
524
- if (t?.timeout !== undefined) u = setTimeout(() => settle(() => n(new TimeoutError)), t.timeout);
534
+ if (t?.timeout !== undefined) i = setTimeout(() => settle(() => n(new TimeoutError)), t.timeout);
525
535
  if (o !== undefined) {
526
536
  s = () => settle(() => n(o.reason));
527
537
  o.addEventListener("abort", s, {
@@ -627,7 +637,7 @@ function createMemo(e, t) {
627
637
  * on owner disposal
628
638
  */ function onSettled(e) {
629
639
  const t = getOwner();
630
- t && !(t.u & CONFIG_CHILDREN_FORBIDDEN) ? trackedEffect(() => untrack(e), {
640
+ t && !(t.i & CONFIG_CHILDREN_FORBIDDEN) ? trackedEffect(() => untrack(e), {
631
641
  name: "onSettled"
632
642
  }) : globalQueue.enqueue(EFFECT_USER, function fire() {
633
643
  // Settled means derived. A settle that reverts optimism (or replays
@@ -1,10 +1,10 @@
1
1
  import { GlobalQueue } from "../core/scheduler.js";
2
2
 
3
- import { installAffectsHooks, getNode, optHooks } from "./store.js";
3
+ import { optHooks, installAffectsHooks } from "./hooks.js";
4
4
 
5
5
  import { lookupTarget, $OWNER } from "./target.js";
6
6
 
7
- import { isWrappable, $TARGET } from "./types.js";
7
+ import { $TARGET } from "./types.js";
8
8
 
9
9
  /**
10
10
  * Store — the store half of `affects()` (plan sec. 29, plan sec. 37; A24 (4), #2882,
@@ -23,113 +23,120 @@ import { isWrappable, $TARGET } from "./types.js";
23
23
  * the verdict. A mark declared on a draft (inside a setter) or over an
24
24
  * optimistic family walks the view the writer sees.
25
25
  *
26
- * Installed by `store/index.ts`: a program with stores carries it; one
27
- * without pays nothing (affects.ts asks `GlobalQueue._storeMarks`).
26
+ * Imported by `affects()`, which installs it on its first store target
27
+ * (#3891, after #3912): a program with stores that never declares a mark
28
+ * carries none of it. It never imports store.ts — the engine's top level —
29
+ * so a program declaring marks without stores carries this module alone:
30
+ * the store's nodes and its wrappability test come through `GlobalQueue`
31
+ * (`_storeNode`, `_storeWrappable`), and node birth and the untracked traps
32
+ * reach back through `affectsHooks` (hooks.ts).
28
33
  */
29
34
  /** The keyless mark's carrier key: a leaf of the record no read serves. */ const $AFFECTS = Symbol(0);
30
35
 
31
36
  const scopes = new Map;
32
37
 
38
+ function scopeOf(e, o) {
39
+ let n = scopes.get(e);
40
+ if (n === undefined) scopes.set(e, n = {
41
+ eo: new Set,
42
+ oo: [],
43
+ bt: o
44
+ });
45
+ return n;
46
+ }
47
+
33
48
  /** The nodes an `affects(store[, key])` declaration marks (affects.ts
34
- * registers each). */ function storeMarks(e, n) {
35
- if (n === undefined) {
36
- const n = getNode(e, $AFFECTS);
37
- let o = scopes.get(n);
38
- if (o === undefined) scopes.set(n, o = {
39
- scope: new Set,
40
- inherited: []
41
- });
42
- const t = [ n ];
43
- walk(e.px, o, t, e.fam, new Set);
44
- return t;
49
+ * registers each). */ function storeMarks(e, o) {
50
+ const n = GlobalQueue.no(e, o === undefined ? $AFFECTS : o);
51
+ const t = scopeOf(n, o);
52
+ if (o === undefined) {
53
+ const o = [ n ];
54
+ walk(e.px, t, o, e.fam, new Set);
55
+ return o;
45
56
  }
46
- const o = e.n?.[n] ?? getNode(e, n);
47
- let t = scopes.get(o);
48
- if (t === undefined) scopes.set(o, t = {
49
- scope: new Set,
50
- inherited: [],
51
- key: n
52
- });
53
- t.scope.add(e.v);
54
- if (e.pb !== null) t.scope.add(e.pb);
55
- return [ o ];
57
+ t.eo.add(e.v);
58
+ if (e.pb !== null) t.eo.add(e.pb);
59
+ return [ n ];
56
60
  }
57
61
 
58
62
  /** The identities reachable from `value` into the scope (the draft's and
59
63
  * the committed backing both, when a draft is open; an optimistic family's
60
64
  * view), every live node under each record into `found`. Untracked by
61
- * construction: raws, never traps. */ function walk(e, n, o, t, s) {
62
- if (!isWrappable(e)) return;
65
+ * construction: raws, never traps. */ function walk(e, o, n, t, s) {
66
+ if (!GlobalQueue.so(e)) return;
63
67
  const f = e[$TARGET] ?? lookupTarget(e, t);
64
- let i = f !== undefined ? f.pb ?? f.v : e;
65
- if (s.has(i)) return;
66
- s.add(i);
67
- n.scope.add(i);
68
+ let r = f !== undefined ? f.pb ?? f.v : e;
69
+ if (s.has(r)) return;
70
+ s.add(r);
71
+ o.eo.add(r);
68
72
  if (f !== undefined) {
69
- if (f.pb !== null) n.scope.add(f.v);
73
+ if (f.pb !== null) o.eo.add(f.v);
70
74
  // The writer's view: the tick's own unflushed guesses are in motion too.
71
- if (f.fam?.opt === true) i = optHooks.view(f, i, true);
75
+ if (f.fam?.opt === true) r = optHooks.view(f, r, true);
72
76
  const e = f.n;
73
- if (e !== null) for (const n of Reflect.ownKeys(e))
77
+ if (e !== null) for (const o of Reflect.ownKeys(e))
74
78
  // Another mark's carrier is its own channel: counting it here would
75
79
  // extend that scope's lifetime to this declaration's.
76
- if (n !== $AFFECTS) o.push(e[n]);
80
+ if (o !== $AFFECTS) n.push(e[o]);
77
81
  const s = f.h;
78
- if (s !== null) for (const e of Reflect.ownKeys(s)) o.push(s[e]);
79
- if (f.k !== null) o.push(f.k);
80
- if (f.dk !== null) o.push(f.dk);
82
+ if (s !== null) for (const e of Reflect.ownKeys(s)) n.push(s[e]);
83
+ if (f.k !== null) n.push(f.k);
84
+ if (f.dk !== null) n.push(f.dk);
81
85
  t = f.fam ?? t;
82
86
  }
83
- for (const e of Reflect.ownKeys(i)) {
84
- if (e === $OWNER) continue;
85
- const f = Object.getOwnPropertyDescriptor(i, e);
86
- if (f === undefined || f.get !== undefined) continue;
87
- walk(f.value, n, o, t, s);
87
+ for (const e of Reflect.ownKeys(r)) if (e !== $OWNER) {
88
+ const f = Object.getOwnPropertyDescriptor(r, e);
89
+ if (f.get === undefined) walk(f.value, o, n, t, s);
88
90
  }
89
91
  }
90
92
 
91
93
  /** A live scope covering `t`'s identity (keyed: for `key`). Chained
92
94
  * backings (§7b): a view's backing is another store's proxy — marks cover
93
- * the BASE raw, so every identity along the chain is checked. */ function covering(e, n, o) {
95
+ * the BASE raw, so every identity along the chain is checked. */ function covering(e, o, n) {
94
96
  for (const [t, s] of scopes) {
95
- if (t === o || s.key !== undefined && s.key !== n) continue;
96
- for (let n = e.v; ;) {
97
- if (s.scope.has(n)) return t;
98
- const e = n?.[$TARGET];
97
+ if (t === n || s.bt !== undefined && s.bt !== o) continue;
98
+ for (let o = e.v; ;) {
99
+ if (s.eo.has(o)) return t;
100
+ const e = o?.[$TARGET];
99
101
  if (e === undefined) break;
100
- const o = e.pb ?? e.v;
101
- if (o === n) break;
102
- n = o;
102
+ const n = e.pb ?? e.v;
103
+ if (n === o) break;
104
+ o = n;
103
105
  }
104
106
  }
105
107
  return null;
106
108
  }
107
109
 
108
- installAffectsHooks({
110
+ /** The store's side: node birth and the untracked traps call it (store.ts,
111
+ * through `affectsHooks`) — with no live mark too: an empty scope map is
112
+ * no work. */ const hooks = {
109
113
  // A node born on a record a live mark covers inherits it (released with
110
114
  // the carrier's last registration).
111
- born(e, n, o) {
112
- if (o === $AFFECTS) return;
113
- const t = covering(e, o, n);
114
- if (t === null) return;
115
- GlobalQueue.h(n);
116
- scopes.get(t).inherited.push(n);
115
+ fo(e, o, n) {
116
+ if (scopes.size === 0 || n === $AFFECTS) return;
117
+ const t = covering(e, n, o);
118
+ if (!t) return;
119
+ GlobalQueue.O(o);
120
+ scopes.get(t).oo.push(o);
117
121
  },
118
122
  // An untracked probe (`isPending(() => s.x)` with no observer) reading a
119
123
  // record a live mark covers: no node carries the mark for it — the
120
124
  // verdict is told directly.
121
- witness(e, n) {
122
- const o = e.n?.[$AFFECTS];
123
- if (o !== undefined && o.m !== null && o.m.T !== 0 || covering(e, n) !== null) GlobalQueue.Bt();
125
+ ro(e, o) {
126
+ if (e.n?.[$AFFECTS]?.m?.T || covering(e, o)) GlobalQueue.Bt();
124
127
  }
125
- });
128
+ };
126
129
 
127
- GlobalQueue.O = storeMarks;
130
+ /** `affects()` calls it before a store-targeted declaration (idempotent). */ function installStoreAffects() {
131
+ installAffectsHooks(hooks);
132
+ }
128
133
 
129
134
  /** The carrier's last mark released (affects.ts): its scope dies, and the
130
- * nodes that inherited the mark release theirs. */ GlobalQueue.N = e => {
131
- const n = scopes.get(e);
132
- if (n === undefined) return;
135
+ * nodes that inherited the mark release theirs. */ function releaseMarkScope(e) {
136
+ const o = scopes.get(e);
137
+ if (o === undefined) return;
133
138
  scopes.delete(e);
134
- GlobalQueue.A(n.inherited);
135
- };
139
+ GlobalQueue.k(o.oo);
140
+ }
141
+
142
+ export { installStoreAffects, releaseMarkScope, storeMarks };
@@ -0,0 +1,13 @@
1
+ let optHooks = null;
2
+
3
+ function installOptHooks(o) {
4
+ optHooks = o;
5
+ }
6
+
7
+ let affectsHooks = null;
8
+
9
+ function installAffectsHooks(o) {
10
+ affectsHooks = o;
11
+ }
12
+
13
+ export { affectsHooks, installAffectsHooks, installOptHooks, optHooks };
@@ -2,8 +2,6 @@ import { createStore as createStore$1, deep as deep$1, snapshot as snapshot$1 }
2
2
 
3
3
  export { storeHasFamily, storeHasOptimisticFamily, storeIsShallow } from "./store.js";
4
4
 
5
- import "./affects.js";
6
-
7
5
  export { createOptimisticStore } from "./optimistic.js";
8
6
 
9
7
  import { createStoreDerived } from "./projection.js";
@@ -10,7 +10,9 @@ import { GlobalQueue, txOf, list, insertSubs, laneDirty, notifyEpoch, schedule }
10
10
 
11
11
  import { runProjectionComputed } from "./projection.js";
12
12
 
13
- import { wrap, nameStore, storeSetter, LaneView, committed, getNode, unwrapValue, sameKey, installOptHooks, resolveChainedRaw, getHasNode, targetsEqual, getContainerNode, cloneRaw } from "./store.js";
13
+ import { wrap, nameStore, storeSetter, LaneView, committed, getNode, unwrapValue, sameKey, resolveChainedRaw, getHasNode, targetsEqual, getContainerNode, cloneRaw } from "./store.js";
14
+
15
+ import { installOptHooks } from "./hooks.js";
14
16
 
15
17
  import { $OWNER } from "./target.js";
16
18
 
@@ -79,7 +81,7 @@ function notifyOptimisticWrites(e, n) {
79
81
  // inner store's truth for its key before guessing over it.
80
82
  const o = e.ch ? e.v : null;
81
83
  const guess = (e, n, i, r) => {
82
- if (o !== null) e.pe = i ? n in t : unwrapValue(t[n]);
84
+ if (o !== null) e.Ae = i ? n in t : unwrapValue(t[n]);
83
85
  // By value: a function leaf is a value, not an updater (#3017).
84
86
  optimisticWrite(e, () => r);
85
87
  };
@@ -157,9 +159,40 @@ function rowSame(e, n, t) {
157
159
  if (t === null || !isWrappable(e) || !isWrappable(n)) return false;
158
160
  const i = t(unwrapValue(e));
159
161
  const r = t(unwrapValue(n));
162
+ // Keyless rows are identified by position — reconcile's fallback when the
163
+ // key is missing. The caller already paired these by index, so two keyless
164
+ // rows are the same row and the landing confirms the arrangement instead of
165
+ // dissolving it. Dissolving held an external echo under the action, and a
166
+ // mainline `until` then timed out on a source that already contained it
167
+ // (#3898). Keyed rows still match only on the key.
168
+ if (i === undefined && r === undefined) return true;
160
169
  return i !== undefined && sameKey(i, r);
161
170
  }
162
171
 
172
+ /** Whether reconcile would write anything into `a` from `b`: wrappable
173
+ * pairs descend, leaves compare as its writes do. A key mismatch is a
174
+ * replacement, which a content difference already says. Plain data — the
175
+ * server's echo of a guessed row is a new object with the same contents. */ function echoes(e, n) {
176
+ if (e === n) return true;
177
+ if (!isWrappable(e) || !isWrappable(n)) return false;
178
+ e = unwrapValue(e);
179
+ n = unwrapValue(n);
180
+ const t = Object.keys(e);
181
+ if (Array.isArray(e) !== Array.isArray(n) || t.length !== Object.keys(n).length) return false;
182
+ for (const i of t) if (!hasOwn.call(n, i) || !echoes(e[i], n[i])) return false;
183
+ return true;
184
+ }
185
+
186
+ // A cycle (a row that references itself) is not plain data: no match, so
187
+ // the landing stays a correction.
188
+ GlobalQueue.sn = (e, n) => {
189
+ try {
190
+ return echoes(e, n);
191
+ } catch {
192
+ return false;
193
+ }
194
+ };
195
+
163
196
  function arrangementEqual(e, n, t) {
164
197
  if (Array.isArray(e) !== Array.isArray(n)) return false;
165
198
  if (Array.isArray(e)) {
@@ -233,10 +266,10 @@ function arrangementEqual(e, n, t) {
233
266
  * node is born for it as the lane's derivation), on `length` only when the
234
267
  * committed length differs, and as the `LaneView`'s base for structural
235
268
  * reads. Unkeyed families compose
236
- * positionally — the guess stands as written (the documented residue). */ GlobalQueue.In = (e, n) => {
269
+ * positionally — the guess stands as written (the documented residue). */ GlobalQueue._n = (e, n) => {
237
270
  const t = e.yt;
238
271
  if (t === undefined || e.bt !== $TRACK) return;
239
- const i = e.m.Z;
272
+ const i = e.m.B;
240
273
  if (!(i instanceof LaneView)) return;
241
274
  if (!Array.isArray(n) || !Array.isArray(i.rows)) {
242
275
  i.base = n;
@@ -262,15 +295,15 @@ function arrangementEqual(e, n, t) {
262
295
  s.add(t);
263
296
  } else a.push(n);
264
297
  }
265
- const l = i.removed;
298
+ const u = i.removed;
266
299
  for (let e = 0; e < n.length; e++) {
267
300
  const t = n[e];
268
301
  const i = isWrappable(t) ? r(t) : undefined;
269
- if (i !== undefined && !s.has(i) && (l === null || !l.has(i))) a.push(t);
302
+ if (i !== undefined && !s.has(i) && (u === null || !u.has(i))) a.push(t);
270
303
  }
271
304
  i.rows = a;
272
305
  i.base = a;
273
- const u = txOf(e);
306
+ const l = txOf(e);
274
307
  const c = committed(t);
275
308
  const d = t.n;
276
309
  for (let e = 0; e < a.length; e++) {
@@ -279,17 +312,17 @@ function arrangementEqual(e, n, t) {
279
312
  // lane's only where the composed row is another row (by key — the same
280
313
  // key's new object is the same row, its target adopted it).
281
314
  if (n === undefined) {
282
- if (!sameRow(c[e], a[e], r)) laneSet(getNode(t, e), a[e], u);
283
- } else if (!sameRow(n.u & CONFIG_OVERRIDE ? n.m.Z : c[e], a[e], r)) laneSet(n, a[e], u);
315
+ if (!sameRow(c[e], a[e], r)) laneSet(getNode(t, e), a[e], l);
316
+ } else if (!sameRow(n.i & CONFIG_OVERRIDE ? n.m.B : c[e], a[e], r)) laneSet(n, a[e], l);
284
317
  }
285
318
  if (d !== null) {
286
319
  for (let e = a.length; ;e++) {
287
320
  const n = d[e];
288
321
  if (n === undefined) break;
289
- laneSet(n, undefined, u);
322
+ laneSet(n, undefined, l);
290
323
  }
291
324
  const e = d.length;
292
- if (e !== undefined && (e.u & CONFIG_OVERRIDE || c.length !== a.length)) laneSet(e, a.length, u);
325
+ if (e !== undefined && (e.i & CONFIG_OVERRIDE || c.length !== a.length)) laneSet(e, a.length, l);
293
326
  }
294
327
  };
295
328
 
@@ -304,11 +337,11 @@ function sameRow(e, n, t) {
304
337
  * node that was not the lane's becomes its derivation (its committed value
305
338
  * untouched beneath; the lane's end commits the latest). */ function laneSet(e, n, t) {
306
339
  const i = ext(e);
307
- if (!(e.u & CONFIG_OVERRIDE)) {
308
- e.u |= CONFIG_OVERRIDE;
340
+ if (!(e.i & CONFIG_OVERRIDE)) {
341
+ e.i |= CONFIG_OVERRIDE;
309
342
  list(e, t);
310
- } else if (i.Z === n) return;
311
- i.Z = n;
343
+ } else if (i.B === n) return;
344
+ i.B = n;
312
345
  if (e.t !== null) {
313
346
  insertSubs(e);
314
347
  laneDirty(e, t);
@@ -354,7 +387,7 @@ function sweepOverlaid() {
354
387
  // The container shows its backing again (a shown arrangement guess
355
388
  // that ended without a landing left its `LaneView` as the value).
356
389
  const t = e.k;
357
- if (t !== null && t.pe instanceof LaneView) t.pe = t.pe.base;
390
+ if (t !== null && t.Ae instanceof LaneView) t.Ae = t.Ae.base;
358
391
  }
359
392
  if (n.size === 0) overlaidFamilies.delete(e);
360
393
  }
@@ -371,8 +404,8 @@ function sweepOverlaid() {
371
404
  if (i !== null) for (const e of Reflect.ownKeys(i)) {
372
405
  const r = i[e];
373
406
  const o = untrack(() => unwrapValue(n[e]));
374
- if (r.tn && r.tn(r.pe, o)) continue;
375
- r.pe = o;
407
+ if (r.en && r.en(r.Ae, o)) continue;
408
+ r.Ae = o;
376
409
  // (The lane's end notified it this epoch when the shown value and the
377
410
  // covered one differed — its readers re-derive already.)
378
411
  if (r.t !== null && r.Pt !== notifyEpoch) {
@@ -384,8 +417,8 @@ function sweepOverlaid() {
384
417
  if (r !== null) for (const e of Reflect.ownKeys(r)) {
385
418
  const i = r[e];
386
419
  const o = untrack(() => e in n);
387
- if (i.pe === o) continue;
388
- i.pe = o;
420
+ if (i.Ae === o) continue;
421
+ i.Ae = o;
389
422
  if (i.t !== null && i.Pt !== notifyEpoch) {
390
423
  insertSubs(i);
391
424
  t = true;
@@ -394,9 +427,9 @@ function sweepOverlaid() {
394
427
  // The arrangement the readers saw (the shown guess) against the inner's
395
428
  // now: a structural reader re-derives when they differ.
396
429
  const o = e.k;
397
- if (o !== null && o.t !== null && o.pe instanceof LaneView && o.Pt !== notifyEpoch) {
430
+ if (o !== null && o.t !== null && o.Ae instanceof LaneView && o.Pt !== notifyEpoch) {
398
431
  const i = Array.isArray(n) ? untrack(() => n.map(unwrapValue)) : n;
399
- if (!arrangementEqual(o.pe.rows, i, e.fam?.key ?? null)) {
432
+ if (!arrangementEqual(o.Ae.rows, i, e.fam?.key ?? null)) {
400
433
  insertSubs(o);
401
434
  t = true;
402
435
  }
@@ -405,12 +438,12 @@ function sweepOverlaid() {
405
438
  }
406
439
 
407
440
  function carriesLane(e) {
408
- if (e.k !== null && e.k.u & CONFIG_OVERRIDE) return true;
409
- if (e.dk !== null && e.dk.u & CONFIG_OVERRIDE) return true;
441
+ if (e.k !== null && e.k.i & CONFIG_OVERRIDE) return true;
442
+ if (e.dk !== null && e.dk.i & CONFIG_OVERRIDE) return true;
410
443
  const n = e.n;
411
- if (n !== null) for (const e of Reflect.ownKeys(n)) if (n[e].u & CONFIG_OVERRIDE) return true;
444
+ if (n !== null) for (const e of Reflect.ownKeys(n)) if (n[e].i & CONFIG_OVERRIDE) return true;
412
445
  const t = e.h;
413
- if (t !== null) for (const e of Reflect.ownKeys(t)) if (t[e].u & CONFIG_OVERRIDE) return true;
446
+ if (t !== null) for (const e of Reflect.ownKeys(t)) if (t[e].i & CONFIG_OVERRIDE) return true;
414
447
  return false;
415
448
  }
416
449
 
@@ -428,9 +461,9 @@ function overlaid(e) {
428
461
  * the backing answers. */ function optimisticHas(e, n) {
429
462
  if (!overlaid(e)) return undefined;
430
463
  const t = e.h?.[n];
431
- if (t !== undefined && t.u & CONFIG_OVERRIDE) return !!shown(t);
464
+ if (t !== undefined && t.i & CONFIG_OVERRIDE) return !!shown(t);
432
465
  const i = e.n?.[n];
433
- if (i !== undefined && i.u & CONFIG_OVERRIDE) return shown(i) !== undefined;
466
+ if (i !== undefined && i.i & CONFIG_OVERRIDE) return shown(i) !== undefined;
434
467
  return undefined;
435
468
  }
436
469
 
@@ -441,7 +474,7 @@ function overlaid(e) {
441
474
  if (t === undefined) return undefined;
442
475
  if (!t) return null;
443
476
  const i = e.n?.[n];
444
- const r = i !== undefined && i.u & CONFIG_OVERRIDE ? shown(i) : e.v[n];
477
+ const r = i !== undefined && i.i & CONFIG_OVERRIDE ? shown(i) : e.v[n];
445
478
  // An array's `length` is reported as the array has it (non-configurable).
446
479
  if (n === "length" && Array.isArray(e.v)) return {
447
480
  value: r,
@@ -474,7 +507,7 @@ i = false) {
474
507
  for (const e of Reflect.ownKeys(o)) {
475
508
  const r = o[e];
476
509
  let s;
477
- if (r.u & CONFIG_OVERRIDE) s = t(r); else if (!i || (s = pendingGuessOf(r)) === NOT_PENDING) continue;
510
+ if (r.i & CONFIG_OVERRIDE) s = t(r); else if (!i || (s = pendingGuessOf(r)) === NOT_PENDING) continue;
478
511
  if (e === "length" && Array.isArray(n)) {
479
512
  if (n.length !== s) ensure().length = s;
480
513
  } else if (!isEqual(n[e], s) || !hasOwn.call(n, e)) {
@@ -487,10 +520,10 @@ i = false) {
487
520
  for (const e of Reflect.ownKeys(s)) {
488
521
  const o = s[e];
489
522
  let a;
490
- if (o.u & CONFIG_OVERRIDE) a = t(o); else if (!i || (a = pendingGuessOf(o)) === NOT_PENDING) continue;
523
+ if (o.i & CONFIG_OVERRIDE) a = t(o); else if (!i || (a = pendingGuessOf(o)) === NOT_PENDING) continue;
491
524
  const f = !!a;
492
- const l = r ?? n;
493
- if (!f && e in l) delete ensure()[e];
525
+ const u = r ?? n;
526
+ if (!f && e in u) delete ensure()[e];
494
527
  }
495
528
  }
496
529
  return r ?? n;
@@ -529,7 +562,7 @@ function createOptimisticStore(e, n, t) {
529
562
  if (!a.node) a.node = getOwner();
530
563
  runProjectionComputed(f, n, s);
531
564
  }, t);
532
- i.u &= ~CONFIG_AUTO_DISPOSE;
565
+ i.i &= ~CONFIG_AUTO_DISPOSE;
533
566
  a.node = i;
534
567
  }
535
568
  return [ f, e => storeSetter(f, e) ];