@solidjs/signals 2.0.0-beta.32 → 2.0.0-beta.34

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.
@@ -24,10 +24,10 @@ import { GlobalQueue, activeTransition, globalQueue, clock, insertSubs, schedule
24
24
  * once the gate holds — the same late-binding contract as verdict.ts.
25
25
  */
26
26
  /** The optimistic half of setSignal, fired when `_overrideValue !== undefined`. */ function optimisticWrite(e, n) {
27
- const t = e.Ae !== NOT_PENDING;
28
- const i = t ? unwrapOverride(e.Ae) : e.be;
27
+ const t = e._e !== NOT_PENDING;
28
+ const i = t ? unwrapOverride(e._e) : e.Ue;
29
29
  if (typeof n === "function") n = n(i);
30
- const u = !!(e.S & STATUS_UNINITIALIZED) || !e.he || !e.he(i, n);
30
+ const u = !!(e.S & STATUS_UNINITIALIZED) || !e.be || !e.be(i, n);
31
31
  if (!u) {
32
32
  // Same-value write with an active override still entangles the current
33
33
  // action's transition — the hold must outlast all overlapping actions.
@@ -41,21 +41,21 @@ import { GlobalQueue, activeTransition, globalQueue, clock, insertSubs, schedule
41
41
  // No revert target is stashed: while the override is active every reader
42
42
  // sees it (A17), so authoritative arrivals commit silently into _value and
43
43
  // reverting is just dropping the override — _value is already correct.
44
- else globalQueue.m.je.push(e);
44
+ else globalQueue.m.Me.push(e);
45
45
  // Stamp ownership on the node (post-merge, so entangled writers share the
46
46
  // joint root). resolveTransition prefers this over the lane's _transition,
47
47
  // which a shared subscriber can merge across transactions (#2912).
48
48
  e.sn = activeTransition;
49
49
  const r = getOrCreateLane(e);
50
- e.Me = r;
50
+ e.Ke = r;
51
51
  // Literal undefined must not land raw: the slot doubles as the optimistic
52
52
  // brand, and erasing it makes the write invisible and routes follow-up
53
53
  // writes off the optimistic path into permanent commits (#2898).
54
- e.Ae = n === undefined ? OVERRIDE_UNDEFINED : n;
54
+ e._e = n === undefined ? OVERRIDE_UNDEFINED : n;
55
55
  // syncCompanions only pokes _pendingSignal/_latestValueComputed — with
56
56
  // neither companion present the call is a guaranteed no-op.
57
- (e.Re !== undefined || e.pe !== undefined) && GlobalQueue._e !== null && GlobalQueue._e(e, n);
58
- e.Se = clock;
57
+ (e.ge !== undefined || e.pe !== undefined) && GlobalQueue.Pe !== null && GlobalQueue.Pe(e, n);
58
+ e.de = clock;
59
59
  insertSubs(e, true);
60
60
  schedule();
61
61
  return n;
@@ -66,8 +66,8 @@ import { GlobalQueue, activeTransition, globalQueue, clock, insertSubs, schedule
66
66
  * while one of its optimistic nodes holds an active override that is still
67
67
  * pending on real (non-affects-sentinel) async.
68
68
  */ function transitionBlocked(e) {
69
- for (let n = 0; n < e.je.length; n++) {
70
- const t = e.je[n];
69
+ for (let n = 0; n < e.Me.length; n++) {
70
+ const t = e.Me[n];
71
71
  if (hasActiveOverride(t) && "S" in t && t.S & STATUS_PENDING && t._ instanceof NotReadyError) {
72
72
  return true;
73
73
  }
@@ -82,15 +82,15 @@ function resolveOptimisticNodes(e) {
82
82
  const n = e.length;
83
83
  for (let t = 0; t < n; t++) {
84
84
  const n = e[t];
85
- n.Me = undefined;
85
+ n.Ke = undefined;
86
86
  // Revert is a pure drop: there is no revert target to commit —
87
87
  // override-covered authoritative values hold in _pendingValue and
88
88
  // elevate on their OWN transition's schedule (A18 as re-ruled 2026-07-07).
89
89
  if (!(n.S & STATUS_PENDING)) n.S &= ~STATUS_UNINITIALIZED;
90
- const i = n.Ae;
91
- n.Ae = NOT_PENDING;
92
- if (i !== NOT_PENDING && n.be !== unwrapOverride(i)) insertSubs(n, true);
93
- n.Ie = null;
90
+ const i = n._e;
91
+ n._e = NOT_PENDING;
92
+ if (i !== NOT_PENDING && n.Ue !== unwrapOverride(i)) insertSubs(n, true);
93
+ n.Ne = null;
94
94
  n.sn = null;
95
95
  }
96
96
  // Settlement checkpoint (#2838): companions caught in this batch (or owned
@@ -98,9 +98,9 @@ function resolveOptimisticNodes(e) {
98
98
  // transition that produced them (A19 — pending is a property of the data).
99
99
  for (let t = 0; t < n; t++) {
100
100
  const n = e[t];
101
- if (n.Re || n.pe) GlobalQueue.un(n);
101
+ if (n.ge || n.pe) GlobalQueue.un(n);
102
102
  const i = n.nn;
103
- if (i && (i.Re === n || i.pe === n)) GlobalQueue.un(i);
103
+ if (i && (i.ge === n || i.pe === n)) GlobalQueue.un(i);
104
104
  }
105
105
  e.splice(0, n);
106
106
  }
@@ -113,7 +113,7 @@ function runQueue(e, n) {
113
113
  * Run effects from all lanes that are ready (no pending async).
114
114
  */ function runLaneEffects(e) {
115
115
  for (const n of activeLanes) {
116
- if (n.tn || n.Ne.size > 0) continue;
116
+ if (n.tn || n.Ae.size > 0) continue;
117
117
  const t = n.rn[e - 1];
118
118
  if (t.length) {
119
119
  n.rn[e - 1] = [];
@@ -124,14 +124,14 @@ function runQueue(e, n) {
124
124
 
125
125
  function cleanupCompletedLanes(e) {
126
126
  for (const n of activeLanes) {
127
- const t = e ? n.Ie === e : !n.Ie;
127
+ const t = e ? n.Ne === e : !n.Ne;
128
128
  if (!t) continue;
129
129
  if (!n.tn) {
130
130
  if (n.rn[0].length) runQueue(n.rn[0], EFFECT_RENDER);
131
131
  if (n.rn[1].length) runQueue(n.rn[1], EFFECT_USER);
132
132
  }
133
- if (n.en.Me === n) n.en.Me = undefined;
134
- n.Ne.clear();
133
+ if (n.en.Ke === n) n.en.Ke = undefined;
134
+ n.Ae.clear();
135
135
  n.rn[0].length = 0;
136
136
  n.rn[1].length = 0;
137
137
  activeLanes.delete(n);
@@ -143,7 +143,7 @@ function cleanupCompletedLanes(e) {
143
143
  // Per-lane suspension: only throw if in same lane as pending async
144
144
  // AND the node doesn't have an active override (overrides are the visible value,
145
145
  // downstream in the lane should read the override, not throw)
146
- const n = e.Me;
146
+ const n = e.Ke;
147
147
  if (!n) return false;
148
148
  return findLane(n) === findLane(currentOptimisticLane) && !hasActiveOverride(e);
149
149
  }
@@ -153,7 +153,7 @@ function cleanupCompletedLanes(e) {
153
153
  * that reads a pending mid-transition write sees the committed value; the sub
154
154
  * is recorded for replay at commit.
155
155
  */ function gatedRead(e, n, t) {
156
- if (latestReadActive || e.De === NOT_PENDING || e.ae || n !== e && !(n.se & REACTIVE_MANUAL_WRITE)) {
156
+ if (latestReadActive || e.De === NOT_PENDING || e.ce || n !== e && !(n.se & REACTIVE_MANUAL_WRITE)) {
157
157
  return false;
158
158
  }
159
159
  activeTransition.ln.add(t);
@@ -164,7 +164,7 @@ function cleanupCompletedLanes(e) {
164
164
  * read()'s value selection under a lane: return the committed `_value` for
165
165
  * optimistic/lane-assigned signals, stale-mode reads, and pending owners.
166
166
  */ function laneReadsCommitted(e, n, t) {
167
- if (e.Ae !== undefined || !!e.Me || !!(n.S & STATUS_PENDING)) return true;
167
+ if (e._e !== undefined || !!e.Ke || !!(n.S & STATUS_PENDING)) return true;
168
168
  if (n === e && stale && t.nn !== e) {
169
169
  // The committed view can hide a same-tick ambient write (a lane member —
170
170
  // even just an isPending companion flip — puts the reader "under a lane"
@@ -185,8 +185,8 @@ function cleanupCompletedLanes(e) {
185
185
  * can run before its OPT-dirty child propagates).
186
186
  */ function recomputeLane(e, n) {
187
187
  if (n) return resolveLane(e) ?? null;
188
- for (let n = e.et; n; n = n.tt) {
189
- const t = n.nt;
188
+ for (let n = e.tt; n; n = n.nt) {
189
+ const t = n.it;
190
190
  if (t.se & REACTIVE_OPTIMISTIC_DIRTY) {
191
191
  const n = resolveLane(t);
192
192
  if (n) {
@@ -202,17 +202,17 @@ function cleanupCompletedLanes(e) {
202
202
  /** recompute()'s catch path: track pending async in the current lane. */ function laneAsyncPending(e) {
203
203
  const n = findLane(currentOptimisticLane);
204
204
  if (n.en !== e) {
205
- n.Ne.add(e);
206
- e.Me = n;
207
- GlobalQueue.ce !== null && GlobalQueue.ce(n.en);
205
+ n.Ae.add(e);
206
+ e.Ke = n;
207
+ GlobalQueue.Se !== null && GlobalQueue.Se(n.en);
208
208
  }
209
209
  }
210
210
 
211
211
  /** recompute()'s success path: the node's async settled, clear it from its lane. */ function laneAsyncSettled(e) {
212
212
  const n = resolveLane(e);
213
213
  if (n) {
214
- n.Ne.delete(e);
215
- GlobalQueue.ce !== null && GlobalQueue.ce(n.en);
214
+ n.Ae.delete(e);
215
+ GlobalQueue.Se !== null && GlobalQueue.Se(n.en);
216
216
  }
217
217
  }
218
218
 
@@ -227,18 +227,18 @@ function trackOptimisticStore(e) {
227
227
  * create optimistic state (verdict.ts at module top level, createOptimistic
228
228
  * and createOptimisticStore at first call) BEFORE any optimistic node exists.
229
229
  */ function installOptimisticEngine() {
230
- if (GlobalQueue.ht !== null) return;
231
- GlobalQueue.ht = optimisticWrite;
230
+ if (GlobalQueue.Gt !== null) return;
231
+ GlobalQueue.Gt = optimisticWrite;
232
232
  GlobalQueue.En = resolveOptimisticNodes;
233
233
  GlobalQueue.Tn = transitionBlocked;
234
234
  GlobalQueue.dn = cleanupCompletedLanes;
235
235
  GlobalQueue.In = runLaneEffects;
236
- GlobalQueue.Ot = gatedRead;
237
- GlobalQueue.Ct = laneSuspends;
238
- GlobalQueue.Rt = laneReadsCommitted;
239
- GlobalQueue.Ze = recomputeLane;
240
- GlobalQueue.ze = laneAsyncPending;
241
- GlobalQueue.Ke = laneAsyncSettled;
236
+ GlobalQueue.Rt = gatedRead;
237
+ GlobalQueue.Ot = laneSuspends;
238
+ GlobalQueue.Pt = laneReadsCommitted;
239
+ GlobalQueue.je = recomputeLane;
240
+ GlobalQueue.$e = laneAsyncPending;
241
+ GlobalQueue.ze = laneAsyncSettled;
242
242
  GlobalQueue.Nn = trackOptimisticStore;
243
243
  }
244
244
 
@@ -1,4 +1,4 @@
1
- import { REACTIVE_DISPOSED, REACTIVE_ZOMBIE, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, CONFIG_TRANSPARENT, defaultContext } from "./constants.js";
1
+ import { REACTIVE_DISPOSED, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, REACTIVE_ZOMBIE, 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 n = e.ke;
15
+ let n = e.Fe;
16
16
  while (n) {
17
17
  const e = n.se;
18
18
  n.se = e | REACTIVE_ZOMBIE;
@@ -26,17 +26,21 @@ function markDisposal(e) {
26
26
  if (e & REACTIVE_IN_HEAP) insertIntoHeap(n, zombieQueue); else insertIntoHeapHeight(n, zombieQueue);
27
27
  }
28
28
  markDisposal(n);
29
- n = n.Fe;
29
+ n = n.He;
30
30
  }
31
31
  }
32
32
 
33
33
  function dispose(e) {
34
- let n = e.et;
34
+ // Leave every scheduler heap on disposal (mirrors `unobserved`): a node
35
+ // still queued here would be recomputed by the next flush, and recompute()
36
+ // rewriting `_flags` would clear REACTIVE_DISPOSED — resurrecting it (#2983).
37
+ deleteFromHeap(e, queueFor(e));
38
+ let n = e.tt;
35
39
  while (n !== null) {
36
40
  n = unlinkSubs(n);
37
41
  }
38
- e.et = null;
39
- e.We = null;
42
+ e.tt = null;
43
+ e.Ye = null;
40
44
  disposeChildren(e, true);
41
45
  }
42
46
 
@@ -51,54 +55,59 @@ function disposeChildren(e, n = false, t) {
51
55
  // edge). Snap runs after the DISPOSED flag is set so the oracle reads
52
56
  // false, and notifies subscribers still watching the companion.
53
57
  const n = e;
54
- if (n.Re || n.pe) GlobalQueue.un(n);
58
+ if (n.ge || n.pe) GlobalQueue.un(n);
55
59
  }
56
- if (n && e.ae) e.Te = null;
57
- let l = t ? e.Qe : e.ke;
60
+ if (n && e.ce) e.Te = null;
61
+ let l = t ? e.We : e.Fe;
58
62
  while (l) {
59
- const e = l.Fe;
60
- if (l.et) {
61
- const e = l;
62
- deleteFromHeap(e, queueFor(e));
63
- let n = e.et;
63
+ const e = l.He;
64
+ const n = l;
65
+ // Heap removal must not be gated on `_deps`: a dependency-free
66
+ // computation queued by refresh() has a null dep list but still sits in
67
+ // the dirty heap, and left there the post-disposal flush recomputes it —
68
+ // recompute() rewriting `_flags` clears REACTIVE_DISPOSED and the node
69
+ // comes back to life (post-unmount runs, leaked cleanups, #2983).
70
+ if (n.se & (REACTIVE_IN_HEAP | REACTIVE_IN_HEAP_HEIGHT)) deleteFromHeap(n, queueFor(n));
71
+ if (n.tt) {
72
+ let e = n.tt;
64
73
  do {
65
- n = unlinkSubs(n);
66
- } while (n !== null);
67
- e.et = null;
68
- e.We = null;
74
+ e = unlinkSubs(e);
75
+ } while (e !== null);
76
+ n.tt = null;
77
+ n.Ye = null;
69
78
  }
70
79
  disposeChildren(l, true);
71
80
  l = e;
72
81
  }
73
82
  if (t) {
74
- e.Qe = null;
83
+ e.We = null;
75
84
  } else {
76
- e.ke = null;
85
+ e.Fe = null;
77
86
  e.qe = 0;
78
87
  }
79
88
  // O(1) splice out of parent's chain on individual dispose. Skipped during
80
89
  // batch dispose (parent already disposed) and zombie disposal (node sits on
81
90
  // parent's _pendingFirstChild). We leave node._nextSibling intact so outer
82
91
  // walks that already advanced past us still reach later siblings.
83
- if (n && !t && !(i & REACTIVE_ZOMBIE) && e.ve !== null && !(e.ve.se & REACTIVE_DISPOSED)) {
84
- const n = e.rt;
85
- const t = e.Fe;
86
- if (n !== null) n.Fe = t; else e.ve.ke = t;
87
- if (t !== null) t.rt = n;
88
- e.rt = null;
92
+ if (n && !t && !(i & REACTIVE_ZOMBIE) && e.ke !== null && !(e.ke.se & REACTIVE_DISPOSED)) {
93
+ const n = e.ct;
94
+ const t = e.He;
95
+ if (n !== null) n.He = t; else e.ke.Fe = t;
96
+ if (t !== null) t.ct = n;
97
+ e.ct = null;
89
98
  }
90
99
  runDisposal(e, t);
91
100
  // Final effect-returned cleanup fires at true disposal, after `_disposal`
92
101
  // to mirror rerun ordering (compute-phase teardown first, cleanup last).
93
- if (n && e.Et) {
94
- const n = e.Et;
95
- e.Et = undefined;
102
+ if (n && e.Tt) {
103
+ const n = e.Tt;
104
+ e.Tt = undefined;
96
105
  n();
97
106
  }
98
107
  }
99
108
 
100
109
  function runDisposal(e, n) {
101
- let t = n ? e.ye : e.Le;
110
+ let t = n ? e.Qe : e.ye;
102
111
  if (!t) return;
103
112
  if (Array.isArray(t)) {
104
113
  for (let e = 0; e < t.length; e++) {
@@ -108,12 +117,12 @@ function runDisposal(e, n) {
108
117
  } else {
109
118
  t.call(t);
110
119
  }
111
- n ? e.ye = null : e.Le = null;
120
+ n ? e.Qe = null : e.ye = null;
112
121
  }
113
122
 
114
123
  function childId(e, n) {
115
124
  let t = e;
116
- while (t.T & CONFIG_TRANSPARENT && t.ve) t = t.ve;
125
+ while (t.T & CONFIG_TRANSPARENT && t.ke) t = t.ke;
117
126
  if (t.id != null) return formatId(t.id, n ? t.qe++ : t.qe);
118
127
  throw new Error("");
119
128
  }
@@ -195,7 +204,7 @@ function formatId(e, n) {
195
204
  * checks. `cleanup()` is the unchecked primitive used by internals.
196
205
  */ function cleanup(e) {
197
206
  if (!context) return e;
198
- if (!context.Le) context.Le = e; else if (Array.isArray(context.Le)) context.Le.push(e); else context.Le = [ context.Le, e ];
207
+ if (!context.ye) context.ye = e; else if (Array.isArray(context.ye)) context.ye.push(e); else context.ye = [ context.ye, e ];
199
208
  return e;
200
209
  }
201
210
 
@@ -236,27 +245,27 @@ function disposeRootSelf(e = true) {
236
245
  id: inheritId(e, t, n),
237
246
  T: t ? CONFIG_TRANSPARENT : 0,
238
247
  Nt: true,
239
- Tt: n?.Nt ? n.Tt : n,
240
- ke: null,
248
+ dt: n?.Nt ? n.dt : n,
241
249
  Fe: null,
242
- rt: null,
243
- Le: null,
250
+ He: null,
251
+ ct: null,
252
+ ye: null,
244
253
  C: n?.C ?? globalQueue,
245
254
  we: n?.we || defaultContext,
246
255
  qe: 0,
247
- ye: null,
248
256
  Qe: null,
249
- ve: n,
257
+ We: null,
258
+ ke: n,
250
259
  dispose: disposeRootSelf
251
260
  };
252
261
  if (n) {
253
- const e = n.ke;
262
+ const e = n.Fe;
254
263
  if (e === null) {
255
- n.ke = i;
264
+ n.Fe = i;
256
265
  } else {
257
- i.Fe = e;
258
- e.rt = i;
259
- n.ke = i;
266
+ i.He = e;
267
+ e.ct = i;
268
+ n.Fe = i;
260
269
  }
261
270
  }
262
271
  return i;