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

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,8 +24,8 @@ 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.o?.Oe !== NOT_PENDING;
28
- const i = t ? unwrapOverride(e.o?.Oe) : e.be;
27
+ const t = e.o?.Pe !== NOT_PENDING;
28
+ const i = t ? unwrapOverride(e.o?.Pe) : e.be;
29
29
  if (typeof n === "function") n = n(i);
30
30
  const u = !!(e.S & STATUS_UNINITIALIZED) ||
31
31
  // A dirty node's _value is stale (its queued recompute hasn't run — e.g.
@@ -34,7 +34,7 @@ import { GlobalQueue, activeTransition, globalQueue, clock, insertSubs, schedule
34
34
  // push returning the shadow to that stale value was dropped, the snap
35
35
  // recompute then committed the parent's old value, and the banner showed
36
36
  // the previous transition's target (#3041 follow-up).
37
- !!((e.ie ?? 0) & (REACTIVE_DIRTY | REACTIVE_CHECK)) || !e.ge || !e.ge(i, n);
37
+ !!((e.ie ?? 0) & (REACTIVE_DIRTY | REACTIVE_CHECK)) || !e.pe || !e.pe(i, n);
38
38
  if (!u) {
39
39
  // Same-value write with an active override still entangles the current
40
40
  // action's transition — the hold must outlast all overlapping actions.
@@ -59,10 +59,10 @@ import { GlobalQueue, activeTransition, globalQueue, clock, insertSubs, schedule
59
59
  // Literal undefined must not land raw: the slot doubles as the optimistic
60
60
  // brand, and erasing it makes the write invisible and routes follow-up
61
61
  // writes off the optimistic path into permanent commits (#2898).
62
- ext(e).Oe = n === undefined ? OVERRIDE_UNDEFINED : n;
62
+ ext(e).Pe = n === undefined ? OVERRIDE_UNDEFINED : n;
63
63
  // syncCompanions only pokes _pendingSignal/_latestValueComputed — with
64
64
  // neither companion present the call is a guaranteed no-op.
65
- (e.o?.ye !== undefined || e.o?.Ce !== undefined) && GlobalQueue.Re !== null && GlobalQueue.Re(e, n);
65
+ (e.o?.ye !== undefined || e.o?.Ce !== undefined) && GlobalQueue.Ue !== null && GlobalQueue.Ue(e, n);
66
66
  if (e.oe !== undefined) e.Te = clock;
67
67
  // §12e: computed-only slot
68
68
  insertSubs(e, true);
@@ -96,8 +96,8 @@ function resolveOptimisticNodes(e) {
96
96
  // override-covered authoritative values hold in _pendingValue and
97
97
  // elevate on their OWN transition's schedule (A18 as re-ruled 2026-07-07).
98
98
  if (!(n.S & STATUS_PENDING)) n.S &= ~STATUS_UNINITIALIZED;
99
- const i = n.o?.Oe;
100
- ext(n).Oe = NOT_PENDING;
99
+ const i = n.o?.Pe;
100
+ ext(n).Pe = NOT_PENDING;
101
101
  if (i !== NOT_PENDING && n.be !== unwrapOverride(i)) insertSubs(n, true);
102
102
  n.Ae = null;
103
103
  if (n.o !== null) n.o.Nt = null;
@@ -122,7 +122,7 @@ 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.rn || n.De.size > 0) continue;
125
+ if (n.rn || n.Oe.size > 0) continue;
126
126
  const t = n.tn[e - 1];
127
127
  if (t.length) {
128
128
  n.tn[e - 1] = [];
@@ -143,7 +143,7 @@ function cleanupCompletedLanes(e) {
143
143
  if (n.tn[1].length) runQueue(n.tn[1], EFFECT_USER);
144
144
  }
145
145
  if (n.nn.o?.Je === n) if (n.nn.o !== null) n.nn.o.Je = undefined;
146
- n.De.clear();
146
+ n.Oe.clear();
147
147
  n.tn[0].length = 0;
148
148
  n.tn[1].length = 0;
149
149
  activeLanes.delete(n);
@@ -165,7 +165,7 @@ function cleanupCompletedLanes(e) {
165
165
  * that reads a pending mid-transition write sees the committed value; the sub
166
166
  * is recorded for replay at commit.
167
167
  */ function gatedRead(e, n, t) {
168
- if (latestReadActive || e.Pe === NOT_PENDING || e.oe || n !== e && !(n.ie & REACTIVE_MANUAL_WRITE)) {
168
+ if (latestReadActive || e.Re === NOT_PENDING || e.oe || n !== e && !(n.ie & REACTIVE_MANUAL_WRITE)) {
169
169
  return false;
170
170
  }
171
171
  activeTransition.cn.add(t);
@@ -176,7 +176,7 @@ function cleanupCompletedLanes(e) {
176
176
  * read()'s value selection under a lane: return the committed `_value` for
177
177
  * optimistic/lane-assigned signals, stale-mode reads, and pending owners.
178
178
  */ function laneReadsCommitted(e, n, t) {
179
- if (e.o?.Oe !== undefined || !!e.o?.Je || !!(n.S & STATUS_PENDING)) {
179
+ if (e.o?.Pe !== undefined || !!e.o?.Je || !!(n.S & STATUS_PENDING)) {
180
180
  // The committed view hides a staged in-flight value that will promote
181
181
  // silently (commitPendingNode never re-notifies). gatedRead records plain
182
182
  // signals for replay at commit; async memos are excluded from it by the
@@ -185,7 +185,7 @@ function cleanupCompletedLanes(e) {
185
185
  // committed value to a reader that never re-ran after the landing, so a
186
186
  // pending-gated branch stayed one value behind permanently (#3041
187
187
  // follow-up). Record the reader under the same replay contract.
188
- if (e.Pe !== NOT_PENDING) (activeTransition ?? globalQueue.m).cn.add(t);
188
+ if (e.Re !== NOT_PENDING) (activeTransition ?? globalQueue.m).cn.add(t);
189
189
  return true;
190
190
  }
191
191
  if (n === e && stale && t.o?.Tt !== e) {
@@ -200,7 +200,7 @@ function cleanupCompletedLanes(e) {
200
200
  // into the transaction (#3041 follow-up: a pending-gated branch that
201
201
  // first read its async source during the landing flush stayed one value
202
202
  // behind permanently); with none, into the ambient batch.
203
- if (e.Pe !== NOT_PENDING) (activeTransition ?? globalQueue.m).cn.add(t);
203
+ if (e.Re !== NOT_PENDING) (activeTransition ?? globalQueue.m).cn.add(t);
204
204
  return true;
205
205
  }
206
206
  return false;
@@ -223,7 +223,7 @@ function cleanupCompletedLanes(e) {
223
223
  // recompute() runs plain: the value stages and commits with the flush.
224
224
  // (el's own override slot excludes companions themselves; a lane merged
225
225
  // into a real optimistic lane resolves to a non-companion source.)
226
- if (!globalQueue.fn && !activeTransition && !n.Ae && n.nn.o?.Tt !== undefined && e.o?.Oe === undefined) {
226
+ if (!globalQueue.fn && !activeTransition && !n.Ae && n.nn.o?.Tt !== undefined && e.o?.Pe === undefined) {
227
227
  if (e.o !== null) e.o.Je = undefined;
228
228
  return false;
229
229
  }
@@ -246,7 +246,7 @@ function cleanupCompletedLanes(e) {
246
246
  /** recompute()'s catch path: track pending async in the current lane. */ function laneAsyncPending(e) {
247
247
  const n = findLane(currentOptimisticLane);
248
248
  if (n.nn !== e) {
249
- n.De.add(e);
249
+ n.Oe.add(e);
250
250
  ext(e).Je = n;
251
251
  e.T |= CONFIG_HAS_LANE;
252
252
  GlobalQueue.de !== null && GlobalQueue.de(n.nn);
@@ -256,7 +256,7 @@ function cleanupCompletedLanes(e) {
256
256
  /** recompute()'s success path: the node's async settled, clear it from its lane. */ function laneAsyncSettled(e) {
257
257
  const n = resolveLane(e);
258
258
  if (n) {
259
- n.De.delete(e);
259
+ n.Oe.delete(e);
260
260
  GlobalQueue.de !== null && GlobalQueue.de(n.nn);
261
261
  }
262
262
  }
@@ -107,7 +107,7 @@ function disposeChildren(e, t = false, n) {
107
107
  }
108
108
 
109
109
  function runDisposal(e, t) {
110
- let n = t ? e.o?.qe : e.pe;
110
+ let n = t ? e.o?.qe : e.Ge;
111
111
  if (!n) return;
112
112
  if (Array.isArray(n)) {
113
113
  for (let e = 0; e < n.length; e++) {
@@ -119,7 +119,7 @@ function runDisposal(e, t) {
119
119
  }
120
120
  if (t) {
121
121
  if (e.o !== null) e.o.qe = null;
122
- } else e.pe = null;
122
+ } else e.Ge = null;
123
123
  }
124
124
 
125
125
  function childId(e, t) {
@@ -206,7 +206,7 @@ function formatId(e, t) {
206
206
  * checks. `cleanup()` is the unchecked primitive used by internals.
207
207
  */ function cleanup(e) {
208
208
  if (!context) return e;
209
- if (!context.pe) context.pe = e; else if (Array.isArray(context.pe)) context.pe.push(e); else context.pe = [ context.pe, e ];
209
+ if (!context.Ge) context.Ge = e; else if (Array.isArray(context.Ge)) context.Ge.push(e); else context.Ge = [ context.Ge, e ];
210
210
  return e;
211
211
  }
212
212
 
@@ -251,7 +251,7 @@ function disposeRootSelf(e = true) {
251
251
  xe: null,
252
252
  Le: null,
253
253
  ft: null,
254
- pe: null,
254
+ Ge: null,
255
255
  C: t?.C ?? globalQueue,
256
256
  we: t?.we || defaultContext,
257
257
  Ze: 0,
@@ -73,8 +73,8 @@ function sweepTransientStoreNodes() {
73
73
  transientStoreNodes.delete(e);
74
74
  continue;
75
75
  }
76
- if (e.Pe !== NOT_PENDING) continue;
77
- if (e.o?.Oe !== undefined && e.o?.Oe !== NOT_PENDING) continue;
76
+ if (e.Re !== NOT_PENDING) continue;
77
+ if (e.o?.Pe !== undefined && e.o?.Pe !== NOT_PENDING) continue;
78
78
  // A live affects() mark keeps the node addressable: sweeping it would
79
79
  // detach the refcount from the slot (a fresh probe would upsert a new,
80
80
  // unmarked node for the same property).
@@ -202,9 +202,9 @@ function mergeTransitionState(e, t) {
202
202
  // reporter deadlocked exactly that way).
203
203
  let i = false;
204
204
  for (let n = e.ut; n !== null; n = n.lt) {
205
- const s = n.ot;
206
- if (s.Pe !== NOT_PENDING) {
207
- const e = s.Ae;
205
+ const r = n.ot;
206
+ if (r.Re !== NOT_PENDING) {
207
+ const e = r.Ae;
208
208
  const n = e != null ? currentTransition(e) : null;
209
209
  // Skip the awaiting transaction's own cargo (t === target: a
210
210
  // fold-staged landing or a write the action itself issued — hold and
@@ -212,8 +212,8 @@ function mergeTransitionState(e, t) {
212
212
  // (t === null) must leave the batch NOW — it commits at this flush's
213
213
  // end, which would reveal the confirmation under the live optimism
214
214
  // it just confirmed.
215
- const r = n === null ? currentBatch.Lt : n !== t && n.sn !== true ? n.Lt : null;
216
- if (r !== null) i = stealEntangledCargo(r, t) || i;
215
+ const s = n === null ? currentBatch.Lt : n !== t && n.sn !== true ? n.Lt : null;
216
+ if (s !== null) i = stealEntangledCargo(s, t) || i;
217
217
  }
218
218
  if (n === e.je) break;
219
219
  }
@@ -257,7 +257,7 @@ function mergeTransitionState(e, t) {
257
257
  n.T |= CONFIG_HELD_TRUTH;
258
258
  for (let e = n.u; e !== null; e = e.ae) {
259
259
  const t = e.ce;
260
- if (t.he && !(t.T & CONFIG_AUTHORITATIVE_READ)) enqueueSub(t);
260
+ if (t.ge && !(t.T & CONFIG_AUTHORITATIVE_READ)) enqueueSub(t);
261
261
  }
262
262
  }
263
263
  }
@@ -343,11 +343,11 @@ class Queue {
343
343
  const t = this.Qt;
344
344
  const i = ++queueRunToken;
345
345
  for (let n = 0; n < t.length; ) {
346
- const s = t[n];
347
- if (s.jt !== i) {
348
- s.jt = i;
349
- s.run?.(e);
350
- if (t[n] !== s) {
346
+ const r = t[n];
347
+ if (r.jt !== i) {
348
+ r.jt = i;
349
+ r.run?.(e);
350
+ if (t[n] !== r) {
351
351
  n = 0;
352
352
  continue;
353
353
  }
@@ -425,7 +425,7 @@ class GlobalQueue extends Queue {
425
425
  // there: `_pendingSignal`/`_latestValueComputed` are only ever assigned by
426
426
  // verdict.ts, and `pendingCheckActive`/`latestReadActive` only flip inside
427
427
  // isPending()/latest()).
428
- static Re=null;
428
+ static Ue=null;
429
429
  static de=null;
430
430
  static me=null;
431
431
  static un=null;
@@ -462,7 +462,7 @@ class GlobalQueue extends Queue {
462
462
  /** Authoritative-view reader wakeup (until()): installed at first until() call.
463
463
  * Call sites are gated by CONFIG_AUTHORITATIVE_OBSERVED, which only until()'s
464
464
  * carve-out read can set, so `!` invocations are safe once the gate holds. */
465
- static Ue=null;
465
+ static he=null;
466
466
  static Xe=null;
467
467
  static _n=null;
468
468
  flush() {
@@ -591,9 +591,9 @@ class GlobalQueue extends Queue {
591
591
  const i = t.source;
592
592
  let n = activeTransition._e.get(i);
593
593
  if (!n) activeTransition._e.set(i, n = new Set);
594
- const s = n.size;
594
+ const r = n.size;
595
595
  n.add(e);
596
- if (n.size !== s) {
596
+ if (n.size !== r) {
597
597
  schedule();
598
598
  GlobalQueue.zt?.(activeTransition);
599
599
  }
@@ -704,13 +704,13 @@ function insertSubs(e, t = false) {
704
704
  // stays authoritative when a bit is set.
705
705
  const i = e.T;
706
706
  const n = (i & CONFIG_HAS_LANE ? e.o?.Je : undefined) || currentOptimisticLane;
707
- const s = (i & CONFIG_HAS_SNAPSHOT) !== 0 && e.o?.We !== undefined;
708
- const r = reaskArmed;
707
+ const r = (i & CONFIG_HAS_SNAPSHOT) !== 0 && e.o?.We !== undefined;
708
+ const s = reaskArmed;
709
709
  for (let i = e.u; i !== null; i = i.ae) {
710
710
  const e = i.ce;
711
711
  // A value-change notification is a new question for the subscriber: any
712
712
  // pending re-ask mark (refresh) it carried is superseded.
713
- if (r) e.ie &= ~REACTIVE_REASK;
713
+ if (s) e.ie &= ~REACTIVE_REASK;
714
714
  // Missed-wake latch (#3037): this write is landing while the subscriber
715
715
  // is mid-recompute (a nested pull committing beneath its reads), and the
716
716
  // heap refuses RECOMPUTING nodes. A gen-current link means the pass
@@ -720,7 +720,7 @@ function insertSubs(e, t = false) {
720
720
  // tail link: it is the read IN FLIGHT — read() links before it pulls, so
721
721
  // this very commit is what that read returns.
722
722
  if (e.ie & REACTIVE_RECOMPUTING_DEPS && i.Ft === e.Ke && i !== e.je) e.ie |= REACTIVE_MISSED_WAKE;
723
- if (s && e.T & CONFIG_IN_SNAPSHOT_SCOPE) {
723
+ if (r && e.T & CONFIG_IN_SNAPSHOT_SCOPE) {
724
724
  e.ie |= REACTIVE_SNAPSHOT_STALE;
725
725
  continue;
726
726
  }
@@ -739,18 +739,24 @@ function insertSubs(e, t = false) {
739
739
  function commitPendingNode(e) {
740
740
  const t = e;
741
741
  if (!t.oe) {
742
- if (e.Pe !== NOT_PENDING) {
743
- e.be = e.Pe;
744
- e.Pe = NOT_PENDING;
742
+ if (e.Re !== NOT_PENDING) {
743
+ e.be = e.Re;
744
+ e.Re = NOT_PENDING;
745
745
  }
746
746
  if (e.T & CONFIG_HAS_COMPANIONS) GlobalQueue.un(e);
747
747
  return;
748
748
  }
749
- if (e.Pe !== NOT_PENDING) {
750
- e.be = e.Pe;
751
- e.Pe = NOT_PENDING;
749
+ if (e.Re !== NOT_PENDING) {
750
+ e.be = e.Re;
751
+ e.Re = NOT_PENDING;
752
752
  // Set _modified for effects, but not for tracked effects (they handle their own scheduling)
753
- if (e.he && e.he !== EFFECT_TRACKED) e.tt = true;
753
+ if (e.ge && e.ge !== EFFECT_TRACKED) e.tt = true;
754
+ // A quiet re-ask classification preserved through a held landing dies
755
+ // with the value commit — the commit IS the reveal (#3178). Gated on the
756
+ // staged value: status propagation queues pending nodes whose windows
757
+ // are still OPEN (no staged value), and their live classification must
758
+ // survive this sweep.
759
+ if (e.o) e.o.De = false;
754
760
  }
755
761
  // The committed hold is the first observable answer for a loading-window
756
762
  // node — the window closes here, not at compute time (#2990). Unconditional
@@ -971,15 +977,15 @@ function transitionComplete(e) {
971
977
  if (e.ue.length) return false;
972
978
  let t = true;
973
979
  for (const [i, n] of e._e) {
974
- let s = false;
980
+ let r = false;
975
981
  for (const e of n) {
976
982
  if (reporterBlocksSource(e, i)) {
977
- s = true;
983
+ r = true;
978
984
  break;
979
985
  }
980
986
  n.delete(e);
981
987
  }
982
- if (!s) e._e.delete(i); else if (i.S & STATUS_PENDING && i.o?._?.source === i) {
988
+ if (!r) e._e.delete(i); else if (i.S & STATUS_PENDING && i.o?._?.source === i) {
983
989
  t = false;
984
990
  break;
985
991
  }
@@ -992,6 +998,13 @@ function transitionComplete(e) {
992
998
  return t;
993
999
  }
994
1000
 
1001
+ /** A fresh, unentered transaction (#3146): the optimistic store's truth
1002
+ * flight DECLARES an owned transaction instead of relying on whatever the
1003
+ * ambient adoption machinery stamped on its firewall. Activate it with
1004
+ * initTransition; it is a plain batch until then. */ function createTransition() {
1005
+ return createBatch();
1006
+ }
1007
+
995
1008
  function currentTransition(e) {
996
1009
  while (e.sn && typeof e.sn === "object") e = e.sn;
997
1010
  return e;
@@ -1030,4 +1043,4 @@ function runInTransition(e, t) {
1030
1043
  }
1031
1044
  }
1032
1045
 
1033
- export { GlobalQueue, Queue, activeAffectsMarks, activeLanes, activeTransition, armReaskClear, assignOrMergeLane, bumpNotifyEpoch, clock, currentTransition, dirtyQueue, enforceLoadingBoundary, entangleConfirmingTransitions, finalizePureQueue, findLane, flush, globalQueue, haltReactivity, hasActiveOverride, insertSubs, notifyEpoch, patchCommitHook, projectionWriteActive, queuePendingNode, reaskArmed, resetErrorHalt, runAsTransitionBatch, runInTransition, schedule, setPatchCommitHook, setProjectionWriteActive, setStoreCommitHook, shiftAffectsMarks, storeCommitHook, zombieQueue };
1046
+ export { GlobalQueue, Queue, activeAffectsMarks, activeLanes, activeTransition, armReaskClear, assignOrMergeLane, bumpNotifyEpoch, clock, createTransition, currentTransition, dirtyQueue, enforceLoadingBoundary, entangleConfirmingTransitions, finalizePureQueue, findLane, flush, globalQueue, haltReactivity, hasActiveOverride, insertSubs, notifyEpoch, patchCommitHook, projectionWriteActive, queuePendingNode, reaskArmed, resetErrorHalt, runAsTransitionBatch, runInTransition, schedule, setPatchCommitHook, setProjectionWriteActive, setStoreCommitHook, shiftAffectsMarks, storeCommitHook, zombieQueue };
@@ -128,10 +128,10 @@ function collectPendingSources(e) {
128
128
 
129
129
  function quietPending(e) {
130
130
  if (e.o?.le) {
131
- for (const t of e.o.le) if (!t.o?.Ge) return false;
131
+ for (const t of e.o.le) if (!t.o?.De) return false;
132
132
  return true;
133
133
  }
134
- return e.o?.Ge ?? false;
134
+ return e.o?.De ?? false;
135
135
  }
136
136
 
137
137
  // NOTE: a loadingValue node's open loading window (_loading) is verdict-quiet
@@ -158,16 +158,20 @@ function computePendingState(e) {
158
158
  const n = t.st || t;
159
159
  return newQuestionInFlight(n);
160
160
  }
161
- if (n && e.Pe !== NOT_PENDING && !hasActiveOverride(e)) {
161
+ if (n && e.Re !== NOT_PENDING && !hasActiveOverride(e)) {
162
162
  return !!(n.ie & REACTIVE_MANUAL_WRITE) || !n.o?.Ie && !(n.S & STATUS_PENDING) || !!(n.S & STATUS_PENDING) && quietPending(n);
163
163
  }
164
164
  // `!comp._loading`: a hold created while the loading window is still open is
165
165
  // the window's own landing in flight to its commit — verdict-quiet like the
166
166
  // rest of the window (the UNINITIALIZED check suppresses exactly this frame
167
167
  // for windowless first loads; born-committed nodes need their own gate, #2990).
168
- if (e.Pe !== NOT_PENDING && !(t.S & STATUS_UNINITIALIZED) && !t.Ne) {
169
- if (hasActiveOverride(e)) return !e.ge || !e.ge(e.Pe, unwrapOverride(e.o?.Oe));
170
- return true;
168
+ if (e.Re !== NOT_PENDING && !(t.S & STATUS_UNINITIALIZED) && !t.Ne) {
169
+ if (hasActiveOverride(e)) return !e.pe || !e.pe(e.Re, unwrapOverride(e.o?.Pe));
170
+ // A quiet re-ask's held landing still answers the same question: the
171
+ // classification survives the landing (asyncWrite) and dies with the
172
+ // commit (commitPendingNode) — verdict-quiet through the reveal, like
173
+ // the loading window above (#3178).
174
+ if (!t.o?.De) return true;
171
175
  }
172
176
  return newQuestionInFlight(t);
173
177
  }
@@ -250,18 +254,18 @@ function updateChildCompanions(e) {
250
254
  function snapCompanionsToState(e) {
251
255
  suppressedProbes.size !== 0 && suppressedProbes.delete(e);
252
256
  const t = e.o?.ye;
253
- if (t && (t.o?.Oe === undefined || t.o?.Oe === NOT_PENDING)) {
257
+ if (t && (t.o?.Pe === undefined || t.o?.Pe === NOT_PENDING)) {
254
258
  const n = computePendingState(e);
255
- if (t.be !== n || t.Pe !== NOT_PENDING) {
259
+ if (t.be !== n || t.Re !== NOT_PENDING) {
256
260
  t.be = n;
257
- t.Pe = NOT_PENDING;
261
+ t.Re = NOT_PENDING;
258
262
  insertSubs(t);
259
263
  schedule();
260
264
  }
261
265
  }
262
266
  const n = e.o?.Ce;
263
267
  if (n && !(n.ie & REACTIVE_DISPOSED)) {
264
- if ((n.o?.Oe === undefined || n.o?.Oe === NOT_PENDING) && n.Pe === NOT_PENDING && !Object.is(n.be, e.be) && !(n.ie & (REACTIVE_DIRTY | REACTIVE_CHECK))) {
268
+ if ((n.o?.Pe === undefined || n.o?.Pe === NOT_PENDING) && n.Re === NOT_PENDING && !Object.is(n.be, e.be) && !(n.ie & (REACTIVE_DIRTY | REACTIVE_CHECK))) {
265
269
  n.ie |= REACTIVE_DIRTY;
266
270
  insertIntoHeap(n, queueFor(n));
267
271
  insertSubs(n);
@@ -298,7 +302,7 @@ function getLatestValueComputed(e) {
298
302
  // created lazily, possibly after the write was processed — syncCompanions
299
303
  // only pushes into companions that already exist, so the first latest()
300
304
  // read inside a held transition showed the committed value (#3041).
301
- if (e.Pe !== NOT_PENDING && !hasActiveOverride(e)) setSignal(t, e.Pe);
305
+ if (e.Re !== NOT_PENDING && !hasActiveOverride(e)) setSignal(t, e.Re);
302
306
  setContextInternal(r);
303
307
  setPendingCheckActive(i);
304
308
  setLatestReadActive(n);
@@ -310,7 +314,7 @@ function getLatestValueComputed(e) {
310
314
  const t = getLatestValueComputed(e);
311
315
  const n = latestReadActive;
312
316
  setLatestReadActive(false);
313
- const i = e.o?.Oe !== undefined && e.o?.Oe !== NOT_PENDING ? unwrapOverride(e.o?.Oe) : e.be;
317
+ const i = e.o?.Pe !== undefined && e.o?.Pe !== NOT_PENDING ? unwrapOverride(e.o?.Pe) : e.be;
314
318
  let r;
315
319
  try {
316
320
  // An untracked latest() read has no reading context, so read() never
@@ -349,7 +353,7 @@ function getLatestValueComputed(e) {
349
353
  if (stale && currentOptimisticLane && t.o?.Je) {
350
354
  const e = findLane(t.o?.Je);
351
355
  const n = findLane(currentOptimisticLane);
352
- if (e !== n && e.De.size > 0) {
356
+ if (e !== n && e.Oe.size > 0) {
353
357
  return i;
354
358
  }
355
359
  }
@@ -357,7 +361,7 @@ function getLatestValueComputed(e) {
357
361
  // speculative value in _pendingValue; a contextless read() only surfaces
358
362
  // _value. Overrides stay authoritative (A17), and stale readers keep the
359
363
  // other transition's committed view, matching read()'s own selection.
360
- if (t.Pe !== NOT_PENDING && !hasActiveOverride(t) && !(stale && t.Ae && activeTransition !== t.Ae)) return t.Pe;
364
+ if (t.Re !== NOT_PENDING && !hasActiveOverride(t) && !(stale && t.Ae && activeTransition !== t.Ae)) return t.Re;
361
365
  return r;
362
366
  }
363
367
 
@@ -433,7 +437,7 @@ function getLatestValueComputed(e) {
433
437
  }
434
438
 
435
439
  function recordFreshRead(e, t) {
436
- if (pendingProbe !== null && e.Pe !== NOT_PENDING && t === e.Pe) {
440
+ if (pendingProbe !== null && e.Re !== NOT_PENDING && t === e.Re) {
437
441
  if (heldAwaitingAsync(e)) return;
438
442
  pendingProbe.freshReads.add(e);
439
443
  }
@@ -441,10 +445,10 @@ function recordFreshRead(e, t) {
441
445
 
442
446
  function applyReask(e, t) {
443
447
  const n = !!(e.S & STATUS_PENDING);
444
- const i = t && !(n && !e.o?.Ge);
445
- const r = n && (e.o?.Ge ?? false) !== i;
448
+ const i = t && !(n && !e.o?.De);
449
+ const r = n && (e.o?.De ?? false) !== i;
446
450
  // Allocation-free for the quiet case: false is the extension default.
447
- if (i) ext(e).Ge = true; else if (e.o !== null) e.o.Ge = false;
451
+ if (i) ext(e).De = true; else if (e.o !== null) e.o.De = false;
448
452
  return r;
449
453
  }
450
454
 
@@ -525,7 +529,7 @@ function isPending(e) {
525
529
  // Hook installation (same late-binding pattern as GlobalQueue._update /
526
530
  // _propagateAffects): core call sites fire these behind the same guards the
527
531
  // direct calls used, so behavior is identical once this module loads.
528
- GlobalQueue.Re = syncCompanions;
532
+ GlobalQueue.Ue = syncCompanions;
529
533
 
530
534
  GlobalQueue.de = updatePendingSignal;
531
535
 
@@ -78,7 +78,74 @@ function createMemo(e, t) {
78
78
  return accessor(computed(e, t));
79
79
  }
80
80
 
81
- function createEffect(e, t, n) {
81
+ /**
82
+ * Creates a reactive effect with **separate compute and effect phases**.
83
+ *
84
+ * - `compute(prev)` runs reactively — *put all reactive reads here*. The
85
+ * returned value is passed to `effect` and is also the new "previous" value
86
+ * for the next run.
87
+ * - `effect(next, prev?)` runs imperatively (untracked) after the queue
88
+ * flushes. *Put DOM writes / fetch / logging / subscriptions here.* It may
89
+ * return a cleanup function which runs before the next effect or on
90
+ * disposal.
91
+ *
92
+ * Reactive reads inside `effect` will *not* re-trigger this effect — that's
93
+ * intentional. If you need a single-phase tracked effect, use
94
+ * `createTrackedEffect` (with the tradeoffs noted there).
95
+ *
96
+ * Pass an `EffectBundle` (`{ effect, error }`) instead of a plain function to
97
+ * intercept **compute-phase** errors — errors thrown by `compute` or arriving
98
+ * from upstream reactive sources (including async rejections), which your own
99
+ * code has no frame to `try/catch`. The `error` handler is the error arm of
100
+ * the effect phase: it runs on the same schedule and in the same imperative,
101
+ * writable scope as `effect` (setting error state via signals is fine), and
102
+ * only for *settled* errors — a transient error that recovers before the
103
+ * effect phase runs `effect` with the recovered value instead, and a held
104
+ * transition defers it exactly as it defers `effect`. Without an `error`
105
+ * handler a compute-phase error is logged and the effect simply skips that
106
+ * run — a non-render effect's reactivity failing does not crash the app.
107
+ * Rethrowing from `error` escalates it to the nearest error boundary
108
+ * (halting the system if none exists).
109
+ *
110
+ * The **effect phase is different**: it is your own imperative code, so handle
111
+ * failures with `try/catch` where they occur. An uncaught effect-phase throw
112
+ * is treated as an unhandled application error — caught by the nearest
113
+ * `createErrorBoundary`/`<Errored>`, and permanently halting the reactive
114
+ * system if there is none. It is *not* routed to the bundle's `error` handler.
115
+ *
116
+ * ```typescript
117
+ * createEffect<T>(compute, effectFn | { effect, error }, options?: EffectOptions);
118
+ * ```
119
+ * @param compute a function that receives its previous value and returns a new value used to react on a computation
120
+ * @param effectFn a function that receives the new value and is used to perform side effects (return a cleanup function), or an `EffectBundle` with `effect` and `error` handlers
121
+ * @param options `EffectOptions` -- name, defer, schedule, transparent
122
+ *
123
+ * @example
124
+ * ```ts
125
+ * const [count, setCount] = createSignal(0);
126
+ *
127
+ * createEffect(
128
+ * () => count(), // compute: tracks `count`
129
+ * value => console.log(value) // effect: side effect
130
+ * );
131
+ *
132
+ * setCount(1); // logs 1 after the next flush
133
+ * ```
134
+ *
135
+ * @example
136
+ * ```ts
137
+ * createEffect(
138
+ * () => userId(),
139
+ * id => {
140
+ * const ctrl = new AbortController();
141
+ * fetch(`/users/${id}`, { signal: ctrl.signal });
142
+ * return () => ctrl.abort(); // cleanup before next run / disposal
143
+ * }
144
+ * );
145
+ * ```
146
+ *
147
+ * @description https://docs.solidjs.com/reference/basic-reactivity/create-effect
148
+ */ function createEffect(e, t, n) {
82
149
  effect(e, t.effect || t, t.error, {
83
150
  user: true,
84
151
  ...n