@solidjs/signals 2.0.0-beta.25 → 2.0.0-beta.27

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.
@@ -1,4 +1,4 @@
1
- import { globalQueue, activeTransition, currentTransition, setActiveTransition, schedule, flush } from "./scheduler.js";
1
+ import { globalQueue, activeTransition, currentTransition, schedule, flush } from "./scheduler.js";
2
2
 
3
3
  import { isThenable } from "./async.js";
4
4
 
@@ -79,13 +79,18 @@ function restoreTransition(e, r) {
79
79
  globalQueue.initTransition();
80
80
  let o = activeTransition;
81
81
  o.ie.push(i);
82
- const done = (e, r, s = false) => {
82
+ const done = (e, r, u = false) => {
83
83
  o = currentTransition(o);
84
- const u = o.ie.indexOf(i);
85
- if (u >= 0) o.ie.splice(u, 1);
86
- setActiveTransition(o);
84
+ const s = o.ie.indexOf(i);
85
+ if (s >= 0) o.ie.splice(s, 1);
86
+ // Re-adopt through initTransition like every other resumption site:
87
+ // a bare setActiveTransition leaves globalQueue._batch as a detached
88
+ // ambient batch, and anything registered before the scheduled flush
89
+ // (held writes on a merging transition, optimistic overrides,
90
+ // affects() marks) lands there with nothing to ever finalize it.
91
+ globalQueue.initTransition(o);
87
92
  schedule();
88
- s ? n(r) : t(e);
93
+ u ? n(r) : t(e);
89
94
  };
90
95
  const step = (e, r) => {
91
96
  let t;
@@ -1,4 +1,4 @@
1
- import { STATUS_UNINITIALIZED, STATUS_ERROR, STATUS_PENDING, REACTIVE_DIRTY, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING, CONFIG_AUTO_DISPOSE } from "./constants.js";
1
+ import { STATUS_UNINITIALIZED, STATUS_ERROR, STATUS_PENDING, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE, REACTIVE_DIRTY, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING } from "./constants.js";
2
2
 
3
3
  import { untrack, context, setSignal } from "./core.js";
4
4
 
@@ -6,7 +6,7 @@ import "./invariants.js";
6
6
 
7
7
  import { NotReadyError, StatusError } from "./error.js";
8
8
 
9
- import { trimStaleDeps, unobserved } from "./graph.js";
9
+ import { unobserved, trimStaleDeps } from "./graph.js";
10
10
 
11
11
  import { enqueueSub } from "./heap.js";
12
12
 
@@ -14,7 +14,7 @@ import { resolveTransition, hasActiveOverride, assignOrMergeLane, resolveLane }
14
14
 
15
15
  import { cleanup } from "./owner.js";
16
16
 
17
- import { globalQueue, GlobalQueue, clock, schedule, flush, queuePendingNode, insertSubs } from "./scheduler.js";
17
+ import { globalQueue, GlobalQueue, clock, currentTransition, schedule, flush, queuePendingNode, insertSubs } from "./scheduler.js";
18
18
 
19
19
  // The lazily-created Set is the ONE container for pending sources. Its
20
20
  // predecessor — a singular slot promoted to a Set on the second source —
@@ -38,58 +38,125 @@ function clearPendingSources(e) {
38
38
  e.oe = undefined;
39
39
  }
40
40
 
41
- function setPendingError(e, n, r) {
41
+ function setPendingError(e, n, t) {
42
42
  if (!n) {
43
43
  e._ = null;
44
44
  return;
45
45
  }
46
- if (r instanceof NotReadyError && r.source === n) {
47
- e._ = r;
46
+ if (t instanceof NotReadyError && t.source === n) {
47
+ e._ = t;
48
48
  return;
49
49
  }
50
- const t = e._;
51
- if (!(t instanceof NotReadyError) || t.source !== n) {
50
+ const r = e._;
51
+ if (!(r instanceof NotReadyError) || r.source !== n) {
52
52
  e._ = new NotReadyError(n);
53
53
  }
54
54
  }
55
55
 
56
56
  function forEachDependent(e, n) {
57
- for (let r = e.o; r !== null; r = r.ue) n(r.le, r);
57
+ for (let t = e.o; t !== null; t = t.ue) n(t.le, t);
58
58
  // `?? null`: affects() marks route plain signals (no `_child` slot) through here.
59
- for (let r = e.u ?? null; r !== null; r = r.fe) {
60
- for (let e = r.o; e !== null; e = e.ue) n(e.le, e);
59
+ for (let t = e.u ?? null; t !== null; t = t.fe) {
60
+ for (let e = t.o; e !== null; e = e.ue) n(e.le, e);
61
61
  }
62
62
  }
63
63
 
64
64
  // Queue a node to re-run on the next flush (used both when a pending source
65
65
  // settles and when an `isPending` observer must re-evaluate after a real error):
66
66
  // shared scheduling helper in heap.ts (tracked effects bypass the heap).
67
+ // Settle-time counterpart of unlinkSubs' last-one-out check. A lazy node that
68
+ // loses its last subscriber while STATUS_PENDING is exempt from autodispose
69
+ // (the in-flight work is an observer), so whatever CLEARS that pending state
70
+ // must run the release — otherwise the node stays linked and recomputes
71
+ // forever with zero subscribers (#2934). The node's own promise/iterator
72
+ // callbacks handle their own release (settleAutodispose in handleAsync); this
73
+ // covers derivatively-pending dependents, which have no callbacks of their own.
74
+ function releaseIfSettledUnobserved(e) {
75
+ e.ae && e.T & CONFIG_AUTO_DISPOSE && !e.o && !(e.se & REACTIVE_ZOMBIE) && !(e.S & STATUS_PENDING) && unobserved(e);
76
+ }
77
+
78
+ // Error-path sweep: notifyStatus(STATUS_ERROR) clears dependents' pending
79
+ // sources through its own recursion (no per-node settle callback), so after
80
+ // the propagation completes, walk the same graph for stranded lazy nodes.
81
+ // Collect-then-release so unobserved() never unlinks under the walk.
82
+ function releaseSettledDependents(e) {
83
+ let n;
84
+ const t = new Set;
85
+ const visit = e => {
86
+ if (t.has(e)) return;
87
+ t.add(e);
88
+ if (!e.o && e.T & CONFIG_AUTO_DISPOSE) (n ??= []).push(e);
89
+ forEachDependent(e, visit);
90
+ };
91
+ forEachDependent(e, visit);
92
+ if (n) for (const e of n) releaseIfSettledUnobserved(e);
93
+ }
94
+
95
+ // Error-dimension twin of settlePendingSource's blocked re-enqueue (#2949):
96
+ // a node in STATUS_ERROR that recovers by recomputing to an UNCHANGED value
97
+ // fires no value notification — the recovery is completely silent. But a
98
+ // dependent that re-ran during the error window consumed its dirty flag and
99
+ // committed nothing (the fresh sibling values it read were absorbed into an
100
+ // errored run), so its committed value is stale. The propagated error is one
101
+ // object identity down the whole dependent tree, and holding it is exactly
102
+ // the "blocked on this error" marker — re-enqueue those holders so they
103
+ // re-run: fresh values commit and flow, and a dependent with another
104
+ // still-broken source simply re-errors. The async dimension needs no twin of
105
+ // its own: recovery there passes through a pending window whose re-runners
106
+ // set _blocked and ride settlePendingSource. Walks the full dependent graph
107
+ // (releaseSettledDependents shape): identity holders can sit below an
108
+ // intermediate whose own error state has since been scrubbed or replaced
109
+ // (e.g. an error boundary's tree node).
110
+ function settleErroredDependents(e, n) {
111
+ let t = false;
112
+ const r = new Set;
113
+ const visit = e => {
114
+ if (r.has(e)) return;
115
+ r.add(e);
116
+ if (e._ === n) {
117
+ enqueueSub(e);
118
+ t = true;
119
+ }
120
+ forEachDependent(e, visit);
121
+ };
122
+ forEachDependent(e, visit);
123
+ if (t) schedule();
124
+ }
125
+
67
126
  function settlePendingSource(e) {
68
127
  let n = false;
128
+ let t;
69
129
  const r = new Set;
70
130
  // Companion updates no-op without the verdict layer (null hook).
71
- const t = GlobalQueue.ae;
72
- const settle = o => {
73
- if (r.has(o) || !removePendingSource(o, e)) return;
74
- r.add(o);
75
- o.ce = clock;
76
- const u = o.oe?.values().next().value;
77
- if (u) {
78
- setPendingError(o, u);
79
- t !== null && t(o);
131
+ const o = GlobalQueue.ce;
132
+ const settle = u => {
133
+ if (r.has(u) || !removePendingSource(u, e)) return;
134
+ r.add(u);
135
+ u.Se = clock;
136
+ const l = u.oe?.values().next().value;
137
+ if (l) {
138
+ setPendingError(u, l);
139
+ o !== null && o(u);
80
140
  } else {
81
- o.S &= ~STATUS_PENDING;
82
- setPendingError(o);
83
- t !== null && t(o);
84
- if (o.Se) {
85
- enqueueSub(o);
141
+ u.S &= ~STATUS_PENDING;
142
+ setPendingError(u);
143
+ o !== null && o(u);
144
+ if (u.de) {
145
+ enqueueSub(u);
86
146
  n = true;
87
147
  }
88
- o.Se = false;
148
+ u.de = false;
149
+ // Fully settled with nobody watching: release candidate (#2934). Checked
150
+ // again at release time — deferred so unobserved() can't unlink subs
151
+ // lists this walk is still iterating.
152
+ if (!u.o && u.T & CONFIG_AUTO_DISPOSE) (t ??= []).push(u);
89
153
  }
90
- forEachDependent(o, settle);
154
+ forEachDependent(u, settle);
91
155
  };
92
156
  forEachDependent(e, settle);
157
+ // Release before the flush schedule below: unobserved() pulls the node back
158
+ // out of the heap, so the enqueueSub above never recomputes a released node.
159
+ if (t) for (const e of t) releaseIfSettledUnobserved(e);
93
160
  if (n) schedule();
94
161
  }
95
162
 
@@ -98,44 +165,69 @@ function isThenable(e) {
98
165
  return e != null && typeof e === "object" && typeof e.then === "function";
99
166
  }
100
167
 
101
- function handleAsync(e, n, r) {
102
- let t = false;
168
+ function handleAsync(e, n, t) {
169
+ let r = false;
103
170
  let o = false;
104
171
  if (typeof n === "object" && n !== null) {
105
172
  untrack(() => {
106
- t = n[Symbol.asyncIterator];
107
- o = !t && isThenable(n);
173
+ r = n[Symbol.asyncIterator];
174
+ o = !r && isThenable(n);
108
175
  });
109
176
  }
110
- if (!o && !t) {
177
+ if (!o && !r) {
111
178
  e.Te = null;
112
179
  return n;
113
180
  }
114
181
  e.Te = n;
115
182
  let u;
116
- const handleError = r => {
183
+ // Settle-time transition re-entry. The loading rail is invisible to
184
+ // transactions (#2933): a boundary-caught first load never registers as an
185
+ // async reporter, so its settle — the boundary's fallback -> content
186
+ // reveal — must flow ambiently. The node can still carry a `_transition`
187
+ // stamp (pending-node bookkeeping rides through the stamping sites), and
188
+ // blindly re-entering that stamped, still-incomplete transaction stashed
189
+ // the reveal with it — a deadlock when the transaction's completion
190
+ // depended on the reveal (#2937). An ESCAPED first load did register and
191
+ // keeps transition scheduling; initialized (value-holding) pending settles
192
+ // are the transaction's reveal machinery and always re-enter.
193
+ const settleTransition = () => {
194
+ const n = resolveTransition(e);
195
+ if (n && e.S & STATUS_UNINITIALIZED && !currentTransition(n).Ee.has(e)) {
196
+ // Drop the stale stamp too: the plain settle write (setSignal) and the
197
+ // stash-path restamp both re-enter the transaction through it.
198
+ e.Ie = null;
199
+ return;
200
+ }
201
+ globalQueue.initTransition(n);
202
+ };
203
+ const handleError = t => {
117
204
  if (e.Te !== n) return;
118
- globalQueue.initTransition(resolveTransition(e));
205
+ settleTransition();
119
206
  // NotReadyError from rejected promises should be treated as pending, not error
120
- notifyStatus(e, r instanceof NotReadyError ? STATUS_PENDING : STATUS_ERROR, r);
121
- e.ce = clock;
207
+ const r = t instanceof NotReadyError;
208
+ notifyStatus(e, r ? STATUS_PENDING : STATUS_ERROR, t);
209
+ e.Se = clock;
210
+ // A real error settles derivatively-pending dependents (notifyStatus
211
+ // cleared their pending sources), so stranded lazy ones release here —
212
+ // the error twin of settlePendingSource's release (#2934).
213
+ if (!r) releaseSettledDependents(e);
122
214
  };
123
- const asyncWrite = (t, o) => {
215
+ const asyncWrite = (r, o) => {
124
216
  if (e.Te !== n) return;
125
217
  // If the node was dirtied by a newer write (optimistic override or regular),
126
218
  // skip this stale async result — the upcoming flush will recompute the node
127
219
  // with the new value, creating a fresh Promise that supersedes this one.
128
220
  if (e.se & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY)) return;
129
- globalQueue.initTransition(resolveTransition(e));
221
+ settleTransition();
130
222
  const u = !!(e.S & STATUS_UNINITIALIZED);
131
223
  trimStaleDeps(e);
132
224
  clearStatus(e);
133
225
  const l = resolveLane(e);
134
- if (l) l.de.delete(e);
135
- if (r) {
136
- r(t);
226
+ if (l) l.Ne.delete(e);
227
+ if (t) {
228
+ t(r);
137
229
  if (u) clearStatus(e, true);
138
- } else if (e.Ee !== undefined) {
230
+ } else if (e.Ae !== undefined) {
139
231
  // Optimistic node — resting OR covered by an active override — holds
140
232
  // through the shared pending-node path, exactly like a plain async memo,
141
233
  // so the commit clears STATUS_UNINITIALIZED (#2806) and elevation to
@@ -145,8 +237,8 @@ function handleAsync(e, n, r) {
145
237
  // (A17 — every reader sees the override); the revert reveals whatever
146
238
  // has committed by then, so corrections reveal atomically with their
147
239
  // transition rather than escaping it.
148
- if (e.Ne === NOT_PENDING) queuePendingNode(e);
149
- e.Ne = t;
240
+ if (e.De === NOT_PENDING) queuePendingNode(e);
241
+ e.De = r;
150
242
  // The hold is a companion-visible write like any other (A13/A19): the
151
243
  // clearStatus() above computed its verdict before the hold existed, so
152
244
  // isPending must re-derive (the value is not final until commit — V1)
@@ -154,22 +246,22 @@ function handleAsync(e, n, r) {
154
246
  // only notified when the hold is visible to them: under an active
155
247
  // override every reader sees the override (A17), so waking subs would
156
248
  // re-show an unchanged view — the revert is the notification point.
157
- GlobalQueue.Ie !== null && GlobalQueue.Ie(e, t);
249
+ GlobalQueue._e !== null && GlobalQueue._e(e, r);
158
250
  if (!hasActiveOverride(e)) insertSubs(e);
159
- e.ce = clock;
251
+ e.Se = clock;
160
252
  } else if (l) {
161
253
  // Route through lane's effect queue for independent flushing
162
- const n = e.Ae;
163
- const r = e.Pe;
164
- const o = e.Re;
254
+ const n = e.Pe;
255
+ const t = e.Ue;
256
+ const o = e.be;
165
257
  try {
166
- if (!n && u || !o || !o(t, r)) {
167
- e.Pe = t;
168
- e.ce = clock;
258
+ if (!n && u || !o || !o(r, t)) {
259
+ e.Ue = r;
260
+ e.Se = clock;
169
261
  // The latest() shadow write gives latest() effects independent lanes; the
170
262
  // _pendingSignal update is a no-op repeat of the clearStatus() call above
171
263
  // (computePendingState doesn't read _value).
172
- GlobalQueue.Ie !== null && GlobalQueue.Ie(e, t);
264
+ GlobalQueue._e !== null && GlobalQueue._e(e, r);
173
265
  insertSubs(e, true);
174
266
  }
175
267
  } catch (n) {
@@ -181,7 +273,7 @@ function handleAsync(e, n, r) {
181
273
  }
182
274
  } else {
183
275
  try {
184
- setSignal(e, () => t);
276
+ setSignal(e, () => r);
185
277
  } catch (n) {
186
278
  // Same containment as above: setSignal's comparator throw is the only
187
279
  // pre-commit failure here, and there is no user callsite to throw to.
@@ -198,17 +290,21 @@ function handleAsync(e, n, r) {
198
290
  // work (a lazy async memo would otherwise tear down and re-execute — one
199
291
  // fetch per suspended re-read). Settling is that observer's release, so
200
292
  // it runs the same last-one-out check the other release sites run.
293
+ // Returns whether the node released, so the iterator branch can stop
294
+ // pulling values instead of pumping an unobserved stream forever (#2935).
201
295
  const settleAutodispose = () => {
202
296
  if (e.T & CONFIG_AUTO_DISPOSE && !e.o && !(e.S & STATUS_PENDING)) {
203
297
  unobserved(e);
298
+ return true;
204
299
  }
300
+ return false;
205
301
  };
206
302
  if (o) {
207
- let r = false, t = false, o, l = true;
303
+ let t = false, r = false, o, l = true;
208
304
  n.then(e => {
209
305
  if (l) {
210
306
  u = e;
211
- r = true;
307
+ t = true;
212
308
  } else {
213
309
  asyncWrite(e);
214
310
  settleAutodispose();
@@ -216,87 +312,95 @@ function handleAsync(e, n, r) {
216
312
  }, e => {
217
313
  if (l) {
218
314
  o = e;
219
- t = true;
315
+ r = true;
220
316
  } else {
221
317
  handleError(e);
222
318
  settleAutodispose();
223
319
  }
224
320
  });
225
321
  l = false;
226
- if (t) {
322
+ if (r) {
227
323
  // Settle through the same status path an async rejection uses, then
228
324
  // unwind the in-progress synchronous read so the errored node isn't
229
325
  // momentarily read as `undefined`.
230
326
  handleError(o);
231
327
  throw o;
232
- } else if (!r) {
328
+ } else if (!t) {
233
329
  globalQueue.initTransition(resolveTransition(e));
234
330
  throw new NotReadyError(context);
235
331
  }
236
332
  }
237
- if (t) {
238
- const r = n[Symbol.asyncIterator]();
239
- let t = false;
333
+ if (r) {
334
+ const t = n[Symbol.asyncIterator]();
335
+ let r = false;
240
336
  let o = false;
241
337
  let l = true;
242
338
  cleanup(() => {
243
339
  if (o) return;
244
340
  o = true;
245
341
  try {
246
- const e = r.return?.();
342
+ const e = t.return?.();
247
343
  if (isThenable(e)) e.then(undefined, () => {});
248
344
  } catch {}
249
345
  });
346
+ // Release check before each next pull: an unobserved lazy node must tear
347
+ // down (its cleanup above closes the iterator) instead of pumping the
348
+ // stream forever with zero subscribers (#2935).
349
+ const iterateOrRelease = () => {
350
+ if (!settleAutodispose()) iterate();
351
+ };
250
352
  const iterate = () => {
251
- let i, s, f = false, a = false, c = true;
252
- r.next().then(r => {
353
+ let s, i, f = false, a = false, c = true;
354
+ t.next().then(t => {
253
355
  if (c) {
254
- i = r;
356
+ s = t;
255
357
  f = true;
256
- if (r.done) o = true;
358
+ if (t.done) o = true;
257
359
  } else if (e.Te !== n) {
258
360
  return;
259
- } else if (!r.done) {
260
- t = true;
261
- asyncWrite(r.value, iterate);
361
+ } else if (!t.done) {
362
+ r = true;
363
+ asyncWrite(t.value, iterateOrRelease);
262
364
  } else {
263
365
  o = true;
264
- if (t) {
366
+ if (r) {
265
367
  schedule();
266
368
  flush();
267
369
  } else {
268
370
  // Empty completion settles like the immediately-done sync path.
269
371
  asyncWrite(undefined);
270
372
  }
373
+ settleAutodispose();
271
374
  }
272
- }, r => {
375
+ }, t => {
273
376
  if (c) {
274
- s = r;
377
+ i = t;
275
378
  a = true;
276
379
  } else if (e.Te === n) {
277
380
  o = true;
278
- handleError(r);
381
+ handleError(t);
382
+ settleAutodispose();
279
383
  }
280
384
  });
281
385
  c = false;
282
386
  if (a) {
283
387
  // Match the promise branch, but only rethrow during the initial read.
284
388
  o = true;
285
- handleError(s);
286
- if (l) throw s;
389
+ handleError(i);
390
+ if (l) throw i;
287
391
  return true;
288
392
  }
289
- if (f && !i.done) {
290
- u = i.value;
291
- t = true;
393
+ if (f && !s.done) {
394
+ u = s.value;
395
+ r = true;
292
396
  return iterate();
293
397
  }
294
- return f && i.done;
398
+ return f && s.done;
295
399
  };
296
- const i = iterate();
400
+ const s = iterate();
297
401
  // Later iterate() calls run from asyncWrite, where rethrowing would be unhandled.
298
402
  l = false;
299
- if (!t && !i) {
403
+ if (!r && !s) {
300
404
  globalQueue.initTransition(resolveTransition(e));
301
405
  throw new NotReadyError(context);
302
406
  }
@@ -306,75 +410,75 @@ function handleAsync(e, n, r) {
306
410
 
307
411
  function clearStatus(e, n = false) {
308
412
  if (e.oe) clearPendingSources(e);
309
- if (e.Se) e.Se = false;
413
+ if (e.de) e.de = false;
310
414
  // The pending window is over; its quiet classification dies with it.
311
415
  // (Unconditional: _reask is baked into the node literals, so this is a
312
416
  // plain store to an existing slot — no shape change.)
313
- e._e = false;
417
+ e.Re = false;
314
418
  e.S = n ? 0 : e.S & STATUS_UNINITIALIZED;
315
419
  if (e._) setPendingError(e);
316
420
  // Update pending signal for isPending() reactivity (companions only exist
317
421
  // once the verdict layer created them, which installs the hooks).
318
- if (e.be || e.ge) GlobalQueue.ae(e);
319
- if (e.u && GlobalQueue.he !== null) GlobalQueue.he(e);
422
+ if (e.he || e.pe) GlobalQueue.ce(e);
423
+ if (e.u && GlobalQueue.Ge !== null) GlobalQueue.Ge(e);
320
424
  if (e.i) e.i();
321
425
  }
322
426
 
323
- function notifyStatus(e, n, r, t, o) {
427
+ function notifyStatus(e, n, t, r, o) {
324
428
  // Wrap regular errors to track source node
325
- if (n === STATUS_ERROR && !(r instanceof StatusError) && !(r instanceof NotReadyError)) r = new StatusError(e, r);
326
- const u = n === STATUS_PENDING && r instanceof NotReadyError ? r.source : undefined;
429
+ if (n === STATUS_ERROR && !(t instanceof StatusError) && !(t instanceof NotReadyError)) t = new StatusError(e, t);
430
+ const u = n === STATUS_PENDING && t instanceof NotReadyError ? t.source : undefined;
327
431
  const l = u === e;
328
- const i = n === STATUS_PENDING && e.Ee !== undefined && !l;
329
- const s = i && hasActiveOverride(e);
330
- if (!t) {
432
+ const s = n === STATUS_PENDING && e.Ae !== undefined && !l;
433
+ const i = s && hasActiveOverride(e);
434
+ if (!r) {
331
435
  if (n === STATUS_PENDING && u) {
332
436
  addPendingSource(e, u);
333
437
  e.S = STATUS_PENDING | e.S & STATUS_UNINITIALIZED;
334
438
  // Preserve the current source on this propagation so render-effect notification
335
439
  // can register every distinct pending source with the transition.
336
- setPendingError(e, u, r);
440
+ setPendingError(e, u, t);
337
441
  } else {
338
442
  clearPendingSources(e);
339
443
  e.S = n | (n !== STATUS_ERROR ? e.S & STATUS_UNINITIALIZED : 0);
340
- e._ = r;
444
+ e._ = t;
341
445
  }
342
- GlobalQueue.ae !== null && GlobalQueue.ae(e);
343
- if (e.u && GlobalQueue.he !== null) GlobalQueue.he(e);
446
+ GlobalQueue.ce !== null && GlobalQueue.ce(e);
447
+ if (e.u && GlobalQueue.Ge !== null) GlobalQueue.Ge(e);
344
448
  }
345
- if (o && !t) {
449
+ if (o && !r) {
346
450
  assignOrMergeLane(e, o);
347
451
  }
348
- const f = t || s;
349
- const a = t || i ? undefined : o;
452
+ const f = r || i;
453
+ const a = r || s ? undefined : o;
350
454
  if (e.i) {
351
- if (t && n === STATUS_PENDING) {
455
+ if (r && n === STATUS_PENDING) {
352
456
  return;
353
457
  }
354
458
  if (f) {
355
- e.i(n, r);
459
+ e.i(n, t);
356
460
  } else {
357
461
  e.i();
358
462
  }
359
463
  return;
360
464
  }
361
- forEachDependent(e, (e, t) => {
362
- e.ce = clock;
363
- if (n === STATUS_PENDING && u && !e.oe?.has(u) || n !== STATUS_PENDING && (e._ !== r || e.oe)) {
465
+ forEachDependent(e, (e, r) => {
466
+ e.Se = clock;
467
+ if (n === STATUS_PENDING && u && !e.oe?.has(u) || n !== STATUS_PENDING && (e._ !== t || e.oe)) {
364
468
  // A pending-observer link is the subscription an `isPending` read created.
365
469
  // It exists so the observer re-runs when the source settles, but it must
366
470
  // not carry a real (non-NotReadyError) error — the synchronous `isPending`
367
471
  // read swallows those, and the async path must match. Re-run the observer
368
472
  // so `isPending` re-evaluates (to not-pending) instead of forwarding.
369
- if (t.De && n !== STATUS_PENDING && !(r instanceof NotReadyError)) {
473
+ if (r.Oe && n !== STATUS_PENDING && !(t instanceof NotReadyError)) {
370
474
  enqueueSub(e);
371
475
  schedule();
372
476
  return;
373
477
  }
374
- if (!f && !e.Ue) queuePendingNode(e);
375
- notifyStatus(e, n, r, f, a);
478
+ if (!f && !e.Ie) queuePendingNode(e);
479
+ notifyStatus(e, n, t, f, a);
376
480
  }
377
481
  });
378
482
  }
379
483
 
380
- export { addPendingSource, clearStatus, forEachDependent, handleAsync, isThenable, notifyStatus, setPendingError, settlePendingSource };
484
+ export { addPendingSource, clearStatus, forEachDependent, handleAsync, isThenable, notifyStatus, releaseSettledDependents, setPendingError, settleErroredDependents, settlePendingSource };