@solidjs/signals 2.0.0-rc.0 → 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 (70) hide show
  1. package/README.md +1 -1
  2. package/dist/dev.js +4287 -2803
  3. package/dist/node.cjs +4563 -3756
  4. package/dist/prod/affects.js +16 -16
  5. package/dist/prod/boundaries.js +90 -90
  6. package/dist/prod/core/action.js +3 -3
  7. package/dist/prod/core/async.js +234 -178
  8. package/dist/prod/core/constants.js +39 -1
  9. package/dist/prod/core/core.js +362 -243
  10. package/dist/prod/core/effect.js +56 -56
  11. package/dist/prod/core/external.js +4 -4
  12. package/dist/prod/core/graph.js +65 -54
  13. package/dist/prod/core/heap.js +47 -39
  14. package/dist/prod/core/invariants.js +3 -2
  15. package/dist/prod/core/lanes.js +41 -34
  16. package/dist/prod/core/optimistic.js +78 -58
  17. package/dist/prod/core/owner.js +101 -100
  18. package/dist/prod/core/scheduler.js +249 -192
  19. package/dist/prod/core/verdict.js +185 -78
  20. package/dist/prod/index.js +7 -7
  21. package/dist/prod/map.js +108 -106
  22. package/dist/prod/signals.js +20 -1
  23. package/dist/prod/store/index.js +36 -0
  24. package/dist/prod/store/next/optimistic.js +385 -0
  25. package/dist/prod/store/next/projection.js +172 -0
  26. package/dist/prod/store/next/reconcile.js +331 -0
  27. package/dist/prod/store/next/store.js +1499 -0
  28. package/dist/prod/store/next/target.js +20 -0
  29. package/dist/prod/store/store.js +126 -882
  30. package/dist/prod/store/utils.js +59 -186
  31. package/dist/types/core/attribution-hooks.d.ts +52 -0
  32. package/dist/types/core/attribution.d.ts +186 -0
  33. package/dist/types/core/constants.d.ts +26 -0
  34. package/dist/types/core/core.d.ts +16 -1
  35. package/dist/types/core/dev.d.ts +20 -3
  36. package/dist/types/core/graph.d.ts +1 -0
  37. package/dist/types/core/lanes.d.ts +4 -16
  38. package/dist/types/core/scheduler.d.ts +12 -2
  39. package/dist/types/core/types.d.ts +85 -41
  40. package/dist/types/signals.d.ts +19 -0
  41. package/dist/types/store/index.d.ts +15 -5
  42. package/dist/types/store/next/optimistic.d.ts +20 -0
  43. package/dist/types/store/next/projection.d.ts +8 -0
  44. package/dist/types/store/next/reconcile.d.ts +3 -0
  45. package/dist/types/store/next/store.d.ts +77 -0
  46. package/dist/types/store/next/target.d.ts +117 -0
  47. package/dist/types/store/store.d.ts +26 -101
  48. package/dist/types/store/utils.d.ts +0 -40
  49. package/dist/types-cjs/core/attribution-hooks.d.cts +52 -0
  50. package/dist/types-cjs/core/attribution.d.cts +186 -0
  51. package/dist/types-cjs/core/constants.d.cts +26 -0
  52. package/dist/types-cjs/core/core.d.cts +16 -1
  53. package/dist/types-cjs/core/dev.d.cts +20 -3
  54. package/dist/types-cjs/core/graph.d.cts +1 -0
  55. package/dist/types-cjs/core/lanes.d.cts +4 -16
  56. package/dist/types-cjs/core/scheduler.d.cts +12 -2
  57. package/dist/types-cjs/core/types.d.cts +85 -41
  58. package/dist/types-cjs/signals.d.cts +19 -0
  59. package/dist/types-cjs/store/index.d.cts +15 -5
  60. package/dist/types-cjs/store/next/optimistic.d.cts +20 -0
  61. package/dist/types-cjs/store/next/projection.d.cts +8 -0
  62. package/dist/types-cjs/store/next/reconcile.d.cts +3 -0
  63. package/dist/types-cjs/store/next/store.d.cts +77 -0
  64. package/dist/types-cjs/store/next/target.d.cts +117 -0
  65. package/dist/types-cjs/store/store.d.cts +26 -101
  66. package/dist/types-cjs/store/utils.d.cts +0 -40
  67. package/package.json +2 -1
  68. package/dist/prod/store/optimistic.js +0 -217
  69. package/dist/prod/store/projection.js +0 -231
  70. package/dist/prod/store/reconcile.js +0 -707
@@ -1,20 +1,20 @@
1
- import { STATUS_UNINITIALIZED, STATUS_ERROR, STATUS_PENDING, REACTIVE_DIRTY, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE } from "./constants.js";
1
+ import { STATUS_UNINITIALIZED, CONFIG_CHILD_COMPANIONS, STATUS_ERROR, STATUS_PENDING, REACTIVE_DIRTY, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE, REACTIVE_DISPOSED } from "./constants.js";
2
2
 
3
- import { untrack, context, setSignal } from "./core.js";
3
+ import { untrack, ext, setSignal, context } from "./core.js";
4
4
 
5
5
  import "./invariants.js";
6
6
 
7
- import { NotReadyError, StatusError } from "./error.js";
7
+ import { StatusError, NotReadyError } from "./error.js";
8
8
 
9
9
  import { trimStaleDeps, unobserved } from "./graph.js";
10
10
 
11
11
  import { enqueueSub } from "./heap.js";
12
12
 
13
- import { resolveTransition, hasActiveOverride, assignOrMergeLane, resolveLane } from "./lanes.js";
13
+ import { hasActiveOverride, assignOrMergeLane, resolveLane, resolveTransition } from "./lanes.js";
14
14
 
15
15
  import { cleanup } from "./owner.js";
16
16
 
17
- import { globalQueue, GlobalQueue, schedule, flush, queuePendingNode, insertSubs, clock, currentTransition } from "./scheduler.js";
17
+ import { GlobalQueue, schedule, flush, queuePendingNode, insertSubs, clock, currentTransition, globalQueue } 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 —
@@ -22,20 +22,20 @@ import { globalQueue, GlobalQueue, schedule, flush, queuePendingNode, insertSubs
22
22
  // overlapping source landed beside the Set and removePendingSource refused
23
23
  // to clear it, stranding the Set members' pending forever (#2893).
24
24
  function addPendingSource(e, n) {
25
- if (e.oe?.has(n)) return false;
26
- (e.oe ??= new Set).add(n);
25
+ if (e.o?.le?.has(n)) return false;
26
+ (ext(e).le ??= new Set).add(n);
27
27
  return true;
28
28
  }
29
29
 
30
30
  function removePendingSource(e, n) {
31
- if (!e.oe?.delete(n)) return false;
32
- if (e.oe.size === 0) e.oe = undefined;
31
+ if (!e.o?.le?.delete(n)) return false;
32
+ if (e.o?.le.size === 0) if (e.o !== null) e.o.le = undefined;
33
33
  return true;
34
34
  }
35
35
 
36
36
  function clearPendingSources(e) {
37
- e.oe?.clear();
38
- e.oe = undefined;
37
+ e.o?.le?.clear();
38
+ if (e.o !== null) e.o.le = undefined;
39
39
  }
40
40
 
41
41
  /**
@@ -45,7 +45,7 @@ function clearPendingSources(e) {
45
45
  * with NO read-visible pending status, no downstream propagation, no
46
46
  * transition, no lane registration. Commit #0 keeps serving.
47
47
  */ function parkLoadingWindow(e, n) {
48
- e.ue = true;
48
+ ext(e).ue = true;
49
49
  if (n.source) addPendingSource(e, n.source);
50
50
  // A settled error is the node's answer ("the error stays the answer until
51
51
  // this retry can actually run") — the park must not replace it: reads
@@ -56,24 +56,24 @@ function clearPendingSources(e) {
56
56
 
57
57
  function setPendingError(e, n, t) {
58
58
  if (!n) {
59
- e._ = null;
59
+ if (e.o !== null) e.o._ = null;
60
60
  return;
61
61
  }
62
62
  if (t instanceof NotReadyError && t.source === n) {
63
- e._ = t;
63
+ ext(e)._ = t;
64
64
  return;
65
65
  }
66
- const r = e._;
66
+ const r = e.o?._;
67
67
  if (!(r instanceof NotReadyError) || r.source !== n) {
68
- e._ = new NotReadyError(n);
68
+ ext(e)._ = new NotReadyError(n);
69
69
  }
70
70
  }
71
71
 
72
72
  function forEachDependent(e, n) {
73
- for (let t = e.o; t !== null; t = t.le) n(t.fe, t);
73
+ for (let t = e.u; t !== null; t = t.fe) n(t.ae, t);
74
74
  // `?? null`: affects() marks route plain signals (no `_child` slot) through here.
75
- for (let t = e.u ?? null; t !== null; t = t.ae) {
76
- for (let e = t.o; e !== null; e = e.le) n(e.fe, e);
75
+ for (let t = e.o?.l ?? null; t !== null; t = t.ce) {
76
+ for (let e = t.u; e !== null; e = e.fe) n(e.ae, e);
77
77
  }
78
78
  }
79
79
 
@@ -88,7 +88,7 @@ function forEachDependent(e, n) {
88
88
  // callbacks handle their own release (settleAutodispose in handleAsync); this
89
89
  // covers derivatively-pending dependents, which have no callbacks of their own.
90
90
  function releaseIfSettledUnobserved(e) {
91
- e.ce && e.T & CONFIG_AUTO_DISPOSE && !e.o && !(e.se & REACTIVE_ZOMBIE) && !(e.S & STATUS_PENDING) && unobserved(e);
91
+ e.Se && e.T & CONFIG_AUTO_DISPOSE && !e.u && !(e.ie & REACTIVE_ZOMBIE) && !(e.S & STATUS_PENDING) && unobserved(e);
92
92
  }
93
93
 
94
94
  // Error-path sweep: notifyStatus(STATUS_ERROR) clears dependents' pending
@@ -101,7 +101,7 @@ function releaseSettledDependents(e) {
101
101
  const visit = e => {
102
102
  if (t.has(e)) return;
103
103
  t.add(e);
104
- if (!e.o && e.T & CONFIG_AUTO_DISPOSE) (n ??= []).push(e);
104
+ if (!e.u && e.T & CONFIG_AUTO_DISPOSE) (n ??= []).push(e);
105
105
  forEachDependent(e, visit);
106
106
  };
107
107
  forEachDependent(e, visit);
@@ -129,7 +129,7 @@ function settleErroredDependents(e, n) {
129
129
  const visit = e => {
130
130
  if (r.has(e)) return;
131
131
  r.add(e);
132
- if (e._ === n) {
132
+ if (e.o?._ === n) {
133
133
  enqueueSub(e);
134
134
  t = true;
135
135
  }
@@ -144,36 +144,36 @@ function settlePendingSource(e) {
144
144
  let t;
145
145
  const r = new Set;
146
146
  // Companion updates no-op without the verdict layer (null hook).
147
- const o = GlobalQueue.Se;
148
- const settle = u => {
149
- if (r.has(u) || !removePendingSource(u, e)) return;
150
- r.add(u);
151
- u.de = clock;
152
- const i = u.oe?.values().next().value;
147
+ const o = GlobalQueue.de;
148
+ const settle = l => {
149
+ if (r.has(l) || !removePendingSource(l, e)) return;
150
+ r.add(l);
151
+ l.Te = clock;
152
+ const u = l.o?.le?.values().next().value;
153
153
  // STATUS_ERROR + pending sources only coexist via an errored loading
154
154
  // window's park (notifyStatus(STATUS_ERROR) clears pending sources
155
155
  // otherwise): the settled error stays the answer through the settle —
156
156
  // nulling it here would have reads throw `null` until the re-enqueued
157
157
  // retry lands, or lose it entirely if that retry parks again (#2989).
158
- const l = u.S & STATUS_ERROR;
159
- if (i) {
160
- if (!l) setPendingError(u, i);
161
- o !== null && o(u);
158
+ const i = l.S & STATUS_ERROR;
159
+ if (u) {
160
+ if (!i) setPendingError(l, u);
161
+ o !== null && o(l);
162
162
  } else {
163
- u.S &= ~STATUS_PENDING;
164
- if (!l) setPendingError(u);
165
- o !== null && o(u);
166
- if (u.ue) {
167
- enqueueSub(u);
163
+ l.S &= ~STATUS_PENDING;
164
+ if (!i) setPendingError(l);
165
+ o !== null && o(l);
166
+ if (l.o?.ue) {
167
+ enqueueSub(l);
168
168
  n = true;
169
169
  }
170
- u.ue = false;
170
+ if (l.o !== null) l.o.ue = false;
171
171
  // Fully settled with nobody watching: release candidate (#2934). Checked
172
172
  // again at release time — deferred so unobserved() can't unlink subs
173
173
  // lists this walk is still iterating.
174
- if (!u.o && u.T & CONFIG_AUTO_DISPOSE) (t ??= []).push(u);
174
+ if (!l.u && l.T & CONFIG_AUTO_DISPOSE) (t ??= []).push(l);
175
175
  }
176
- forEachDependent(u, settle);
176
+ forEachDependent(l, settle);
177
177
  };
178
178
  forEachDependent(e, settle);
179
179
  // Release before the flush schedule below: unobserved() pulls the node back
@@ -197,13 +197,13 @@ function handleAsync(e, n, t) {
197
197
  });
198
198
  }
199
199
  if (!o && !r) {
200
- e.Te = null;
200
+ if (e.o !== null) e.o.Ee = null;
201
201
  // A sync landing is the first real answer for a loadingValue node.
202
- e.Ee = false;
202
+ e.Ie = false;
203
203
  return n;
204
204
  }
205
- e.Te = n;
206
- let u;
205
+ ext(e).Ee = n;
206
+ let l;
207
207
  // Settle-time transition re-entry. The loading rail is invisible to
208
208
  // transactions (#2933): a boundary-caught first load never registers as an
209
209
  // async reporter, so its settle — the boundary's fallback -> content
@@ -216,52 +216,52 @@ function handleAsync(e, n, t) {
216
216
  // are the transaction's reveal machinery and always re-enter.
217
217
  const settleTransition = () => {
218
218
  const n = resolveTransition(e);
219
- if (n && e.S & STATUS_UNINITIALIZED && !currentTransition(n).Ie.has(e)) {
219
+ if (n && e.S & STATUS_UNINITIALIZED && !currentTransition(n).Ne.has(e)) {
220
220
  // Drop the stale stamp too: the plain settle write (setSignal) and the
221
221
  // stash-path restamp both re-enter the transaction through it.
222
- e.Ne = null;
222
+ e._e = null;
223
223
  return;
224
224
  }
225
225
  globalQueue.initTransition(n);
226
226
  };
227
227
  const handleError = t => {
228
- if (e.Te !== n) return;
228
+ if (e.o?.Ee !== n) return;
229
229
  // NotReadyError from rejected promises should be treated as pending, not error
230
230
  let r = t instanceof NotReadyError;
231
- if (r && e.Ee) {
231
+ if (r && e.Ie) {
232
232
  // Loading window: the flight died waiting on an unready source. Keep
233
233
  // serving commit #0 — same parking as recompute's catch for sync
234
234
  // dependency throws. The dead flight is released so the clock-gated
235
235
  // error-retry pull (updateIfNecessary) can also re-ask.
236
- e.Te = null;
236
+ if (e.o !== null) e.o.Ee = null;
237
237
  parkLoadingWindow(e, t);
238
- e.de = clock;
238
+ e.Te = clock;
239
239
  return;
240
240
  }
241
241
  settleTransition();
242
242
  notifyStatus(e, r ? STATUS_PENDING : STATUS_ERROR, t);
243
- e.de = clock;
243
+ e.Te = clock;
244
244
  // A real error settles derivatively-pending dependents (notifyStatus
245
245
  // cleared their pending sources), so stranded lazy ones release here —
246
246
  // the error twin of settlePendingSource's release (#2934).
247
247
  if (!r) releaseSettledDependents(e);
248
248
  };
249
249
  const asyncWrite = (r, o) => {
250
- if (e.Te !== n) return;
250
+ if (e.o?.Ee !== n) return;
251
251
  // If the node was dirtied by a newer write (optimistic override or regular),
252
252
  // skip this stale async result — the upcoming flush will recompute the node
253
253
  // with the new value, creating a fresh Promise that supersedes this one.
254
- if (e.se & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY)) return;
254
+ if (e.ie & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY)) return;
255
255
  settleTransition();
256
- const u = !!(e.S & STATUS_UNINITIALIZED);
256
+ const l = !!(e.S & STATUS_UNINITIALIZED);
257
257
  trimStaleDeps(e);
258
258
  clearStatus(e);
259
- const i = resolveLane(e);
260
- if (i) i.Ae.delete(e);
259
+ const u = resolveLane(e);
260
+ if (u) u.Ae.delete(e);
261
261
  if (t) {
262
262
  t(r);
263
- if (u) clearStatus(e, true);
264
- } else if (e._e !== undefined) {
263
+ if (l) clearStatus(e, true);
264
+ } else if (e.o?.De !== undefined) {
265
265
  // Optimistic node — resting OR covered by an active override — holds
266
266
  // through the shared pending-node path, exactly like a plain async memo,
267
267
  // so the commit clears STATUS_UNINITIALIZED (#2806) and elevation to
@@ -271,8 +271,8 @@ function handleAsync(e, n, t) {
271
271
  // (A17 — every reader sees the override); the revert reveals whatever
272
272
  // has committed by then, so corrections reveal atomically with their
273
273
  // transition rather than escaping it.
274
- if (e.De === NOT_PENDING) queuePendingNode(e);
275
- e.De = r;
274
+ if (e.Pe === NOT_PENDING) queuePendingNode(e);
275
+ e.Pe = r;
276
276
  // The hold is a companion-visible write like any other (A13/A19): the
277
277
  // clearStatus() above computed its verdict before the hold existed, so
278
278
  // isPending must re-derive (the value is not final until commit — V1)
@@ -280,22 +280,24 @@ function handleAsync(e, n, t) {
280
280
  // only notified when the hold is visible to them: under an active
281
281
  // override every reader sees the override (A17), so waking subs would
282
282
  // re-show an unchanged view — the revert is the notification point.
283
- GlobalQueue.Pe !== null && GlobalQueue.Pe(e, r);
284
- if (!hasActiveOverride(e)) insertSubs(e);
285
- e.de = clock;
286
- } else if (i) {
283
+ GlobalQueue.Oe !== null && GlobalQueue.Oe(e, r);
284
+ if (!hasActiveOverride(e)) {
285
+ insertSubs(e);
286
+ }
287
+ e.Te = clock;
288
+ } else if (u) {
287
289
  // Route through lane's effect queue for independent flushing
288
290
  const n = e.Re;
289
- const t = e.Ue;
290
- const o = e.be;
291
+ const t = e.be;
292
+ const o = e.Ue;
291
293
  try {
292
- if (!n && u || !o || !o(r, t)) {
293
- e.Ue = r;
294
- e.de = clock;
294
+ if (!n && l || !o || !o(r, t)) {
295
+ e.be = r;
296
+ e.Te = clock;
295
297
  // The latest() shadow write gives latest() effects independent lanes; the
296
298
  // _pendingSignal update is a no-op repeat of the clearStatus() call above
297
299
  // (computePendingState doesn't read _value).
298
- GlobalQueue.Pe !== null && GlobalQueue.Pe(e, r);
300
+ GlobalQueue.Oe !== null && GlobalQueue.Oe(e, r);
299
301
  insertSubs(e, true);
300
302
  }
301
303
  } catch (n) {
@@ -320,7 +322,7 @@ function handleAsync(e, n, t) {
320
322
  // held-value branch is window-gated, and commitPendingNode closes the
321
323
  // window when the hold commits, so no one-frame isPending pulse can leak
322
324
  // to live observers between the landing and its commit (#2990).
323
- if (e.De === NOT_PENDING) e.Ee = false;
325
+ if (e.Pe === NOT_PENDING) e.Ie = false;
324
326
  settlePendingSource(e);
325
327
  schedule();
326
328
  flush();
@@ -334,100 +336,74 @@ function handleAsync(e, n, t) {
334
336
  // Returns whether the node released, so the iterator branch can stop
335
337
  // pulling values instead of pumping an unobserved stream forever (#2935).
336
338
  const settleAutodispose = () => {
337
- if (e.T & CONFIG_AUTO_DISPOSE && !e.o && !(e.S & STATUS_PENDING)) {
339
+ if (e.T & CONFIG_AUTO_DISPOSE && !e.u && !(e.S & STATUS_PENDING)) {
338
340
  unobserved(e);
339
341
  return true;
340
342
  }
341
343
  return false;
342
344
  };
343
- if (o) {
344
- let t = false, r = false, o, i = true;
345
- n.then(e => {
346
- if (i) {
347
- u = e;
348
- t = true;
349
- } else {
350
- asyncWrite(e);
351
- settleAutodispose();
352
- }
353
- }, e => {
354
- if (i) {
355
- o = e;
356
- r = true;
357
- } else {
358
- handleError(e);
359
- settleAutodispose();
360
- }
361
- });
362
- i = false;
363
- if (r) {
364
- // Settle through the same status path an async rejection uses, then
365
- // unwind the in-progress synchronous read so the errored node isn't
366
- // momentarily read as `undefined`.
367
- handleError(o);
368
- throw o;
369
- } else if (!t) {
370
- // Loading window: serve commit #0 instead of suspending. No transition
371
- // is opened — first-flight work on a loadingValue node is loading-class
372
- // (invisible to boundaries and transitions); the flight itself is
373
- // already registered in _inFlight and lands through asyncWrite.
374
- if (e.Ee) return e.Ue;
375
- globalQueue.initTransition(resolveTransition(e));
376
- throw new NotReadyError(context);
377
- } else {
378
- // Synchronously-resolved promise: the first real answer landed.
379
- e.Ee = false;
380
- }
381
- }
382
- if (r) {
383
- const t = n[Symbol.asyncIterator]();
384
- let r = false;
385
- let o = false;
386
- let i = true;
387
- cleanup(() => {
388
- if (o) return;
389
- o = true;
345
+ // Consumes an AsyncIterable as this flight's value stream. Two postures:
346
+ // LIVE (called synchronously from this read the compute returned an
347
+ // iterable directly, or a sync-settled thenable held one), where the
348
+ // initial drain may stash a sync first yield for the caller to return and
349
+ // close registration uses the ambient owner; and DEFERRED (the flattening
350
+ // path — a thenable resolved to an iterable in a later microtask), where
351
+ // there is no caller to serve and no ambient owner: sync-settled steps
352
+ // write through asyncWrite, and close registration goes through the slot
353
+ // the thenable branch pre-registered while it still owned the context.
354
+ // Returns whether a sync answer landed (first yield or empty completion) —
355
+ // meaningful only in the live posture.
356
+ const consumeIterator = (t, r) => {
357
+ const o = t[Symbol.asyncIterator]();
358
+ let u = false;
359
+ let i = false;
360
+ let s = !r;
361
+ const close = () => {
362
+ if (i) return;
363
+ i = true;
390
364
  try {
391
- const e = t.return?.();
365
+ const e = o.return?.();
392
366
  if (isThenable(e)) e.then(undefined, () => {});
393
367
  } catch {}
394
- });
368
+ };
369
+ r ? r(close) : cleanup(close);
395
370
  // Release check before each next pull: an unobserved lazy node must tear
396
- // down (its cleanup above closes the iterator) instead of pumping the
397
- // stream forever with zero subscribers (#2935).
371
+ // down (its close above runs via disposal, closing the iterator) instead
372
+ // of pumping the stream forever with zero subscribers (#2935).
398
373
  const iterateOrRelease = () => {
399
374
  if (!settleAutodispose()) iterate();
400
375
  };
401
376
  const iterate = () => {
402
- let l, s, f = false, a = false, c = true;
377
+ let t, r, f = false, a = false, c = true;
403
378
  // Protocol tolerance, matching `for await`: `await` unwraps whatever
404
379
  // next() returns — a thenable OR a bare IteratorResult. Real producers
405
380
  // use the bare form as a promise-free fast path when a value is already
406
381
  // buffered (seroval's deserialized streams do), so a bare result is
407
382
  // assimilated as an already-settled step instead of crashing on `.then`.
408
- const S = t.next();
383
+ const S = o.next();
409
384
  const d = isThenable(S) ? S : {
410
385
  then: e => void e(S)
411
386
  };
412
- d.then(t => {
387
+ d.then(r => {
413
388
  // The sync stash only serves the INITIAL drain (handleAsync's caller
414
389
  // consumes syncValue / throws NotReady from it). A sync-settled step
415
390
  // after an async gap — seroval buffering values between pulls, a
416
391
  // sync-thenable producer mid-stream — has no caller reading the
417
392
  // stash: it must write through the async path or the value is
418
- // silently dropped.
419
- if (c && i) {
420
- l = t;
393
+ // silently dropped. (The deferred posture never has a caller, so
394
+ // initialRead starts false there and everything writes through.)
395
+ if (c && s) {
396
+ t = r;
421
397
  f = true;
422
- if (t.done) o = true;
423
- } else if (e.Te !== n) {
398
+ if (r.done) i = true;
399
+ } else if (e.o?.Ee !== n) {
424
400
  return;
425
- } else if (!t.done) {
426
- r = true;
427
- asyncWrite(t.value, iterateOrRelease);
401
+ } else if (!r.done) {
402
+ u = true;
403
+ asyncWrite(r.value, iterateOrRelease);
428
404
  } else {
429
- o = true;
430
- if (r) {
405
+ i = true;
406
+ if (u) {
431
407
  schedule();
432
408
  flush();
433
409
  } else {
@@ -437,11 +413,11 @@ function handleAsync(e, n, t) {
437
413
  settleAutodispose();
438
414
  }
439
415
  }, t => {
440
- if (c) {
441
- s = t;
416
+ if (c && s) {
417
+ r = t;
442
418
  a = true;
443
- } else if (e.Te === n) {
444
- o = true;
419
+ } else if (e.o?.Ee === n) {
420
+ i = true;
445
421
  handleError(t);
446
422
  settleAutodispose();
447
423
  }
@@ -449,102 +425,182 @@ function handleAsync(e, n, t) {
449
425
  c = false;
450
426
  if (a) {
451
427
  // Match the promise branch, but only rethrow during the initial read.
452
- o = true;
453
- handleError(s);
454
- if (i) throw s;
428
+ i = true;
429
+ handleError(r);
430
+ if (s) throw r;
455
431
  return true;
456
432
  }
457
- if (f && !l.done) {
458
- u = l.value;
459
- r = true;
433
+ if (f && !t.done) {
434
+ l = t.value;
435
+ u = true;
460
436
  return iterate();
461
437
  }
462
- return f && l.done;
438
+ return f && t.done;
463
439
  };
464
- const l = iterate();
440
+ const f = iterate();
465
441
  // Later iterate() calls run from asyncWrite, where rethrowing would be unhandled.
466
- i = false;
467
- if (!r && !l) {
442
+ s = false;
443
+ return u || f;
444
+ };
445
+ // Landed-synchronously verdict for a LIVE iterator drain; null when no live
446
+ // drain ran (plain promise flight, or a deferred flatten). Drives the
447
+ // shared NotReady/loading tail below.
448
+ let u = null;
449
+ // Flatten one async level: a thenable that RESOLVES to an AsyncIterable —
450
+ // the shape every async stub returning a stream produces — consumes as the
451
+ // stream itself rather than settling on the iterable object. One level
452
+ // only: A+ `then` already collapses nested thenables, so the resolved
453
+ // value is never itself a thenable.
454
+ const flattenIfIterable = (e, n) => {
455
+ let t = false;
456
+ if (typeof e === "object" && e !== null) {
457
+ untrack(() => {
458
+ t = e[Symbol.asyncIterator];
459
+ });
460
+ }
461
+ if (!t) return false;
462
+ const r = consumeIterator(e, n);
463
+ if (!n) u = r;
464
+ return true;
465
+ };
466
+ if (o) {
467
+ let t = false, r = false, o, u = true;
468
+ // Close registration for the flattening path. Consumption starts in a
469
+ // microtask where the ambient owner is gone (or worse, someone else's),
470
+ // so `cleanup()` can't be used — the close targets el's disposal list
471
+ // directly, exactly where a live cleanup() during this recompute would
472
+ // have put it. Deliberately NOT pre-registered at flight start: a
473
+ // non-null `_disposal` reclassifies the node into recompute's deferred
474
+ // (zombie) disposal path, and plain promise flights — the overwhelming
475
+ // majority — must not pay that. Only a flight that actually flattens
476
+ // becomes disposal-bearing, which is exactly the class a directly
477
+ // returned iterable already occupies.
478
+ const registerDeferredClose = n => {
479
+ if (!e.he) e.he = n; else if (Array.isArray(e.he)) e.he.push(n); else e.he = [ e.he, n ];
480
+ };
481
+ n.then(r => {
482
+ if (u) {
483
+ l = r;
484
+ t = true;
485
+ } else if (e.o?.Ee === n && !(e.ie & REACTIVE_DISPOSED) && flattenIfIterable(r, registerDeferredClose)) ; else {
486
+ asyncWrite(r);
487
+ settleAutodispose();
488
+ }
489
+ }, e => {
490
+ if (u) {
491
+ o = e;
492
+ r = true;
493
+ } else {
494
+ handleError(e);
495
+ settleAutodispose();
496
+ }
497
+ });
498
+ u = false;
499
+ if (r) {
500
+ // Settle through the same status path an async rejection uses, then
501
+ // unwind the in-progress synchronous read so the errored node isn't
502
+ // momentarily read as `undefined`.
503
+ handleError(o);
504
+ throw o;
505
+ } else if (!t) {
506
+ // Loading window: serve commit #0 instead of suspending. No transition
507
+ // is opened — first-flight work on a loadingValue node is loading-class
508
+ // (invisible to boundaries and transitions); the flight itself is
509
+ // already registered in _inFlight and lands through asyncWrite.
510
+ if (e.Ie) return e.be;
511
+ globalQueue.initTransition(resolveTransition(e));
512
+ throw new NotReadyError(context);
513
+ } else if (!flattenIfIterable(l)) {
514
+ // Synchronously-resolved promise: the first real answer landed.
515
+ e.Ie = false;
516
+ }
517
+ // A sync-resolved promise holding an AsyncIterable flattened LIVE (we
518
+ // are still inside the synchronous read): full initial-drain semantics
519
+ // apply and the shared tail below settles the verdict.
520
+ }
521
+ if (r) flattenIfIterable(n);
522
+ if (u !== null) {
523
+ if (!u) {
468
524
  // Loading window: serve commit #0 (see the promise branch above).
469
- if (e.Ee) return e.Ue;
525
+ if (e.Ie) return e.be;
470
526
  globalQueue.initTransition(resolveTransition(e));
471
527
  throw new NotReadyError(context);
472
528
  }
473
529
  // A sync first yield (or immediate empty completion) is the first real
474
530
  // answer; async yields clear inside asyncWrite.
475
- e.Ee = false;
531
+ e.Ie = false;
476
532
  }
477
- return u;
533
+ return l;
478
534
  }
479
535
 
480
536
  function clearStatus(e, n = false) {
481
- if (e.oe) clearPendingSources(e);
482
- if (e.ue) e.ue = false;
537
+ if (e.o?.le) clearPendingSources(e);
538
+ if (e.o?.ue) if (e.o !== null) e.o.ue = false;
483
539
  // The pending window is over; its quiet classification dies with it.
484
540
  // (Unconditional: _reask is baked into the node literals, so this is a
485
541
  // plain store to an existing slot — no shape change.)
486
- e.he = false;
542
+ if (e.o !== null) e.o.pe = false;
487
543
  e.S = n ? 0 : e.S & STATUS_UNINITIALIZED;
488
- if (e._) setPendingError(e);
544
+ if (e.o?._) setPendingError(e);
489
545
  // Update pending signal for isPending() reactivity (companions only exist
490
546
  // once the verdict layer created them, which installs the hooks).
491
- if (e.ge || e.pe) GlobalQueue.Se(e);
492
- if (e.u && GlobalQueue.Oe !== null) GlobalQueue.Oe(e);
493
- if (e.i) e.i();
547
+ if (e.o?.Ge || e.o?.ge) GlobalQueue.de(e);
548
+ if (e.o?.l && e.T & CONFIG_CHILD_COMPANIONS && GlobalQueue.ye !== null) GlobalQueue.ye(e);
549
+ if (e.o?.A) e.o.A.call(e);
494
550
  }
495
551
 
496
552
  function notifyStatus(e, n, t, r, o) {
497
553
  // Wrap regular errors to track source node
498
554
  if (n === STATUS_ERROR && !(t instanceof StatusError) && !(t instanceof NotReadyError)) t = new StatusError(e, t);
499
- const u = n === STATUS_PENDING && t instanceof NotReadyError ? t.source : undefined;
500
- const i = u === e;
501
- const l = n === STATUS_PENDING && e._e !== undefined && !i;
502
- const s = l && hasActiveOverride(e);
555
+ const l = n === STATUS_PENDING && t instanceof NotReadyError ? t.source : undefined;
556
+ const u = l === e;
557
+ const i = n === STATUS_PENDING && e.o?.De !== undefined && !u;
558
+ const s = i && hasActiveOverride(e);
503
559
  if (!r) {
504
- if (n === STATUS_PENDING && u) {
505
- addPendingSource(e, u);
560
+ if (n === STATUS_PENDING && l) {
561
+ addPendingSource(e, l);
506
562
  e.S = STATUS_PENDING | e.S & STATUS_UNINITIALIZED;
507
563
  // Preserve the current source on this propagation so render-effect notification
508
564
  // can register every distinct pending source with the transition.
509
- setPendingError(e, u, t);
565
+ setPendingError(e, l, t);
510
566
  } else {
511
567
  clearPendingSources(e);
512
568
  e.S = n | (n !== STATUS_ERROR ? e.S & STATUS_UNINITIALIZED : 0);
513
- e._ = t;
569
+ ext(e)._ = t;
514
570
  }
515
- GlobalQueue.Se !== null && GlobalQueue.Se(e);
516
- if (e.u && GlobalQueue.Oe !== null) GlobalQueue.Oe(e);
571
+ GlobalQueue.de !== null && GlobalQueue.de(e);
572
+ if (e.o?.l && e.T & CONFIG_CHILD_COMPANIONS && GlobalQueue.ye !== null) GlobalQueue.ye(e);
517
573
  }
518
574
  if (o && !r) {
519
575
  assignOrMergeLane(e, o);
520
576
  }
521
577
  const f = r || s;
522
- const a = r || l ? undefined : o;
523
- if (e.i) {
578
+ const a = r || i ? undefined : o;
579
+ if (e.o?.A) {
524
580
  if (r && n === STATUS_PENDING) {
525
581
  return;
526
582
  }
527
583
  if (f) {
528
- e.i(n, t);
584
+ e.o.A.call(e, n, t);
529
585
  } else {
530
- e.i();
586
+ e.o.A.call(e);
531
587
  }
532
588
  return;
533
589
  }
534
590
  forEachDependent(e, (e, r) => {
535
- e.de = clock;
536
- if (n === STATUS_PENDING && u && !e.oe?.has(u) || n !== STATUS_PENDING && (e._ !== t || e.oe)) {
591
+ e.Te = clock;
592
+ if (n === STATUS_PENDING && l && !e.o?.le?.has(l) || n !== STATUS_PENDING && (e.o?._ !== t || e.o?.le)) {
537
593
  // A pending-observer link is the subscription an `isPending` read created.
538
594
  // It exists so the observer re-runs when the source settles, but it must
539
595
  // not carry a real (non-NotReadyError) error — the synchronous `isPending`
540
596
  // read swallows those, and the async path must match. Re-run the observer
541
597
  // so `isPending` re-evaluates (to not-pending) instead of forwarding.
542
- if (r.Ge && n !== STATUS_PENDING && !(t instanceof NotReadyError)) {
598
+ if (r.me && n !== STATUS_PENDING && !(t instanceof NotReadyError)) {
543
599
  enqueueSub(e);
544
600
  schedule();
545
601
  return;
546
602
  }
547
- if (!f && !e.Ne) queuePendingNode(e);
603
+ if (!f && !e._e) queuePendingNode(e);
548
604
  notifyStatus(e, n, t, f, a);
549
605
  }
550
606
  });