@solidjs/signals 2.0.0-rc.3 → 2.0.0-rc.4

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 (48) hide show
  1. package/dist/dev.js +1334 -94
  2. package/dist/node.cjs +2589 -1367
  3. package/dist/prod/affects.js +13 -12
  4. package/dist/prod/boundaries.js +39 -34
  5. package/dist/prod/core/action.js +3 -3
  6. package/dist/prod/core/async.js +48 -46
  7. package/dist/prod/core/core.js +99 -67
  8. package/dist/prod/core/effect.js +25 -28
  9. package/dist/prod/core/external.js +2 -2
  10. package/dist/prod/core/graph.js +85 -49
  11. package/dist/prod/core/heap.js +10 -10
  12. package/dist/prod/core/lanes.js +19 -19
  13. package/dist/prod/core/optimistic.js +36 -33
  14. package/dist/prod/core/owner.js +13 -13
  15. package/dist/prod/core/scheduler.js +131 -85
  16. package/dist/prod/core/verdict.js +29 -15
  17. package/dist/prod/index.js +4 -0
  18. package/dist/prod/map.js +101 -101
  19. package/dist/prod/signals.js +1 -1
  20. package/dist/prod/store/index.js +2 -0
  21. package/dist/prod/store/next/optimistic.js +65 -11
  22. package/dist/prod/store/next/patch-hooks.js +13 -0
  23. package/dist/prod/store/next/patch.js +614 -0
  24. package/dist/prod/store/next/reconcile.js +307 -120
  25. package/dist/prod/store/next/store.js +321 -92
  26. package/dist/prod/store/next/target.js +13 -4
  27. package/dist/prod/store/store.js +5 -5
  28. package/dist/types/core/core.d.ts +15 -1
  29. package/dist/types/core/dev.d.ts +8 -0
  30. package/dist/types/core/graph.d.ts +22 -0
  31. package/dist/types/core/scheduler.d.ts +12 -0
  32. package/dist/types/store/index.d.ts +2 -0
  33. package/dist/types/store/next/patch-hooks.d.ts +41 -0
  34. package/dist/types/store/next/patch.d.ts +91 -0
  35. package/dist/types/store/next/reconcile.d.ts +14 -0
  36. package/dist/types/store/next/store.d.ts +30 -2
  37. package/dist/types/store/next/target.d.ts +58 -8
  38. package/dist/types-cjs/core/core.d.cts +15 -1
  39. package/dist/types-cjs/core/dev.d.cts +8 -0
  40. package/dist/types-cjs/core/graph.d.cts +22 -0
  41. package/dist/types-cjs/core/scheduler.d.cts +12 -0
  42. package/dist/types-cjs/store/index.d.cts +2 -0
  43. package/dist/types-cjs/store/next/patch-hooks.d.cts +41 -0
  44. package/dist/types-cjs/store/next/patch.d.cts +91 -0
  45. package/dist/types-cjs/store/next/reconcile.d.cts +14 -0
  46. package/dist/types-cjs/store/next/store.d.cts +30 -2
  47. package/dist/types-cjs/store/next/target.d.cts +58 -8
  48. package/package.json +2 -2
@@ -1,4 +1,4 @@
1
- import { CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE, STATUS_PENDING, REACTIVE_RECOMPUTING_DEPS } from "./constants.js";
1
+ import { CONFIG_AUTO_DISPOSE, STATUS_PENDING, REACTIVE_DISPOSED, REACTIVE_ZOMBIE, REACTIVE_RECOMPUTING_DEPS } from "./constants.js";
2
2
 
3
3
  import { deleteFromHeap, queueFor } from "./heap.js";
4
4
 
@@ -8,36 +8,36 @@ import { bumpNotifyEpoch } from "./scheduler.js";
8
8
 
9
9
  // https://github.com/stackblitz/alien-signals/blob/v2.0.3/src/system.ts#L100
10
10
  function unlinkSubs(e) {
11
- const l = e.ut;
12
- const n = e.it;
13
- const u = e.fe;
14
- const s = e.el;
15
- if (u !== null) u.el = s; else l.rt = s;
16
- if (s !== null) s.fe = u; else {
17
- l.u = u;
18
- if (u === null) {
19
- l.o?.ft?.();
11
+ const n = e.ut;
12
+ const l = e.it;
13
+ const o = e.ae;
14
+ const u = e.en;
15
+ if (o !== null) o.en = u; else n.rt = u;
16
+ if (u !== null) u.ae = o; else {
17
+ n.u = o;
18
+ if (o === null) {
19
+ n.o?.ft?.();
20
20
  // No more subscribers; only tear down if CONFIG_AUTO_DISPOSE is set.
21
21
  // A pending node is exempt: its in-flight async work (or the
22
22
  // transition holding it) is an observer — tearing down would orphan
23
23
  // the work and re-execute it on the next read. The settle path runs
24
24
  // this same last-one-out check when that observer releases (the
25
- // untracked-read dispose in core.ts guards on pending identically).
26
- const e = l;
27
- e.Se && e.T & CONFIG_AUTO_DISPOSE && !(e.ie & REACTIVE_ZOMBIE) && !(e.S & STATUS_PENDING) && unobserved(e);
25
+ // untracked-read dormancy sweep guards on pending identically).
26
+ const e = n;
27
+ e.oe && e.T & CONFIG_AUTO_DISPOSE && !(e.ie & REACTIVE_ZOMBIE) && !(e.S & STATUS_PENDING) && unobserved(e);
28
28
  }
29
29
  }
30
- return n;
30
+ return l;
31
31
  }
32
32
 
33
33
  function trimStaleDeps(e) {
34
- const l = e.Ye;
35
- let n = l !== null ? l.it : e.nt;
36
- if (n !== null) {
34
+ const n = e.Ye;
35
+ let l = n !== null ? n.it : e.nt;
36
+ if (l !== null) {
37
37
  do {
38
- n = unlinkSubs(n);
39
- } while (n !== null);
40
- if (l !== null) l.it = null; else e.nt = null;
38
+ l = unlinkSubs(l);
39
+ } while (l !== null);
40
+ if (n !== null) n.it = null; else e.nt = null;
41
41
  }
42
42
  }
43
43
 
@@ -46,11 +46,11 @@ function trimStaleDeps(e) {
46
46
  // and skipping early also avoids adding one (hidden-class churn) via the
47
47
  // null-out below.
48
48
  function clearDeps(e) {
49
- let l = e.nt;
50
- if (!l) return;
49
+ let n = e.nt;
50
+ if (!n) return;
51
51
  do {
52
- l = unlinkSubs(l);
53
- } while (l !== null);
52
+ n = unlinkSubs(n);
53
+ } while (n !== null);
54
54
  e.nt = null;
55
55
  e.Ye = null;
56
56
  }
@@ -61,27 +61,63 @@ function unobserved(e) {
61
61
  disposeChildren(e, true);
62
62
  }
63
63
 
64
+ /**
65
+ * Deferred dormancy for never-observed auto-dispose computeds (#3078).
66
+ *
67
+ * An untracked top-level read of a subscriber-less observation-lifecycle memo
68
+ * used to call unobserved() inline at the end of read(). That kept the leak
69
+ * closed (the compute links the memo into its deps' sub lists — without a
70
+ * teardown point a never-observed memo is retained by its sources forever;
71
+ * upstream alien-signals has exactly this retention), but it made reads
72
+ * destructive: each read disposed the node, the next read revived it with a
73
+ * full recompute in whatever ambient transition/lane context happened to be
74
+ * current, so consecutive reads could return different answers with no write
75
+ * in between.
76
+ *
77
+ * Instead, reads queue the node here and the scheduler sweeps at the top of
78
+ * the next flush (before runHeap, so a same-tick dirtying is reclaimed
79
+ * instead of recomputed). Reads become idempotent within a tick (the node
80
+ * stays alive and serves its cache, uniform with observed memos) while
81
+ * reclamation still happens within one microtask — the enqueue site arms
82
+ * schedule(), so a flush is guaranteed even when no other work is queued.
83
+ */ const dormantNodes = new Set;
84
+
85
+ function sweepDormant() {
86
+ if (dormantNodes.size === 0) return;
87
+ for (const e of dormantNodes) {
88
+ // Re-validate at sweep time: the node may have gained a subscriber (its
89
+ // lifecycle is the unlinkSubs cascade now), gone pending (in-flight async
90
+ // is an observer; the settle path re-runs last-one-out), lost its
91
+ // AUTO_DISPOSE bit (owner teardown strips it, #3024), or already been
92
+ // torn down.
93
+ if (!e.u && e.T & CONFIG_AUTO_DISPOSE && !(e.S & STATUS_PENDING) && !(e.ie & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE))) {
94
+ unobserved(e);
95
+ }
96
+ }
97
+ dormantNodes.clear();
98
+ }
99
+
64
100
  // https://github.com/stackblitz/alien-signals/blob/v2.0.3/src/system.ts#L52
65
- function link(e, l, n = false) {
101
+ function link(e, n, l = false) {
66
102
  // Repeat touches within one pass AND-combine `_pendingObserver`: a probe
67
103
  // read (`isPending(() => x())`) beside a value read of the same dep must
68
104
  // not relabel the value dependency as probe-only — the value read is what
69
105
  // real-error propagation and affects() coverage key off, regardless of
70
106
  // read order within the computation.
71
- const u = l.Ye;
72
- if (u !== null && u.ut === e) {
73
- u.me &&= n;
107
+ const o = n.Ye;
108
+ if (o !== null && o.ut === e) {
109
+ o.me &&= l;
74
110
  return;
75
111
  }
76
- let s = null;
77
- const o = l.ie & REACTIVE_RECOMPUTING_DEPS;
78
- if (o) {
79
- s = u !== null ? u.it : l.nt;
80
- if (s !== null && s.ut === e) {
81
- s.ll = l.Ze;
82
- l.Ye = s;
112
+ let u = null;
113
+ const t = n.ie & REACTIVE_RECOMPUTING_DEPS;
114
+ if (t) {
115
+ u = o !== null ? o.it : n.nt;
116
+ if (u !== null && u.ut === e) {
117
+ u.nn = n.Ze;
118
+ n.Ye = u;
83
119
  // First touch of this pass: the previous pass's label is stale.
84
- s.me = n;
120
+ u.me = l;
85
121
  return;
86
122
  }
87
123
  }
@@ -90,26 +126,26 @@ function link(e, l, n = false) {
90
126
  // [deps.._depsTail] prefix — the O(1) equivalent of scanning the dep list
91
127
  // (the old alien-signals `isValidLink` walk, O(n²) when a computation
92
128
  // re-reads earlier deps non-consecutively, e.g. store leaf reads).
93
- const t = e.rt;
94
- if (t !== null && t.ae === l && (!o || t.ll === l.Ze)) {
129
+ const s = e.rt;
130
+ if (s !== null && s.ce === n && (!t || s.nn === n.Ze)) {
95
131
  // Gen-matched during a recompute = repeat touch this pass (AND); outside
96
132
  // a recompute there is no pass boundary, so the latest read labels it.
97
- if (o) t.me &&= n; else t.me = n;
133
+ if (t) s.me &&= l; else s.me = l;
98
134
  return;
99
135
  }
100
- const i = l.Ye = e.rt = {
136
+ const r = n.Ye = e.rt = {
101
137
  ut: e,
102
- ae: l,
103
- it: s,
104
- el: t,
105
- fe: null,
106
- ll: l.Ze,
107
- me: n
138
+ ce: n,
139
+ it: u,
140
+ en: s,
141
+ ae: null,
142
+ nn: n.Ze,
143
+ me: l
108
144
  };
109
- if (u !== null) u.it = i; else l.nt = i;
110
- if (t !== null) t.fe = i; else e.u = i;
145
+ if (o !== null) o.it = r; else n.nt = r;
146
+ if (s !== null) s.ae = r; else e.u = r;
111
147
  // New subscriber edge: staged-rewrite skips (§12d) must not miss it.
112
148
  bumpNotifyEpoch();
113
149
  }
114
150
 
115
- export { clearDeps, link, trimStaleDeps, unlinkSubs, unobserved };
151
+ export { clearDeps, dormantNodes, link, sweepDormant, trimStaleDeps, unlinkSubs, unobserved };
@@ -1,6 +1,6 @@
1
1
  import "./core.js";
2
2
 
3
- import { REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, EFFECT_TRACKED, EFFECT_USER, REACTIVE_RECOMPUTING_DEPS, REACTIVE_MANUAL_WRITE, REACTIVE_ZOMBIE, REACTIVE_CHECK, REACTIVE_DIRTY, CONFIG_FW_CHILDREN } from "./constants.js";
3
+ import { REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, EFFECT_TRACKED, EFFECT_USER, REACTIVE_ZOMBIE, REACTIVE_RECOMPUTING_DEPS, REACTIVE_MANUAL_WRITE, REACTIVE_CHECK, REACTIVE_DIRTY, CONFIG_FW_CHILDREN } from "./constants.js";
4
4
 
5
5
  import { zombieQueue, dirtyQueue } from "./scheduler.js";
6
6
 
@@ -27,7 +27,7 @@ import { zombieQueue, dirtyQueue } from "./scheduler.js";
27
27
  }
28
28
 
29
29
  function actualInsertIntoHeap(e, E) {
30
- const t = (e.ve?.Ct ? e.ve.Ot?.Le : e.ve?.Le) ?? -1;
30
+ const t = (e.ke?.Ct ? e.ke.Ot?.Le : e.ke?.Le) ?? -1;
31
31
  if (t >= e.Le) e.Le = t + 1;
32
32
  const n = e.Le;
33
33
  const I = E.eE[n];
@@ -97,16 +97,16 @@ function markNode(e, E = REACTIVE_DIRTY) {
97
97
  const t = e.ie;
98
98
  if ((t & (REACTIVE_CHECK | REACTIVE_DIRTY)) >= E) return;
99
99
  e.ie = t & -4 | E;
100
- for (let E = e.u; E !== null; E = E.fe) {
101
- markNode(E.ae, REACTIVE_CHECK);
100
+ for (let E = e.u; E !== null; E = E.ae) {
101
+ markNode(E.ce, REACTIVE_CHECK);
102
102
  }
103
103
  // Firewall children (projection machinery only): gate the cold-extension
104
104
  // deref on the config bit — markNode runs per sub edge per write, and an
105
105
  // unconditional _x chase here taxed every propagation (diamond -22%).
106
106
  if (e.T & CONFIG_FW_CHILDREN) {
107
- for (let E = e.o.l; E !== null; E = E.ce) {
108
- for (let e = E.u; e !== null; e = e.fe) {
109
- markNode(e.ae, REACTIVE_CHECK);
107
+ for (let E = e.o.i; E !== null; E = E.Se) {
108
+ for (let e = E.u; e !== null; e = e.ae) {
109
+ markNode(e.ce, REACTIVE_CHECK);
110
110
  }
111
111
  }
112
112
  }
@@ -130,17 +130,17 @@ function adjustHeight(e, E) {
130
130
  for (let E = e.nt; E; E = E.it) {
131
131
  const e = E.ut;
132
132
  const n = e.lt || e;
133
- if (n.Se && n.Le >= t) t = n.Le + 1;
133
+ if (n.oe && n.Le >= t) t = n.Le + 1;
134
134
  }
135
135
  if (e.Le !== t) {
136
136
  e.Le = t;
137
- for (let E = e.u; E !== null; E = E.fe) {
137
+ for (let E = e.u; E !== null; E = E.ae) {
138
138
  // Route each subscriber by its own zombie flag, mirroring the
139
139
  // post-recompute height-adjust path. Inserting into the running `heap`
140
140
  // unconditionally can park a zombie in `dirtyQueue` (or a live node in
141
141
  // `zombieQueue`), breaking the flag/queue invariant `deleteFromHeap`
142
142
  // relies on — the same corruption class as #2759.
143
- insertIntoHeapHeight(E.ae, queueFor(E.ae));
143
+ insertIntoHeapHeight(E.ce, queueFor(E.ce));
144
144
  }
145
145
  }
146
146
  }
@@ -20,16 +20,16 @@ const activeLanes = new Set;
20
20
  }
21
21
  // Detect parent lane: _parentSource chains from pendingSignal → pendingValueComputed → original.
22
22
  // The child lane should not merge with the parent lane.
23
- const i = n.o?.It;
23
+ const i = n.o?.Et;
24
24
  const t = i?.o?.Be;
25
25
  const r = t ? findLane(t) : null;
26
26
  e = {
27
- nn: n,
27
+ tn: n,
28
28
  Ae: new Set,
29
- en: [ [], [] ],
30
- tn: null,
29
+ rn: [ [], [] ],
30
+ an: null,
31
31
  _e: activeTransition,
32
- rn: r
32
+ sn: r
33
33
  };
34
34
  signalLanes.set(n, e);
35
35
  activeLanes.add(e);
@@ -51,13 +51,13 @@ function adoptCompanionLane(n, e) {
51
51
  const t = findLane(i);
52
52
  // Only the companion's own unmerged root is safely re-parentable: a root
53
53
  // that absorbed other lanes carries work that is not a child of this owner.
54
- if (t !== e && t.nn === n && !t.rn) t.rn = e;
54
+ if (t !== e && t.tn === n && !t.sn) t.sn = e;
55
55
  }
56
56
 
57
57
  /**
58
58
  * Union-find: find the root lane.
59
59
  */ function findLane(n) {
60
- while (n.tn) n = n.tn;
60
+ while (n.an) n = n.an;
61
61
  return n;
62
62
  }
63
63
 
@@ -67,16 +67,16 @@ function adoptCompanionLane(n, e) {
67
67
  n = findLane(n);
68
68
  e = findLane(e);
69
69
  if (n === e) return n;
70
- e.tn = n;
70
+ e.an = n;
71
71
  // Move (not copy) the merged lane's work: after the merge all routing goes
72
72
  // through findLane() to the root, so anything left behind here is dead —
73
73
  // and anything *added* here later is a routing bug (INV-5).
74
74
  for (const i of e.Ae) n.Ae.add(i);
75
75
  e.Ae.clear();
76
- n.en[0].push(...e.en[0]);
77
- n.en[1].push(...e.en[1]);
78
- e.en[0].length = 0;
79
- e.en[1].length = 0;
76
+ n.rn[0].push(...e.rn[0]);
77
+ n.rn[1].push(...e.rn[1]);
78
+ e.rn[0].length = 0;
79
+ e.rn[1].length = 0;
80
80
  return n;
81
81
  }
82
82
 
@@ -97,10 +97,10 @@ function resolveTransition(n) {
97
97
  // transactions (#2912) — the merged root's _transition would hand this
98
98
  // node's override to whichever action wrote last through the shared
99
99
  // reader. Chase merge chains; a dead owner settled through another path.
100
- if (hasActiveOverride(n) && n.o?.Et) {
101
- const e = ext(n).Et = currentTransition(n.o?.Et);
102
- if (e.an !== true) return e;
103
- if (n.o !== null) n.o.Et = null;
100
+ if (hasActiveOverride(n) && n.o?.Nt) {
101
+ const e = ext(n).Nt = currentTransition(n.o?.Nt);
102
+ if (e.fn !== true) return e;
103
+ if (n.o !== null) n.o.Nt = null;
104
104
  }
105
105
  return resolveLane(n)?._e ?? n._e;
106
106
  }
@@ -122,7 +122,7 @@ function resolveTransition(n) {
122
122
  // If the subscriber's lane was merged into another lane, it's stale —
123
123
  // replace it with the new source lane instead of following the merge chain
124
124
  // (which would incorrectly merge the new lane into the old group)
125
- if (t.tn) {
125
+ if (t.an) {
126
126
  ext(n).Be = e;
127
127
  n.T |= CONFIG_HAS_LANE;
128
128
  return;
@@ -132,10 +132,10 @@ function resolveTransition(n) {
132
132
  if (r !== i && !hasActiveOverride(n)) {
133
133
  // Parent-child lanes stay independent so isPending resolves without
134
134
  // waiting for the parent's async. The child keeps ownership.
135
- if (i.rn && findLane(i.rn) === r) {
135
+ if (i.sn && findLane(i.sn) === r) {
136
136
  ext(n).Be = e;
137
137
  n.T |= CONFIG_HAS_LANE;
138
- } else if (r.rn && findLane(r.rn) === i) ; else mergeLanes(i, r);
138
+ } else if (r.sn && findLane(r.sn) === i) ; else mergeLanes(i, r);
139
139
  }
140
140
  return;
141
141
  }
@@ -1,4 +1,4 @@
1
- import { NOT_PENDING, unwrapOverride, STATUS_UNINITIALIZED, REACTIVE_DIRTY, REACTIVE_CHECK, CONFIG_HAS_LANE, OVERRIDE_UNDEFINED, STATUS_PENDING, REACTIVE_MANUAL_WRITE, REACTIVE_OPTIMISTIC_DIRTY, EFFECT_RENDER, EFFECT_USER } from "./constants.js";
1
+ import { NOT_PENDING, unwrapOverride, STATUS_UNINITIALIZED, REACTIVE_DIRTY, REACTIVE_CHECK, CONFIG_HAS_LANE, OVERRIDE_UNDEFINED, STATUS_PENDING, EFFECT_RENDER, REACTIVE_MANUAL_WRITE, REACTIVE_OPTIMISTIC_DIRTY, EFFECT_USER } from "./constants.js";
2
2
 
3
3
  import { ext, latestReadActive, currentOptimisticLane, stale } from "./core.js";
4
4
 
@@ -48,11 +48,11 @@ import { GlobalQueue, activeTransition, globalQueue, clock, insertSubs, schedule
48
48
  // No revert target is stashed: while the override is active every reader
49
49
  // sees it (A17), so authoritative arrivals commit silently into _value and
50
50
  // reverting is just dropping the override — _value is already correct.
51
- else globalQueue.k.Ke.push(e);
51
+ else globalQueue.m.Ke.push(e);
52
52
  // Stamp ownership on the node (post-merge, so entangled writers share the
53
53
  // joint root). resolveTransition prefers this over the lane's _transition,
54
54
  // which a shared subscriber can merge across transactions (#2912).
55
- ext(e).Et = activeTransition;
55
+ ext(e).Nt = activeTransition;
56
56
  const l = getOrCreateLane(e);
57
57
  ext(e).Be = l;
58
58
  e.T |= CONFIG_HAS_LANE;
@@ -63,7 +63,7 @@ import { GlobalQueue, activeTransition, globalQueue, clock, insertSubs, schedule
63
63
  // syncCompanions only pokes _pendingSignal/_latestValueComputed — with
64
64
  // neither companion present the call is a guaranteed no-op.
65
65
  (e.o?.Ge !== undefined || e.o?.ge !== undefined) && GlobalQueue.Oe !== null && GlobalQueue.Oe(e, n);
66
- if (e.Se !== undefined) e.Te = clock;
66
+ if (e.oe !== undefined) e.Te = clock;
67
67
  // §12e: computed-only slot
68
68
  insertSubs(e, true);
69
69
  schedule();
@@ -100,7 +100,7 @@ function resolveOptimisticNodes(e) {
100
100
  ext(n).De = NOT_PENDING;
101
101
  if (i !== NOT_PENDING && n.be !== unwrapOverride(i)) insertSubs(n, true);
102
102
  n._e = null;
103
- if (n.o !== null) n.o.Et = null;
103
+ if (n.o !== null) n.o.Nt = null;
104
104
  }
105
105
  // Settlement checkpoint (#2838): companions caught in this batch (or owned
106
106
  // by a node in it) re-derive from committed state, so verdicts survive the
@@ -108,7 +108,7 @@ function resolveOptimisticNodes(e) {
108
108
  for (let t = 0; t < n; t++) {
109
109
  const n = e[t];
110
110
  if (n.o?.Ge || n.o?.ge) GlobalQueue.un(n);
111
- const i = n.o?.It;
111
+ const i = n.o?.Et;
112
112
  if (i && (i.o?.Ge === n || i.o?.ge === n)) GlobalQueue.un(i);
113
113
  }
114
114
  e.splice(0, n);
@@ -122,29 +122,32 @@ function runQueue(e, n) {
122
122
  * Run effects from all lanes that are ready (no pending async).
123
123
  */ function runLaneEffects(e) {
124
124
  for (const n of activeLanes) {
125
- if (n.tn || n.Ae.size > 0) continue;
126
- const t = n.en[e - 1];
125
+ if (n.an || n.Ae.size > 0) continue;
126
+ const t = n.rn[e - 1];
127
127
  if (t.length) {
128
- n.en[e - 1] = [];
128
+ n.rn[e - 1] = [];
129
129
  runQueue(t, e);
130
130
  }
131
131
  }
132
+ // Optimistic patch applications ride the same visibility slot as lane
133
+ // effects (in-flight DOM updates); no-op unless patches registered.
134
+ if (e === EFFECT_RENDER) GlobalQueue.ln?.();
132
135
  }
133
136
 
134
137
  function cleanupCompletedLanes(e) {
135
138
  for (const n of activeLanes) {
136
139
  const t = e ? n._e === e : !n._e;
137
140
  if (!t) continue;
138
- if (!n.tn) {
139
- if (n.en[0].length) runQueue(n.en[0], EFFECT_RENDER);
140
- if (n.en[1].length) runQueue(n.en[1], EFFECT_USER);
141
+ if (!n.an) {
142
+ if (n.rn[0].length) runQueue(n.rn[0], EFFECT_RENDER);
143
+ if (n.rn[1].length) runQueue(n.rn[1], EFFECT_USER);
141
144
  }
142
- if (n.nn.o?.Be === n) if (n.nn.o !== null) n.nn.o.Be = undefined;
145
+ if (n.tn.o?.Be === n) if (n.tn.o !== null) n.tn.o.Be = undefined;
143
146
  n.Ae.clear();
144
- n.en[0].length = 0;
145
- n.en[1].length = 0;
147
+ n.rn[0].length = 0;
148
+ n.rn[1].length = 0;
146
149
  activeLanes.delete(n);
147
- signalLanes.delete(n.nn);
150
+ signalLanes.delete(n.tn);
148
151
  }
149
152
  }
150
153
 
@@ -162,10 +165,10 @@ function cleanupCompletedLanes(e) {
162
165
  * that reads a pending mid-transition write sees the committed value; the sub
163
166
  * is recorded for replay at commit.
164
167
  */ function gatedRead(e, n, t) {
165
- if (latestReadActive || e.Pe === NOT_PENDING || e.Se || n !== e && !(n.ie & REACTIVE_MANUAL_WRITE)) {
168
+ if (latestReadActive || e.Pe === NOT_PENDING || e.oe || n !== e && !(n.ie & REACTIVE_MANUAL_WRITE)) {
166
169
  return false;
167
170
  }
168
- activeTransition.ln.add(t);
171
+ activeTransition.cn.add(t);
169
172
  return true;
170
173
  }
171
174
 
@@ -182,10 +185,10 @@ function cleanupCompletedLanes(e) {
182
185
  // committed value to a reader that never re-ran after the landing, so a
183
186
  // pending-gated branch stayed one value behind permanently (#3041
184
187
  // follow-up). Record the reader under the same replay contract.
185
- if (e.Pe !== NOT_PENDING) (activeTransition ?? globalQueue.k).ln.add(t);
188
+ if (e.Pe !== NOT_PENDING) (activeTransition ?? globalQueue.m).cn.add(t);
186
189
  return true;
187
190
  }
188
- if (n === e && stale && t.o?.It !== e) {
191
+ if (n === e && stale && t.o?.Et !== e) {
189
192
  // The committed view can hide a staged write (a lane member — even just
190
193
  // an isPending companion flip — puts the reader "under a lane"). The
191
194
  // staged value commits with no re-delivery (commitPendingNode never
@@ -197,7 +200,7 @@ function cleanupCompletedLanes(e) {
197
200
  // into the transaction (#3041 follow-up: a pending-gated branch that
198
201
  // first read its async source during the landing flush stayed one value
199
202
  // behind permanently); with none, into the ambient batch.
200
- if (e.Pe !== NOT_PENDING) (activeTransition ?? globalQueue.k).ln.add(t);
203
+ if (e.Pe !== NOT_PENDING) (activeTransition ?? globalQueue.m).cn.add(t);
201
204
  return true;
202
205
  }
203
206
  return false;
@@ -220,7 +223,7 @@ function cleanupCompletedLanes(e) {
220
223
  // recompute() runs plain: the value stages and commits with the flush.
221
224
  // (el's own override slot excludes companions themselves; a lane merged
222
225
  // into a real optimistic lane resolves to a non-companion source.)
223
- if (!globalQueue.sn && !activeTransition && !n._e && n.nn.o?.It !== undefined && e.o?.De === undefined) {
226
+ if (!globalQueue.En && !activeTransition && !n._e && n.tn.o?.Et !== undefined && e.o?.De === undefined) {
224
227
  if (e.o !== null) e.o.Be = undefined;
225
228
  return false;
226
229
  }
@@ -242,11 +245,11 @@ function cleanupCompletedLanes(e) {
242
245
 
243
246
  /** recompute()'s catch path: track pending async in the current lane. */ function laneAsyncPending(e) {
244
247
  const n = findLane(currentOptimisticLane);
245
- if (n.nn !== e) {
248
+ if (n.tn !== e) {
246
249
  n.Ae.add(e);
247
250
  ext(e).Be = n;
248
251
  e.T |= CONFIG_HAS_LANE;
249
- GlobalQueue.de !== null && GlobalQueue.de(n.nn);
252
+ GlobalQueue.de !== null && GlobalQueue.de(n.tn);
250
253
  }
251
254
  }
252
255
 
@@ -254,13 +257,13 @@ function cleanupCompletedLanes(e) {
254
257
  const n = resolveLane(e);
255
258
  if (n) {
256
259
  n.Ae.delete(e);
257
- GlobalQueue.de !== null && GlobalQueue.de(n.nn);
260
+ GlobalQueue.de !== null && GlobalQueue.de(n.tn);
258
261
  }
259
262
  }
260
263
 
261
264
  function trackOptimisticStore(e) {
262
265
  // After initTransition, globalQueue._batch IS activeTransition (same reference)
263
- globalQueue.k.cn.add(e);
266
+ globalQueue.m.Tn.add(e);
264
267
  schedule();
265
268
  }
266
269
 
@@ -269,19 +272,19 @@ function trackOptimisticStore(e) {
269
272
  * create optimistic state (verdict.ts at module top level, createOptimistic
270
273
  * and createOptimisticStore at first call) BEFORE any optimistic node exists.
271
274
  */ function installOptimisticEngine() {
272
- if (GlobalQueue.vt !== null) return;
273
- GlobalQueue.vt = optimisticWrite;
274
- GlobalQueue.fn = resolveOptimisticNodes;
275
- GlobalQueue.En = transitionBlocked;
276
- GlobalQueue.Tn = cleanupCompletedLanes;
277
- GlobalQueue.dn = runLaneEffects;
275
+ if (GlobalQueue.kt !== null) return;
276
+ GlobalQueue.kt = optimisticWrite;
277
+ GlobalQueue.dn = resolveOptimisticNodes;
278
+ GlobalQueue.In = transitionBlocked;
279
+ GlobalQueue.Nn = cleanupCompletedLanes;
280
+ GlobalQueue._n = runLaneEffects;
278
281
  GlobalQueue.Ft = gatedRead;
279
282
  GlobalQueue.ht = laneSuspends;
280
283
  GlobalQueue.gt = laneReadsCommitted;
281
284
  GlobalQueue.je = recomputeLane;
282
285
  GlobalQueue.$e = laneAsyncPending;
283
286
  GlobalQueue.ze = laneAsyncSettled;
284
- GlobalQueue.In = trackOptimisticStore;
287
+ GlobalQueue.An = trackOptimisticStore;
285
288
  }
286
289
 
287
290
  export { installOptimisticEngine };
@@ -1,4 +1,4 @@
1
- import { REACTIVE_DISPOSED, REACTIVE_ZOMBIE, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, CONFIG_TRANSPARENT, defaultContext, CONFIG_AUTO_DISPOSE } from "./constants.js";
1
+ import { REACTIVE_DISPOSED, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, CONFIG_TRANSPARENT, defaultContext } from "./constants.js";
2
2
 
3
3
  import { context, runWithOwner, pendingCheckActive, latestReadActive, tracking } from "./core.js";
4
4
 
@@ -12,7 +12,7 @@ const PENDING_OWNER = {};
12
12
 
13
13
  // Dummy owner to trigger store's read() path
14
14
  function markDisposal(e) {
15
- let t = e.ke;
15
+ let t = e.ve;
16
16
  while (t) {
17
17
  const e = t.ie;
18
18
  t.ie = e | REACTIVE_ZOMBIE;
@@ -54,8 +54,8 @@ function disposeChildren(e, t = false, n) {
54
54
  const t = e;
55
55
  if (t.o?.Ge || t.o?.ge) GlobalQueue.un(t);
56
56
  }
57
- if (t && e.Se && e.o !== null) e.o.Ee = null;
58
- let o = n ? e.o?.qe ?? null : e.ke;
57
+ if (t && e.oe && e.o !== null) e.o.Ee = null;
58
+ let o = n ? e.o?.qe ?? null : e.ve;
59
59
  while (o) {
60
60
  const e = o.Ve;
61
61
  const t = o;
@@ -82,17 +82,17 @@ function disposeChildren(e, t = false, n) {
82
82
  if (n) {
83
83
  if (e.o !== null) e.o.qe = null;
84
84
  } else {
85
- e.ke = null;
85
+ e.ve = null;
86
86
  e.Me = 0;
87
87
  }
88
88
  // O(1) splice out of parent's chain on individual dispose. Skipped during
89
89
  // batch dispose (parent already disposed) and zombie disposal (node sits on
90
90
  // parent's _pendingFirstChild). We leave node._nextSibling intact so outer
91
91
  // walks that already advanced past us still reach later siblings.
92
- if (t && !n && !(i & REACTIVE_ZOMBIE) && e.ve !== null && !(e.ve.ie & REACTIVE_DISPOSED)) {
92
+ if (t && !n && !(i & REACTIVE_ZOMBIE) && e.ke !== null && !(e.ke.ie & REACTIVE_DISPOSED)) {
93
93
  const t = e.ct;
94
94
  const n = e.Ve;
95
- if (t !== null) t.Ve = n; else e.ve.ke = n;
95
+ if (t !== null) t.Ve = n; else e.ke.ve = n;
96
96
  if (n !== null) n.ct = t;
97
97
  e.ct = null;
98
98
  }
@@ -124,7 +124,7 @@ function runDisposal(e, t) {
124
124
 
125
125
  function childId(e, t) {
126
126
  let n = e;
127
- while (n.T & CONFIG_TRANSPARENT && n.ve) n = n.ve;
127
+ while (n.T & CONFIG_TRANSPARENT && n.ke) n = n.ke;
128
128
  if (n.id != null) return formatId(n.id, t ? n.Me++ : n.Me);
129
129
  throw new Error("");
130
130
  }
@@ -248,7 +248,7 @@ function disposeRootSelf(e = true) {
248
248
  T: n ? CONFIG_TRANSPARENT : 0,
249
249
  Ct: true,
250
250
  Ot: t?.Ct ? t.Ot : t,
251
- ke: null,
251
+ ve: null,
252
252
  Ve: null,
253
253
  ct: null,
254
254
  he: null,
@@ -256,17 +256,17 @@ function disposeRootSelf(e = true) {
256
256
  we: t?.we || defaultContext,
257
257
  Me: 0,
258
258
  o: null,
259
- ve: t,
259
+ ke: t,
260
260
  dispose: disposeRootSelf
261
261
  };
262
262
  if (t) {
263
- const e = t.ke;
263
+ const e = t.ve;
264
264
  if (e === null) {
265
- t.ke = i;
265
+ t.ve = i;
266
266
  } else {
267
267
  i.Ve = e;
268
268
  e.ct = i;
269
- t.ke = i;
269
+ t.ve = i;
270
270
  }
271
271
  }
272
272
  return i;