@solidjs/signals 2.0.0-rc.1 → 2.0.0-rc.2

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 (50) hide show
  1. package/dist/dev.js +1661 -361
  2. package/dist/node.cjs +1939 -1331
  3. package/dist/prod/affects.js +16 -16
  4. package/dist/prod/boundaries.js +90 -90
  5. package/dist/prod/core/action.js +3 -3
  6. package/dist/prod/core/async.js +74 -72
  7. package/dist/prod/core/constants.js +39 -1
  8. package/dist/prod/core/core.js +332 -224
  9. package/dist/prod/core/effect.js +56 -56
  10. package/dist/prod/core/external.js +4 -4
  11. package/dist/prod/core/graph.js +65 -54
  12. package/dist/prod/core/heap.js +52 -44
  13. package/dist/prod/core/invariants.js +3 -2
  14. package/dist/prod/core/lanes.js +41 -34
  15. package/dist/prod/core/optimistic.js +63 -60
  16. package/dist/prod/core/owner.js +97 -96
  17. package/dist/prod/core/scheduler.js +243 -194
  18. package/dist/prod/core/verdict.js +184 -77
  19. package/dist/prod/map.js +104 -104
  20. package/dist/prod/signals.js +1 -1
  21. package/dist/prod/store/next/optimistic.js +31 -23
  22. package/dist/prod/store/next/projection.js +3 -3
  23. package/dist/prod/store/next/reconcile.js +78 -74
  24. package/dist/prod/store/next/store.js +357 -90
  25. package/dist/prod/store/store.js +7 -7
  26. package/dist/types/core/attribution-hooks.d.ts +52 -0
  27. package/dist/types/core/attribution.d.ts +186 -0
  28. package/dist/types/core/constants.d.ts +26 -0
  29. package/dist/types/core/core.d.ts +8 -1
  30. package/dist/types/core/dev.d.ts +20 -3
  31. package/dist/types/core/graph.d.ts +1 -0
  32. package/dist/types/core/lanes.d.ts +4 -16
  33. package/dist/types/core/scheduler.d.ts +8 -0
  34. package/dist/types/core/types.d.ts +85 -41
  35. package/dist/types/store/next/projection.d.ts +0 -16
  36. package/dist/types/store/next/store.d.ts +6 -0
  37. package/dist/types/store/next/target.d.ts +18 -0
  38. package/dist/types-cjs/core/attribution-hooks.d.cts +52 -0
  39. package/dist/types-cjs/core/attribution.d.cts +186 -0
  40. package/dist/types-cjs/core/constants.d.cts +26 -0
  41. package/dist/types-cjs/core/core.d.cts +8 -1
  42. package/dist/types-cjs/core/dev.d.cts +20 -3
  43. package/dist/types-cjs/core/graph.d.cts +1 -0
  44. package/dist/types-cjs/core/lanes.d.cts +4 -16
  45. package/dist/types-cjs/core/scheduler.d.cts +8 -0
  46. package/dist/types-cjs/core/types.d.cts +85 -41
  47. package/dist/types-cjs/store/next/projection.d.cts +0 -16
  48. package/dist/types-cjs/store/next/store.d.cts +6 -0
  49. package/dist/types-cjs/store/next/target.d.cts +18 -0
  50. package/package.json +1 -1
@@ -1,14 +1,14 @@
1
1
  import { handleAsync, clearStatus, parkLoadingWindow, notifyStatus, settleErroredDependents } from "./async.js";
2
2
 
3
- import { EFFECT_TRACKED, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING, STATUS_UNINITIALIZED, STATUS_ERROR, REACTIVE_REASK, REACTIVE_RECOMPUTING_DEPS, CONFIG_SYNC, STATUS_PENDING, REACTIVE_NONE, REACTIVE_SNAPSHOT_STALE, unwrapOverride, OVERRIDE_UNDEFINED, REACTIVE_LAZY, REACTIVE_DISPOSED, CONFIG_CHILDREN_FORBIDDEN, CONFIG_AUTO_DISPOSE, REACTIVE_CHECK, REACTIVE_DIRTY, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, defaultContext, CONFIG_IN_SNAPSHOT_SCOPE, CONFIG_TRANSPARENT, CONFIG_OWNED_WRITE, CONFIG_NO_SNAPSHOT, $REFRESH, REACTIVE_MANUAL_WRITE, NO_SNAPSHOT, STORE_SNAPSHOT_PROPS, EFFECT_USER } from "./constants.js";
3
+ import { EFFECT_TRACKED, REACTIVE_OPTIMISTIC_DIRTY, CONFIG_OPTIMISTIC, NOT_PENDING, STATUS_UNINITIALIZED, STATUS_ERROR, REACTIVE_REASK, REACTIVE_RECOMPUTING_DEPS, CONFIG_SYNC, CONFIG_HAS_LANE, STATUS_PENDING, REACTIVE_MISSED_WAKE, REACTIVE_NONE, REACTIVE_SNAPSHOT_STALE, unwrapOverride, OVERRIDE_UNDEFINED, CONFIG_HAS_COMPANIONS, REACTIVE_LAZY, REACTIVE_DISPOSED, CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, REACTIVE_CHECK, REACTIVE_DIRTY, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, defaultContext, CONFIG_IN_SNAPSHOT_SCOPE, CONFIG_TRANSPARENT, CONFIG_OWNED_WRITE, CONFIG_NO_SNAPSHOT, $REFRESH, REACTIVE_MANUAL_WRITE, CONFIG_FW_CHILDREN, NO_SNAPSHOT, CONFIG_HAS_SNAPSHOT, STORE_SNAPSHOT_PROPS, EFFECT_USER } from "./constants.js";
4
4
 
5
5
  import { NotReadyError } from "./error.js";
6
6
 
7
7
  import { trimStaleDeps, link, unobserved } from "./graph.js";
8
8
 
9
- import { deleteFromHeap, queueFor, insertIntoHeapHeight, markNode, markHeap, insertIntoHeap } from "./heap.js";
9
+ import { deleteFromHeap, queueFor, insertIntoHeapHeight, enqueueSub, markNode, markHeap, insertIntoHeap } from "./heap.js";
10
10
 
11
- import { GlobalQueue, activeTransition, globalQueue, clock, insertSubs, queuePendingNode, runInTransition, schedule, dirtyQueue, projectionWriteActive, armReaskClear } from "./scheduler.js";
11
+ import { GlobalQueue, activeTransition, globalQueue, clock, insertSubs, queuePendingNode, runInTransition, schedule, notifyEpoch, dirtyQueue, bumpNotifyEpoch, projectionWriteActive, reaskArmed, armReaskClear } from "./scheduler.js";
12
12
 
13
13
  import "./invariants.js";
14
14
 
@@ -16,7 +16,7 @@ import { disposeChildren, markDisposal, inheritId } from "./owner.js";
16
16
 
17
17
  GlobalQueue.Ce = recompute;
18
18
 
19
- GlobalQueue.me = disposeChildren;
19
+ GlobalQueue.Fe = disposeChildren;
20
20
 
21
21
  let tracking = false;
22
22
 
@@ -48,8 +48,8 @@ let snapshotSources = null;
48
48
 
49
49
  function ownerInSnapshotScope(e) {
50
50
  while (e) {
51
- if (e.ve) return true;
52
- e = e.Fe;
51
+ if (e.He) return true;
52
+ e = e.ve;
53
53
  }
54
54
  return false;
55
55
  }
@@ -60,11 +60,11 @@ function setSnapshotCapture(e) {
60
60
  }
61
61
 
62
62
  function markSnapshotScope(e) {
63
- e.ve = true;
63
+ e.He = true;
64
64
  }
65
65
 
66
66
  function releaseSnapshotScope(e) {
67
- e.ve = false;
67
+ e.He = false;
68
68
  releaseSubtree(e);
69
69
  schedule();
70
70
  }
@@ -72,29 +72,29 @@ function releaseSnapshotScope(e) {
72
72
  function releaseSubtree(e) {
73
73
  let t = e.ke;
74
74
  while (t) {
75
- if (t.ve) {
76
- t = t.He;
75
+ if (t.He) {
76
+ t = t.Ve;
77
77
  continue;
78
78
  }
79
- if (t.ce) {
79
+ if (t.Se) {
80
80
  const e = t;
81
81
  e.T &= ~CONFIG_IN_SNAPSHOT_SCOPE;
82
- if (e.se & REACTIVE_SNAPSHOT_STALE) {
83
- e.se &= ~REACTIVE_SNAPSHOT_STALE;
84
- e.se |= REACTIVE_DIRTY;
85
- if (dirtyQueue.Ve > e.Le) dirtyQueue.Ve = e.Le;
82
+ if (e.ie & REACTIVE_SNAPSHOT_STALE) {
83
+ e.ie &= ~REACTIVE_SNAPSHOT_STALE;
84
+ e.ie |= REACTIVE_DIRTY;
85
+ if (dirtyQueue.xe > e.Le) dirtyQueue.xe = e.Le;
86
86
  insertIntoHeap(e, dirtyQueue);
87
87
  }
88
88
  }
89
89
  releaseSubtree(t);
90
- t = t.He;
90
+ t = t.Ve;
91
91
  }
92
92
  }
93
93
 
94
94
  function clearSnapshots() {
95
95
  if (snapshotSources) {
96
96
  for (const e of snapshotSources) {
97
- delete e.xe;
97
+ delete e.o?.Qe;
98
98
  // StoreNode targets share one pre-initialized hidden class (see
99
99
  // createStoreProxy) — assign undefined instead of deleting, and only
100
100
  // when present so signal-node sources don't grow the field.
@@ -106,46 +106,50 @@ function clearSnapshots() {
106
106
  }
107
107
 
108
108
  function recompute(e, t = false) {
109
+ // §12d: any recompute can clean a marked subscriber — invalidate skips.
110
+ bumpNotifyEpoch();
109
111
  const n = e.Re;
110
112
  if (!t) {
111
- if (e.Ne && (!n || activeTransition) && activeTransition !== e.Ne) globalQueue.initTransition(e.Ne);
113
+ if (e._e && (!n || activeTransition) && activeTransition !== e._e) globalQueue.initTransition(e._e);
112
114
  deleteFromHeap(e, queueFor(e));
113
- e.Te = null;
115
+ if (e.o !== null) e.o.Ee = null;
114
116
  // Tracked effects run after finalizePureQueue, so dispose immediately instead of deferring
115
- if (e.Ne || n === EFFECT_TRACKED) disposeChildren(e); else if (e.ke !== null || e.he !== null) {
117
+ if (e._e || n === EFFECT_TRACKED) disposeChildren(e); else if (e.ke !== null || e.he !== null) {
116
118
  markDisposal(e);
117
- e.Qe = e.he;
118
- e.We = e.ke;
119
+ const t = ext(e);
120
+ t.We = e.he;
121
+ t.qe = e.ke;
119
122
  e.he = null;
120
123
  e.ke = null;
121
- e.qe = 0;
124
+ e.Me = 0;
122
125
  } else ;
123
126
  }
124
- let i = !!(e.se & REACTIVE_OPTIMISTIC_DIRTY);
125
- const l = e.De !== undefined && e.De !== NOT_PENDING;
126
- const u = !!(e.S & STATUS_UNINITIALIZED);
127
+ let i = !!(e.ie & REACTIVE_OPTIMISTIC_DIRTY);
128
+ const u = (e.T & CONFIG_OPTIMISTIC) !== 0 && e.o?.De !== NOT_PENDING && e.o?.De !== undefined;
129
+ const l = !!(e.S & STATUS_UNINITIALIZED);
127
130
  // Outgoing error, captured before the compute clears status: if this run
128
131
  // recovers to an unchanged value, dependents still holding this object must
129
132
  // be swept (settleErroredDependents, #2949).
130
- const s = e.S & STATUS_ERROR ? e._ : undefined;
133
+ const o = e.S & STATUS_ERROR ? e.o?._ : undefined;
131
134
  // Re-ask classification lives in the verdict module; capture the flag before
132
135
  // the recompute wipes _flags below.
133
- const o = (e.se & REACTIVE_REASK) !== 0;
136
+ const s = (e.ie & REACTIVE_REASK) !== 0;
134
137
  // Captured before the compute clears it on a sync landing: if that landing
135
138
  // is transition-held below, the window must stay open until the hold
136
139
  // commits (commitPendingNode) — a closed window plus a held value reads as
137
140
  // a pending frame to live observers of the verdict (#2990).
138
- const a = e.Ee;
141
+ const a = e.Ie;
139
142
  const r = context;
140
143
  context = e;
141
144
  e.Ye = null;
142
145
  e.Ze++;
143
- e.se = REACTIVE_RECOMPUTING_DEPS;
144
- e.de = clock;
145
- let c = e._e === NOT_PENDING ? e.be : e._e;
146
+ e.ie = REACTIVE_RECOMPUTING_DEPS;
147
+ e.Te = clock;
148
+ let c = e.Pe === NOT_PENDING ? e.be : e.Pe;
146
149
  let _ = e.Le;
147
- let f = tracking;
148
- let E = currentOptimisticLane;
150
+ let f = false;
151
+ let E = tracking;
152
+ let I = currentOptimisticLane;
149
153
  tracking = true;
150
154
  // A computed's fn establishes its OWN dependencies, so it must never run
151
155
  // inside a latest() read window: read() short-circuits through the
@@ -165,7 +169,7 @@ function recompute(e, t = false) {
165
169
  // latest()/isPending() pull stages instead of direct-committing (#3009).
166
170
  // The predicate lives with the engine (recomputeLane).
167
171
  else if (t === false) i = false;
168
- } else if (activeTransition && !t && activeTransition.Me.length) {
172
+ } else if (activeTransition && !t && activeTransition.Ke.length) {
169
173
  // Lane adoption: parent-deeper-than-owned-child can run before its OPT-dirty
170
174
  // child propagates. Walk deps once and inherit the OPT lane so this node
171
175
  // recomputes under the right posture and propagates correctly.
@@ -176,42 +180,44 @@ function recompute(e, t = false) {
176
180
  }
177
181
  }
178
182
  const T = n && n !== EFFECT_USER;
179
- const I = stale;
183
+ const d = stale;
180
184
  if (T) stale = true;
181
185
  try {
182
186
  if (!false && e.T & CONFIG_SYNC) {
183
- c = e.ce(c);
184
- e.Te = null;
185
- e.Ee = false;
187
+ c = e.Se(c);
188
+ if (e.o !== null) e.o.Ee = null;
189
+ e.Ie = false;
186
190
  } else {
187
191
  // Snapshot `_inFlight` so we can detect whether `_fn` self-registered an async
188
192
  // subscription (e.g. `createProjection` calls `handleAsync` from inside its body
189
193
  // with a setter callback). In that case, the outer `handleAsync` call below would
190
194
  // clobber the fresh subscription, so we skip it and let the internally-registered
191
195
  // iteration drive updates.
192
- const t = e.Te;
193
- const n = e.ce(c);
196
+ const t = e.o?.Ee;
197
+ const n = e.Se(c);
194
198
  const i = typeof n === "object" && n !== null;
195
- const l = e.Te !== t;
196
- c = l || !i ? n : handleAsync(e, n);
197
- if (!l && !i) {
198
- e.Te = null;
199
+ const u = e.o?.Ee !== t;
200
+ c = u || !i ? n : handleAsync(e, n);
201
+ if (!u && !i) {
202
+ if (e.o !== null) e.o.Ee = null;
199
203
  // A sync (non-object) return is the first real answer; async-shaped
200
204
  // results clear inside handleAsync at their own landing points, and a
201
205
  // self-registered flight (inFlightChanged — projections) clears when
202
206
  // its internal handleAsync lands.
203
- e.Ee = false;
207
+ e.Ie = false;
204
208
  }
205
209
  }
206
- // On a status-free node clearStatus is a guaranteed no-op: every branch
207
- // in its body is gated on one of these fields, and with _statusFlags === 0
208
- // the flags write (create or not) stores the 0 already there.
209
- if (e.S !== 0 || e.i !== undefined || e._ || e.pe || e.le || e.oe !== undefined || e.Oe !== undefined || e.ge !== undefined || e.u !== null) clearStatus(e, t);
210
- // _optimisticLane is only ever assigned by engine paths.
211
- if (e.Ke) GlobalQueue.Be(e);
210
+ // On a status-free node clearStatus is a guaranteed no-op: every field
211
+ // its body gates on is either _statusFlags or lives in the cold
212
+ // extension no extension, no status to clear. (_x from an unrelated
213
+ // installer just makes clearStatus a cheap re-verified no-op.)
214
+ if (e.S !== 0 || e.o !== null) clearStatus(e, t);
215
+ // _optimisticLane is only ever assigned by engine paths (CONFIG_HAS_LANE
216
+ // is their sticky presence mark).
217
+ if (e.T & CONFIG_HAS_LANE && e.o?.Be) GlobalQueue.ze(e);
212
218
  } catch (t) {
213
219
  const n = t instanceof NotReadyError;
214
- if (n && e.Ee) {
220
+ if (n && e.Ie) {
215
221
  // Loading window with an unready sync dependency: register for the
216
222
  // source's settle (the settlePendingSource walk runs off
217
223
  // _pendingSources + _blocked alone) but take NO read-visible pending
@@ -223,28 +229,34 @@ function recompute(e, t = false) {
223
229
  } else {
224
230
  // Track pending async in the lane (not the lane's source — it creates the lane
225
231
  // but doesn't belong to it). Set lane BEFORE notifyStatus for downstream propagation.
226
- if (n && currentOptimisticLane) GlobalQueue.ze(e);
232
+ if (n && currentOptimisticLane) GlobalQueue.$e(e);
227
233
  let i = false;
228
234
  if (n) {
229
- e.le = true;
230
- if (GlobalQueue.$e !== null) i = GlobalQueue.$e(e, o);
235
+ ext(e).ue = true;
236
+ if (GlobalQueue.Je !== null) i = GlobalQueue.Je(e, s);
231
237
  }
232
- notifyStatus(e, n ? STATUS_PENDING : STATUS_ERROR, t, undefined, n ? e.Ke : undefined);
233
- if (i) GlobalQueue.k(e);
238
+ notifyStatus(e, n ? STATUS_PENDING : STATUS_ERROR, t, undefined, n ? e.o?.Be : undefined);
239
+ if (i) GlobalQueue.p(e);
234
240
  }
235
241
  } finally {
236
- tracking = f;
242
+ tracking = E;
237
243
  latestReadActive = N;
238
- if (T) stale = I;
239
- e.se = REACTIVE_NONE | (t ? e.se & REACTIVE_SNAPSHOT_STALE : 0);
244
+ if (T) stale = d;
245
+ // Consume the missed-wake latch (#3037, set by insertSubs): a dep write
246
+ // landed beneath this pass on a link it had already validated. The wipe
247
+ // below must not key off DIRTY/CHECK — the read-time pull protocol
248
+ // (markNode(c) in read()) marks the running node as part of ordinary
249
+ // bookkeeping, and those marks are correctly discarded here.
250
+ f = (e.ie & REACTIVE_MISSED_WAKE) !== 0;
251
+ e.ie = REACTIVE_NONE | (t ? e.ie & REACTIVE_SNAPSHOT_STALE : 0);
240
252
  context = r;
241
253
  }
242
- if (!e._) {
254
+ if (!e.o?._) {
243
255
  trimStaleDeps(e);
244
- const o = l ? unwrapOverride(e.De) : e._e === NOT_PENDING ? e.be : e._e;
256
+ const s = u ? unwrapOverride(e.o?.De) : e.Pe === NOT_PENDING ? e.be : e.Pe;
245
257
  let r = false;
246
258
  try {
247
- r = !n && u || !e.Ue || !e.Ue(o, c);
259
+ r = !n && l || !e.Ue || !e.Ue(s, c);
248
260
  } catch (t) {
249
261
  // A throwing user comparator is an error of this node's computation.
250
262
  // Route it through the same status path as a compute-phase throw so
@@ -258,91 +270,105 @@ function recompute(e, t = false) {
258
270
  // gate: the explicit recompute(node, true) inside effect() does not enqueue, so
259
271
  // effect() can call its runner synchronously for the first run.
260
272
  if (n && r) {
261
- e.Je = !e._;
273
+ e.Xe = !e.o?._;
262
274
  // Reuse one bound runner per effect — runEffect no-ops on a stale
263
275
  // `_modified`, so re-enqueueing the same function is harmless.
264
- if (!t) e.C.enqueue(n, e.Xe ??= GlobalQueue.et.bind(null, e));
276
+ if (!t) e.C.enqueue(n, e.et ??= GlobalQueue.tt.bind(null, e));
265
277
  }
266
- if (e._) ; else if (r) {
267
- const u = l ? e.De : undefined;
278
+ if (e.o?._) ; else if (r) {
279
+ const l = u ? e.o?.De : undefined;
268
280
  if (t ||
269
281
  // Plain sync flush (no transition on either side) commits effect
270
282
  // values directly — the pending round-trip (queuePendingNode +
271
283
  // commitPendingNodes) exists to sequence transition reveals, and
272
284
  // paying it per effect on the plain path is pure overhead.
273
- n && (activeTransition !== e.Ne || activeTransition === null) || i) {
285
+ n && (activeTransition !== e._e || activeTransition === null) || i) {
274
286
  e.be = c;
275
287
  // Lane-propagated correction: upstream data is fresh, correct the
276
288
  // override unconditionally. The direct _value commit is the lane's
277
289
  // own reveal schedule; drop any superseded older hold so its queued
278
290
  // commit can't clobber the fresh value.
279
- if (l && i) {
280
- e.De = c === undefined ? OVERRIDE_UNDEFINED : c;
281
- e._e = NOT_PENDING;
291
+ if (u && i) {
292
+ ext(e).De = c === undefined ? OVERRIDE_UNDEFINED : c;
293
+ e.Pe = NOT_PENDING;
282
294
  }
283
295
  } else {
284
- e._e = c;
296
+ e.Pe = c;
285
297
  // A window landing that gets held re-opens the window until the hold
286
298
  // commits — the verdict's held-value branch is window-gated (#2990).
287
- if (a) e.Ee = true;
299
+ if (a) e.Ie = true;
288
300
  // Transition-held sync recompute is a write path like setSignal/asyncWrite,
289
301
  // so sync derivations of held sources stay visible to isPending()/latest()
290
302
  // (#2831). Both companion writes are transition-scoped (optimistic) and
291
303
  // auto-revert/re-derive at commit. Skipped for plain flushes where the
292
304
  // pending value commits before effects run.
293
- if ((activeTransition || e.Ne) && GlobalQueue.Pe !== null) GlobalQueue.Pe(e, c);
305
+ if ((activeTransition || e._e) && GlobalQueue.Oe !== null) GlobalQueue.Oe(e, c);
294
306
  }
295
307
  // insertSubs only walks _subs (no scheduling of its own), so a
296
308
  // subscriber-less node has nothing to notify.
297
- if (e.o !== null && (!l || i || e.De !== u)) insertSubs(e, i || l);
298
- } else if (l) {
309
+ if (e.u !== null && (!u || i || e.o?.De !== l)) insertSubs(e, i || u);
310
+ } else if (u) {
299
311
  // Unchanged value (equals the override) recomputed while the override
300
312
  // is active: _value may still be stale, so hold the authoritative value
301
313
  // for commit on its own transition's schedule — invisibly (A17/A18).
302
- if (e._e === NOT_PENDING) queuePendingNode(e);
303
- e._e = c;
304
- if (a) e.Ee = true;
314
+ if (e.Pe === NOT_PENDING) queuePendingNode(e);
315
+ e.Pe = c;
316
+ if (a) e.Ie = true;
305
317
  // see the held branch above (#2990)
306
318
  } else if (e.Le != _) {
307
- for (let t = e.o; t !== null; t = t.ue) {
308
- insertIntoHeapHeight(t.fe, queueFor(t.fe));
319
+ for (let t = e.u; t !== null; t = t.fe) {
320
+ insertIntoHeapHeight(t.ae, queueFor(t.ae));
309
321
  }
310
322
  }
311
323
  // Silent recovery: errored → unchanged value fires no notification, but
312
324
  // dependents still holding the propagated error consumed their dirty flag
313
325
  // in an errored run and may sit on stale commits (#2949). Changed-value
314
326
  // recoveries ride insertSubs above; a comparator throw re-errored the node
315
- // (el._error re-set), so this only runs on a genuinely clean recovery.
316
- if (s !== undefined && !r && !e._) settleErroredDependents(e, s);
327
+ // (el._x?._error re-set), so this only runs on a genuinely clean recovery.
328
+ if (o !== undefined && !r && !e.o?._) settleErroredDependents(e, o);
317
329
  }
318
- currentOptimisticLane = E;
319
- const d = e._e !== NOT_PENDING || e.We !== null || e.Qe !== null || (e.S & (STATUS_PENDING | STATUS_UNINITIALIZED)) !== 0;
330
+ currentOptimisticLane = I;
331
+ const S = e.Pe !== NOT_PENDING || e.o !== null && (e.o.qe !== null || e.o.We !== null) || (e.S & (STATUS_PENDING | STATUS_UNINITIALIZED)) !== 0;
320
332
  // Override-covered holds (hasOverride) always queue: their commit belongs
321
333
  // to their own transition's schedule (A18 re-rule) and is unobservable
322
334
  // under the override (A17). Revert no longer commits anything, so an
323
335
  // unqueued covered hold would leak (INV-7) once the revert clears
324
336
  // _transition.
325
- d && (!t || e.S & STATUS_PENDING) && (!e.Ne || l) && queuePendingNode(e);
326
- e.Ne && n && activeTransition !== e.Ne && runInTransition(e.Ne, () => recompute(e));
337
+ S && (!t || e.S & STATUS_PENDING) && (!e._e || u) && queuePendingNode(e);
338
+ e._e && n && activeTransition !== e._e && runInTransition(e._e, () => recompute(e));
339
+ // Missed-wake reschedule (see the finally above): values this pass read
340
+ // before the nested commit are stale, so run again now that the heap will
341
+ // accept the node. Equality gates stop same-value landings from cascading,
342
+ // and a re-run only latches again if another nested commit changes a dep
343
+ // beneath it — convergent unless deps genuinely keep changing.
344
+ if (f) {
345
+ enqueueSub(e);
346
+ schedule();
347
+ }
327
348
  }
328
349
 
329
350
  function updateIfNecessary(e) {
330
- if (e.se & REACTIVE_CHECK) {
331
- for (let t = e.tt; t; t = t.nt) {
332
- const n = t.it;
351
+ // Never re-enter a node that is currently computing: its dep bookkeeping
352
+ // (_depsTail/_depGen) is live, and a nested recompute would corrupt it.
353
+ // A mid-pass mark stays latched for recompute's own tail to reschedule
354
+ // (#3037); readers meanwhile serve the values the pass has so far.
355
+ if (e.ie & REACTIVE_RECOMPUTING_DEPS) return;
356
+ if (e.ie & REACTIVE_CHECK) {
357
+ for (let t = e.nt; t; t = t.it) {
358
+ const n = t.ut;
333
359
  const i = n.lt || n;
334
- if (i.ce) {
360
+ if (i.Se) {
335
361
  updateIfNecessary(i);
336
362
  }
337
- if (e.se & REACTIVE_DIRTY) {
363
+ if (e.ie & REACTIVE_DIRTY) {
338
364
  break;
339
365
  }
340
366
  }
341
367
  }
342
- if (e.se & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY) || e._ && e.de < clock && !e.Te) {
368
+ if (e.ie & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY) || e.o?._ && e.Te < clock && !e.o?.Ee) {
343
369
  recompute(e);
344
370
  }
345
- e.se = e.se & (REACTIVE_SNAPSHOT_STALE | REACTIVE_IN_HEAP | REACTIVE_IN_HEAP_HEIGHT);
371
+ e.ie = e.ie & (REACTIVE_SNAPSHOT_STALE | REACTIVE_IN_HEAP | REACTIVE_IN_HEAP_HEIGHT);
346
372
  }
347
373
 
348
374
  function computed(e, t) {
@@ -351,44 +377,72 @@ function computed(e, t) {
351
377
  // `T | undefined` node is a real commit #0. The typeof guard tolerates
352
378
  // non-object option values that older call shapes force through `as any`.
353
379
  const i = t !== null && typeof t === "object" && "loadingValue" in t;
354
- const l = {
380
+ const u = {
355
381
  id: inheritId(t, n, context),
356
- T: (n ? CONFIG_TRANSPARENT : 0) | (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (!context || t?.lazy ? CONFIG_AUTO_DISPOSE : 0) | (t?.sync ? CONFIG_SYNC : 0) | (t?.V ? CONFIG_NO_SNAPSHOT : 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
382
+ T: (n ? CONFIG_TRANSPARENT : 0) | (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (!context || t?.lazy ? CONFIG_AUTO_DISPOSE : 0) | (t?.sync ? CONFIG_SYNC : 0) | (t?.H ? CONFIG_NO_SNAPSHOT : 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
357
383
  Ue: t?.equals != null ? t.equals : isEqual,
358
- ut: t?.unobserved,
359
384
  he: null,
360
385
  C: context?.C ?? globalQueue,
361
386
  we: context?.we ?? defaultContext,
362
- qe: 0,
363
- ce: e,
387
+ Me: 0,
388
+ Se: e,
364
389
  be: i ? t.loadingValue : undefined,
365
390
  Le: 0,
366
- u: null,
367
- st: undefined,
368
- ot: null,
369
- tt: null,
391
+ ot: undefined,
392
+ st: null,
393
+ nt: null,
370
394
  Ye: null,
371
395
  Ze: 0,
372
- o: null,
396
+ u: null,
373
397
  rt: null,
374
- Fe: context,
375
- He: null,
398
+ ve: context,
399
+ Ve: null,
376
400
  ct: null,
377
401
  ke: null,
378
- se: t?.lazy ? REACTIVE_LAZY : REACTIVE_NONE,
402
+ ie: t?.lazy ? REACTIVE_LAZY : REACTIVE_NONE,
379
403
  // A loadingValue node is born committed: commit #0 is already in _value.
380
404
  S: i ? 0 : STATUS_UNINITIALIZED,
381
- de: clock,
382
- _e: NOT_PENDING,
383
- Qe: null,
384
- We: null,
385
- Te: null,
386
- Ne: null,
405
+ Te: clock,
406
+ Pe: NOT_PENDING,
407
+ _e: null,
408
+ _t: -1,
409
+ Ie: i,
410
+ // Cold machinery (async/transition/optimistic/verdict slots) lives one
411
+ // hop away in the lazily-allocated extension — the core literal MUST
412
+ // stay under V8's in-object boundary (§12: past ~39 fields every
413
+ // allocation spills to a backing store and creation cost ~4x's).
414
+ o: null
415
+ };
416
+ if (t?.unobserved) ext(u).ft = t.unobserved;
417
+ setupComputedNode(u, t);
418
+ return u;
419
+ }
420
+
421
+ /** Lazily allocate a node's cold extension (ONE shape for signals and
422
+ * computeds — `_x` access stays monomorphic). Installers write through
423
+ * this; hot paths read `el._x?._field` gated by the _config presence bits.
424
+ * Never call ext() just to store a field's default. */ function ext(e) {
425
+ return e.o ??= {
426
+ De: undefined,
427
+ Et: undefined,
428
+ Be: undefined,
429
+ Ge: undefined,
430
+ ge: undefined,
431
+ It: undefined,
432
+ t: 0,
433
+ Ee: null,
434
+ _: undefined,
435
+ ue: undefined,
436
+ le: undefined,
437
+ A: undefined,
387
438
  pe: false,
388
- Ee: i
439
+ l: null,
440
+ ft: undefined,
441
+ Qe: undefined,
442
+ We: null,
443
+ qe: null,
444
+ Nt: undefined
389
445
  };
390
- setupComputedNode(l, t);
391
- return l;
392
446
  }
393
447
 
394
448
  /**
@@ -396,52 +450,50 @@ function computed(e, t) {
396
450
  * so V8 sees the full hidden class shape at construction time. Effects always run in lazy
397
451
  * mode (recompute is called explicitly by `effect()`), so we hardcode the lazy bits and skip
398
452
  * the auto-dispose CONFIG bit (effect() previously cleared it post-construction).
399
- */ function createEffectNode(e, t, n, i, l, u) {
400
- const s = u?.transparent ?? false;
401
- const o = {
402
- id: inheritId(u, s, context),
403
- T: (s ? CONFIG_TRANSPARENT : 0) | (u?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (u?.sync ? CONFIG_SYNC : 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
453
+ */ function createEffectNode(e, t, n, i, u, l) {
454
+ const o = l?.transparent ?? false;
455
+ const s = {
456
+ id: inheritId(l, o, context),
457
+ T: (o ? CONFIG_TRANSPARENT : 0) | (l?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (l?.sync ? CONFIG_SYNC : 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
404
458
  Ue: false,
405
- ut: u?.unobserved,
406
459
  he: null,
407
460
  C: context?.C ?? globalQueue,
408
461
  we: context?.we ?? defaultContext,
409
- qe: 0,
410
- ce: e,
462
+ Me: 0,
463
+ Se: e,
411
464
  be: undefined,
412
465
  Le: 0,
413
- u: null,
414
- st: undefined,
415
- ot: null,
416
- tt: null,
466
+ ot: undefined,
467
+ st: null,
468
+ nt: null,
417
469
  Ye: null,
418
470
  Ze: 0,
419
- o: null,
471
+ u: null,
420
472
  rt: null,
421
- Fe: context,
422
- He: null,
473
+ ve: context,
474
+ Ve: null,
423
475
  ct: null,
424
476
  ke: null,
425
- se: REACTIVE_LAZY,
477
+ ie: REACTIVE_LAZY,
426
478
  S: STATUS_UNINITIALIZED,
427
- de: clock,
428
- _e: NOT_PENDING,
429
- Qe: null,
430
- We: null,
431
- Te: null,
432
- Ne: null,
433
- pe: false,
434
- Ee: false,
435
- Je: false,
436
- _t: undefined,
437
- ft: t,
438
- Et: n,
439
- Nt: undefined,
479
+ Te: clock,
480
+ Pe: NOT_PENDING,
481
+ _e: null,
482
+ _t: -1,
483
+ Ie: false,
484
+ Xe: false,
485
+ Tt: undefined,
486
+ dt: t,
487
+ St: n,
488
+ At: undefined,
440
489
  Re: i,
441
- i: l
490
+ o: null
442
491
  };
443
- setupComputedNode(o, lazyOptions);
444
- return o;
492
+ // Boundary effects carry a status channel; most effects never touch _x.
493
+ if (u !== undefined) ext(s).A = u;
494
+ if (l?.unobserved) ext(s).ft = l.unobserved;
495
+ setupComputedNode(s, lazyOptions);
496
+ return s;
445
497
  }
446
498
 
447
499
  const lazyOptions = {
@@ -449,24 +501,25 @@ const lazyOptions = {
449
501
  };
450
502
 
451
503
  function setupComputedNode(e, t) {
452
- e.ot = e;
453
- const n = context?.Tt ? context.It : context;
504
+ e.st = e;
505
+ const n = context?.Ct ? context.Ot : context;
454
506
  if (context) {
455
507
  const t = context.ke;
456
508
  if (t === null) {
457
509
  context.ke = e;
458
510
  } else {
459
- e.He = t;
511
+ e.Ve = t;
460
512
  t.ct = e;
461
513
  context.ke = e;
462
514
  }
463
515
  }
464
516
  if (n) e.Le = n.Le + 1;
465
- if (GlobalQueue.dt !== null) GlobalQueue.dt(e);
517
+ if (GlobalQueue.Rt !== null) GlobalQueue.Rt(e);
466
518
  !t?.lazy && recompute(e, true);
467
519
  if (snapshotCaptureActive && !t?.lazy) {
468
520
  if (!(e.S & STATUS_PENDING) && !(e.T & CONFIG_NO_SNAPSHOT)) {
469
- e.xe = e.be === undefined ? NO_SNAPSHOT : e.be;
521
+ ext(e).Qe = e.be === undefined ? NO_SNAPSHOT : e.be;
522
+ e.T |= CONFIG_HAS_SNAPSHOT;
470
523
  snapshotSources.add(e);
471
524
  }
472
525
  }
@@ -475,19 +528,31 @@ function setupComputedNode(e, t) {
475
528
  function signal(e, t, n = null) {
476
529
  const i = {
477
530
  Ue: t?.equals != null ? t.equals : isEqual,
478
- T: (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (t?.V ? CONFIG_NO_SNAPSHOT : 0),
479
- ut: t?.unobserved,
531
+ T: (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (t?.H ? CONFIG_NO_SNAPSHOT : 0),
480
532
  be: e,
481
- o: null,
533
+ u: null,
482
534
  rt: null,
483
- de: clock,
535
+ Te: clock,
484
536
  lt: n,
485
- ae: n?.u || null,
486
- _e: NOT_PENDING
537
+ ce: n?.o?.l || null,
538
+ Pe: NOT_PENDING,
539
+ // Signal-literal diet (§12e): NO _time/_fn/_statusFlags slots. Stores
540
+ // materialize one signal per touched leaf, so signal bytes are store
541
+ // bytes. _time is write-only on signals (every read site is computed-
542
+ // typed error-retry gating); _fn/_statusFlags read falsy-identically as
543
+ // missing properties on the shared paths (undefined masks to 0).
544
+ _e: null,
545
+ _t: -1,
546
+ o: null
487
547
  };
488
- n && (n.u = i);
548
+ if (t?.unobserved) ext(i).ft = t.unobserved;
549
+ if (n) {
550
+ ext(n).l = i;
551
+ n.T |= CONFIG_FW_CHILDREN;
552
+ }
489
553
  if (snapshotCaptureActive && !(i.T & CONFIG_NO_SNAPSHOT) && !((n?.S ?? 0) & STATUS_PENDING)) {
490
- i.xe = e === undefined ? NO_SNAPSHOT : e;
554
+ ext(i).Qe = e === undefined ? NO_SNAPSHOT : e;
555
+ i.T |= CONFIG_HAS_SNAPSHOT;
491
556
  snapshotSources.add(i);
492
557
  }
493
558
  return i;
@@ -495,13 +560,15 @@ function signal(e, t, n = null) {
495
560
 
496
561
  function optimisticSignal(e, t) {
497
562
  const n = signal(e, t);
498
- n.De = NOT_PENDING;
563
+ ext(n).De = NOT_PENDING;
564
+ n.T |= CONFIG_OPTIMISTIC;
499
565
  return n;
500
566
  }
501
567
 
502
568
  function optimisticComputed(e, t) {
503
569
  const n = computed(e, t);
504
- n.De = NOT_PENDING;
570
+ ext(n).De = NOT_PENDING;
571
+ n.T |= CONFIG_OPTIMISTIC;
505
572
  return n;
506
573
  }
507
574
 
@@ -531,11 +598,11 @@ function isEqual(e, t) {
531
598
  * );
532
599
  * ```
533
600
  */ function untrack(e, t) {
534
- if (GlobalQueue.St === null && !tracking && true) return e();
601
+ if (GlobalQueue.Gt === null && !tracking && true) return e();
535
602
  const n = tracking;
536
603
  tracking = false;
537
604
  try {
538
- if (GlobalQueue.St !== null) return GlobalQueue.St(e);
605
+ if (GlobalQueue.Gt !== null) return GlobalQueue.Gt(e);
539
606
  return e();
540
607
  } finally {
541
608
  tracking = n;
@@ -547,11 +614,17 @@ function isEqual(e, t) {
547
614
  * an isPending() probe (`refresh`) additionally pulls the node fully up to
548
615
  * date so its status flags reflect the current graph.
549
616
  */ function prepareComputed(e, t) {
550
- if (e.se & REACTIVE_LAZY) {
551
- e.se &= ~REACTIVE_LAZY;
552
- recompute(e, true);
553
- } else if (e.se & REACTIVE_DISPOSED) {
617
+ if (e.ie & REACTIVE_LAZY) {
618
+ e.ie &= ~REACTIVE_LAZY;
554
619
  recompute(e, true);
620
+ } else if (e.ie & REACTIVE_DISPOSED) {
621
+ // Two disposal lifecycles share the flag (#3024). Observation-lifecycle
622
+ // nodes (CONFIG_AUTO_DISPOSE) are dormant — torn down by unobserved()
623
+ // when the last subscriber left — and reads reawaken them; that is the
624
+ // pay-for-use contract. Owner-lifecycle nodes are dead: recomputing would
625
+ // re-run user code in a torn-down tree (and discard manual writes on
626
+ // derived-writable signals), so reads return the last committed value.
627
+ if (e.T & CONFIG_AUTO_DISPOSE) recompute(e, true);
555
628
  } else if (t) {
556
629
  updateIfNecessary(e);
557
630
  }
@@ -571,15 +644,15 @@ function isEqual(e, t) {
571
644
  * snapshot / transition / lane / dev-strictRead state all take the full
572
645
  * resolution. Anything slow returns READ_SLOW; the caller then calls read().
573
646
  */ function readNodeFast(e) {
574
- if (latestReadActive || pendingCheckActive || e.ce || e.lt || e.De !== undefined || e.xe !== undefined || activeTransition !== null || currentOptimisticLane !== null || snapshotCaptureActive || false) return READ_SLOW;
647
+ if (latestReadActive || pendingCheckActive || e.Se || e.lt || e.o?.De !== undefined || e.o?.Qe !== undefined || activeTransition !== null || currentOptimisticLane !== null || snapshotCaptureActive || false) return READ_SLOW;
575
648
  let t = context;
576
- if (t?.Tt) t = t.It;
649
+ if (t?.Ct) t = t.Ot;
577
650
  if (t && tracking) link(e, t);
578
651
  // Children-forbidden readers (createTrackedEffect / onSettled callbacks) get
579
652
  // committed visibility: like the effect half of createEffect and event
580
653
  // handlers, effect-phase code never observes its own unsettled write — the
581
654
  // write lands in the same flush's continuation (#3006).
582
- return !t || e._e === NOT_PENDING || t.T & CONFIG_CHILDREN_FORBIDDEN ? e.be : e._e;
655
+ return !t || e.Pe === NOT_PENDING || t.T & CONFIG_CHILDREN_FORBIDDEN ? e.be : e.Pe;
583
656
  }
584
657
 
585
658
  function read(e) {
@@ -587,83 +660,93 @@ function read(e) {
587
660
  // Checked before isPending so that isPending(() => latest(x)) checks
588
661
  // the _pendingSignal of _latestValueComputed (async in flight) rather
589
662
  // than the original node (which stays "pending" while held in a transition).
590
- if (latestReadActive) return GlobalQueue.At(e);
663
+ if (latestReadActive) return GlobalQueue.Pt(e);
591
664
  let t = context;
592
- if (t?.Tt) t = t.It;
665
+ if (t?.Ct) t = t.Ot;
593
666
  const n = e;
594
667
  const i = e.lt;
595
- const l = i || e;
668
+ const u = i || e;
596
669
  // Handle isPending() mode: collect pending state while preserving normal read semantics.
597
670
  // Probe mode is suspended while preparing the node so nested reads during a
598
671
  // recompute don't collect into the probe.
599
672
  if (pendingCheckActive) {
600
- GlobalQueue.Ct(e, t, l, i);
601
- } else if (typeof n.ce === "function") {
673
+ GlobalQueue.Dt(e, t, u, i);
674
+ } else if (typeof n.Se === "function") {
602
675
  prepareComputed(e, false);
603
676
  }
604
- if (!n.ce && l === e && e.De === undefined && e.xe === undefined && activeTransition === null && currentOptimisticLane === null && !snapshotCaptureActive && true) {
677
+ if (!n.Se && u === e && e.o?.De === undefined && e.o?.Qe === undefined && activeTransition === null && currentOptimisticLane === null && !snapshotCaptureActive && true) {
605
678
  if (t && tracking) link(e, t);
606
679
  // Committed visibility for children-forbidden readers — see readNodeFast.
607
- return !t || e._e === NOT_PENDING || t.T & CONFIG_CHILDREN_FORBIDDEN ? e.be : e._e;
680
+ return !t || e.Pe === NOT_PENDING || t.T & CONFIG_CHILDREN_FORBIDDEN ? e.be : e.Pe;
608
681
  }
609
682
  if (t && tracking) {
610
683
  link(e, t, pendingCheckActive);
611
- if (l.ce) {
684
+ if (u.Se) {
612
685
  const n = queueFor(e);
613
- if (l.Le >= n.Ve) {
686
+ if (u.Le >= n.xe) {
614
687
  markNode(t);
615
688
  markHeap(n);
616
- updateIfNecessary(l);
689
+ updateIfNecessary(u);
617
690
  }
618
- const i = l.Le;
691
+ const i = u.Le;
619
692
  // parent check is shallow, might need to be recursive
620
- if (i >= t.Le && e.Fe !== t) {
693
+ if (i >= t.Le && e.ve !== t) {
621
694
  t.Le = i + 1;
622
695
  }
623
696
  }
624
697
  }
625
- if (l.S & STATUS_PENDING) {
626
- if (t && !(stale && l.Ne && activeTransition !== l.Ne)) {
698
+ if (u.S & STATUS_PENDING) {
699
+ if (t && !(stale && u._e && activeTransition !== u._e)) {
627
700
  // Per-lane suspension lives with the engine (a non-null lane implies it
628
701
  // is installed): under a lane, only same-lane pending async without an
629
702
  // active override throws.
630
- if (currentOptimisticLane === null || GlobalQueue.Ot(l)) {
703
+ if (currentOptimisticLane === null || GlobalQueue.ht(u)) {
631
704
  if (!tracking && e !== t) link(e, t);
632
- throw l._;
705
+ throw u.o?._;
633
706
  }
634
- } else if (t && l !== e && l.S & STATUS_UNINITIALIZED) {
707
+ } else if (t && u.S & STATUS_UNINITIALIZED) {
708
+ // A stale (render) reader of a node held pending in ANOTHER transition
709
+ // normally keeps showing the node's committed value instead of
710
+ // entangling the two transactions — but an uninitialized node has no
711
+ // committed value to show. Suspend on it (firewall-backed store reads
712
+ // always took this branch; plain memos now do too): the reader
713
+ // registers as a reporter of that source, and its pending-node stamp
714
+ // ties it to the active transaction, so the two transactions merge
715
+ // when the source settles. Falling through served `undefined` as if
716
+ // settled and stranded the reader outside both transactions, so it
717
+ // never re-ran when either landed (#3043 port).
635
718
  if (!tracking && e !== t) link(e, t);
636
- throw l._;
637
- } else if (!t && l.S & STATUS_UNINITIALIZED) {
638
- throw l._;
719
+ throw u.o?._;
720
+ } else if (!t && u.S & STATUS_UNINITIALIZED) {
721
+ throw u.o?._;
639
722
  }
640
723
  }
641
724
  // `owner` is the computed itself, or the firewall behind a store node —
642
725
  // firewall-backed reads follow the same rules (memo parity, #2897 ruling):
643
726
  // an errored derive throws for every late reader instead of silently
644
727
  // serving node values (the seed, or last-good data after a failed refetch).
645
- if (l.ce && l.S & STATUS_ERROR) {
728
+ if (u.Se && u.S & STATUS_ERROR) {
646
729
  // Only a genuine reactive re-read may retry an errored async source:
647
730
  // - tracking: owned/tracked scope only (never events / `untrack` / effect side-effect phase)
648
731
  // - !pendingCheckActive: an `isPending` probe observes the error, never refetches
649
732
  // - owner._time < clock: only on a later cycle than the one the error was found
650
- if (tracking && !pendingCheckActive && l.de < clock) {
651
- recompute(l);
733
+ if (tracking && !pendingCheckActive && u.Te < clock) {
734
+ recompute(u);
652
735
  return read(e);
653
- } else throw l._;
736
+ } else throw u.o?._;
654
737
  }
655
738
  if (snapshotCaptureActive && t && t.T & CONFIG_IN_SNAPSHOT_SCOPE) {
656
- const n = e.xe;
739
+ const n = e.o?.Qe;
657
740
  if (n !== undefined) {
658
741
  const i = n === NO_SNAPSHOT ? undefined : n;
659
- const l = e._e !== NOT_PENDING ? e._e : e.be;
660
- if (l !== i) t.se |= REACTIVE_SNAPSHOT_STALE;
742
+ const u = e.Pe !== NOT_PENDING ? e.Pe : e.be;
743
+ if (u !== i) t.ie |= REACTIVE_SNAPSHOT_STALE;
661
744
  return i;
662
745
  }
663
746
  }
664
- if (e.De !== undefined && e.De !== NOT_PENDING) {
747
+ if (e.o?.De !== undefined && e.o?.De !== NOT_PENDING) {
665
748
  // A17: the override IS the value for every reader.
666
- return unwrapOverride(e.De);
749
+ return unwrapOverride(e.o?.De);
667
750
  }
668
751
  // Entanglement gate: a reader recomputing under an optimistic lane that reads
669
752
  // a pending mid-transition write sees the committed value. Projection-store
@@ -672,7 +755,7 @@ function read(e) {
672
755
  // _pendingValue for correct fetching. The sub is recorded for replay at commit
673
756
  // so it re-runs with the new committed view. (Gate details live with the
674
757
  // engine — a non-null lane implies it is installed.)
675
- if (currentOptimisticLane !== null && activeTransition !== null && t !== null && GlobalQueue.Rt(e, l, t)) {
758
+ if (currentOptimisticLane !== null && activeTransition !== null && t !== null && GlobalQueue.Ft(e, u, t)) {
676
759
  return e.be;
677
760
  }
678
761
  // In optimistic lane context, return _value for optimistic/lane-assigned signals
@@ -681,36 +764,48 @@ function read(e) {
681
764
  // (The lane-context clause lives with the engine.) Children-forbidden readers
682
765
  // (createTrackedEffect / onSettled callbacks) get committed visibility — see
683
766
  // readNodeFast (#3006).
684
- const u = !t || currentOptimisticLane !== null && GlobalQueue.Dt(e, l, t) || e._e === NOT_PENDING || t.T & CONFIG_CHILDREN_FORBIDDEN || stale && e.Ne && activeTransition !== e.Ne ? e.be : e._e;
767
+ const l = !t || currentOptimisticLane !== null && GlobalQueue.gt(e, u, t) || e.Pe === NOT_PENDING || t.T & CONFIG_CHILDREN_FORBIDDEN || stale && e._e && activeTransition !== e._e ? e.be : e.Pe;
685
768
  // Record that this isPending() probe observed the fresh pending value, so
686
769
  // the probe doesn't pair "pending" with the new value (#2831).
687
- if (pendingCheckActive) GlobalQueue.Pt(e, u);
688
- if (!t && l === e && typeof n.ce === "function" && e.T & CONFIG_AUTO_DISPOSE && !(l.S & STATUS_PENDING) && !e.o) {
770
+ if (pendingCheckActive) GlobalQueue.Ht(e, l);
771
+ if (!t && u === e && typeof n.Se === "function" && e.T & CONFIG_AUTO_DISPOSE && !(u.S & STATUS_PENDING) && !e.u) {
689
772
  unobserved(e);
690
773
  }
691
- return u;
774
+ return l;
692
775
  }
693
776
 
694
777
  function setSignal(e, t) {
695
- if (e.Ne && activeTransition !== e.Ne) globalQueue.initTransition(e.Ne);
778
+ if (e._e && activeTransition !== e._e) globalQueue.initTransition(e._e);
696
779
  // The optimistic write path lives with the engine: only optimisticSignal /
697
780
  // optimisticComputed callers and optimistic store nodes carry an
698
- // _overrideValue slot, and every module that creates one installs the
699
- // engine first.
700
- if (e.De !== undefined && !projectionWriteActive) return GlobalQueue.Gt(e, t);
701
- const n = e._e === NOT_PENDING ? e.be : e._e;
781
+ // _overrideValue slot (flagged by CONFIG_OPTIMISTIC a masked read of the
782
+ // always-present config instead of a missing-property probe), and every
783
+ // module that installs one installs the engine first.
784
+ if (e.T & CONFIG_OPTIMISTIC && !projectionWriteActive) return GlobalQueue.vt(e, t);
785
+ const n = e.Pe === NOT_PENDING ? e.be : e.Pe;
702
786
  if (typeof t === "function") t = t(n);
703
787
  // Uninitialized check first: the first commit has no previous value, so the
704
788
  // user comparator must not run against `undefined` (matches recompute).
705
789
  const i = !!(e.S & STATUS_UNINITIALIZED) || !e.Ue || !e.Ue(n, t);
706
790
  if (!i) return t;
707
- if (e._e === NOT_PENDING) queuePendingNode(e);
708
- e._e = t;
791
+ const u = e.Pe !== NOT_PENDING;
792
+ if (!u) queuePendingNode(e);
793
+ e.Pe = t;
709
794
  // syncCompanions only pokes _pendingSignal/_latestValueComputed — with
710
795
  // neither companion present the call is a guaranteed no-op (companions are
711
- // only ever created, never removed, and creating one installs the hook).
712
- (e.Oe !== undefined || e.ge !== undefined) && GlobalQueue.Pe !== null && GlobalQueue.Pe(e, t);
713
- e.de = clock;
796
+ // only ever created, never removed, and creating one installs the hook and
797
+ // sets CONFIG_HAS_COMPANIONS one masked read replaces two optional-field
798
+ // probes on every write).
799
+ e.T & CONFIG_HAS_COMPANIONS && GlobalQueue.Oe !== null && GlobalQueue.Oe(e, t);
800
+ // _time is a computed-only slot (§12e): writing it on a signal would fork
801
+ // the lean shape. Every read site is computed-typed.
802
+ if (e.Se !== undefined) e.Te = clock;
803
+ // Staged-rewrite fast path (§12d): a re-write to a node whose subscribers
804
+ // were already walked — and where nothing has recomputed or linked since
805
+ // (epoch) — re-stages the value and stops. The walk is idempotent (subs
806
+ // marked, heap entries flag-guarded, effects queued once); lane and reask
807
+ // contexts change what a walk MEANS, so they always walk.
808
+ if (u && e._t === notifyEpoch && currentOptimisticLane === null && !reaskArmed) return t;
714
809
  insertSubs(e);
715
810
  schedule();
716
811
  return t;
@@ -724,11 +819,12 @@ function setSignal(e, t) {
724
819
  * cleanup point.
725
820
  */ function suppressComputedRecompute(e) {
726
821
  deleteFromHeap(e, queueFor(e));
727
- if (!(e.se & REACTIVE_MANUAL_WRITE) && e._e === NOT_PENDING) {
822
+ if (!(e.ie & REACTIVE_MANUAL_WRITE) && e.Pe === NOT_PENDING) {
728
823
  queuePendingNode(e);
729
824
  schedule();
730
825
  }
731
- e.se = e.se & -4 | REACTIVE_MANUAL_WRITE;
826
+ e.ie = e.ie & -4 | REACTIVE_MANUAL_WRITE;
827
+ e.kt = clock;
732
828
  }
733
829
 
734
830
  /**
@@ -807,7 +903,19 @@ function staleValues(e, t = true) {
807
903
  if (!t) {
808
904
  return;
809
905
  }
810
- if (typeof t.ce === "function" && !(t.se & (REACTIVE_DISPOSED | REACTIVE_MANUAL_WRITE))) {
906
+ if (typeof t.Se === "function" && !(t.ie & REACTIVE_DISPOSED)) {
907
+ if (t.ie & REACTIVE_MANUAL_WRITE) {
908
+ // A manual write in the CURRENT tick wins over the refresh (#2692).
909
+ // A mask stamped in an earlier tick only survives because a
910
+ // transaction (action) is holding the pending drain open; there the
911
+ // refresh is a later, explicit re-ask and lifts the mask — otherwise
912
+ // any setStore early in an action silently swallows every refresh()
913
+ // for the rest of the transaction (#3026).
914
+ if (t.kt === clock) return;
915
+ t.ie &= ~REACTIVE_MANUAL_WRITE;
916
+ // No REASK below: the batch carries a manual value change, so the
917
+ // recompute is not a quiet re-ask of an unchanged question.
918
+ }
811
919
  // A refresh with no value-change dirt already queued is a re-ask of the
812
920
  // same question: mark it so the recompute classifies any resulting
813
921
  // pending window as quiet (not pending). If the node is already dirty
@@ -815,14 +923,14 @@ function staleValues(e, t = true) {
815
923
  // REACTIVE_IN_HEAP counts as dirt: insertSubs schedules subscribers by
816
924
  // heap insertion alone (no DIRTY/CHECK flag), so a same-batch value
817
925
  // change followed by refresh() must not be laundered into a quiet re-ask.
818
- if (!(t.se & (REACTIVE_DIRTY | REACTIVE_CHECK | REACTIVE_IN_HEAP))) {
819
- t.se |= REACTIVE_REASK;
926
+ else if (!(t.ie & (REACTIVE_DIRTY | REACTIVE_CHECK | REACTIVE_IN_HEAP))) {
927
+ t.ie |= REACTIVE_REASK;
820
928
  armReaskClear();
821
929
  }
822
- t.se = t.se & ~REACTIVE_CHECK | REACTIVE_DIRTY;
930
+ t.ie = t.ie & ~REACTIVE_CHECK | REACTIVE_DIRTY;
823
931
  insertIntoHeap(t, queueFor(t));
824
932
  schedule();
825
933
  }
826
934
  }
827
935
 
828
- export { READ_SLOW, clearSnapshots, computed, context, createEffectNode, currentOptimisticLane, isEqual, latestReadActive, markSnapshotScope, optimisticComputed, optimisticSignal, pendingCheckActive, prepareComputed, read, readNodeFast, recompute, refresh, releaseSnapshotScope, runWithOwner, setContextInternal, setLatestReadActive, setMemo, setPendingCheckActive, setSignal, setSnapshotCapture, signal, snapshotCaptureActive, snapshotSources, stale, staleValues, suppressComputedRecompute, tracking, untrack };
936
+ export { READ_SLOW, clearSnapshots, computed, context, createEffectNode, currentOptimisticLane, ext, isEqual, latestReadActive, markSnapshotScope, optimisticComputed, optimisticSignal, pendingCheckActive, prepareComputed, read, readNodeFast, recompute, refresh, releaseSnapshotScope, runWithOwner, setContextInternal, setLatestReadActive, setMemo, setPendingCheckActive, setSignal, setSnapshotCapture, signal, snapshotCaptureActive, snapshotSources, stale, staleValues, suppressComputedRecompute, tracking, untrack };