@solidjs/signals 2.0.0-rc.17 → 2.0.0-rc.19

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 (71) hide show
  1. package/dist/dev-shared.js +426 -241
  2. package/dist/dev.attribution.js +19 -16
  3. package/dist/dev.js +768 -409
  4. package/dist/observe/boundaries.js +88 -67
  5. package/dist/observe/core/action.js +8 -0
  6. package/dist/observe/core/async.js +183 -138
  7. package/dist/observe/core/attribution.js +30 -28
  8. package/dist/observe/core/constants.js +23 -1
  9. package/dist/observe/core/core.js +185 -157
  10. package/dist/observe/core/effect.js +34 -37
  11. package/dist/observe/core/graph.js +8 -8
  12. package/dist/observe/core/heap.js +5 -5
  13. package/dist/observe/core/lanes.js +104 -84
  14. package/dist/observe/core/owner.js +17 -16
  15. package/dist/observe/core/scheduler.js +61 -47
  16. package/dist/observe/core/truth.js +6 -6
  17. package/dist/observe/core/verdict.js +15 -15
  18. package/dist/observe/deferred.js +40 -38
  19. package/dist/observe/from.js +248 -0
  20. package/dist/observe/index.js +4 -2
  21. package/dist/observe/map.js +22 -22
  22. package/dist/observe/reveal.js +5 -5
  23. package/dist/observe/signals.js +57 -15
  24. package/dist/observe/store/affects.js +1 -1
  25. package/dist/observe/store/hooks.js +3 -0
  26. package/dist/observe/store/optimistic.js +11 -11
  27. package/dist/observe/store/projection.js +34 -21
  28. package/dist/observe/store/reconcile.js +138 -122
  29. package/dist/observe/store/store.js +628 -641
  30. package/dist/observe/store/target.js +1 -5
  31. package/dist/observe/store/utils.js +7 -2
  32. package/dist/prod/boundaries.js +153 -132
  33. package/dist/prod/core/action.js +8 -0
  34. package/dist/prod/core/async.js +242 -197
  35. package/dist/prod/core/constants.js +16 -1
  36. package/dist/prod/core/core.js +166 -138
  37. package/dist/prod/core/effect.js +31 -34
  38. package/dist/prod/core/graph.js +5 -5
  39. package/dist/prod/core/heap.js +12 -12
  40. package/dist/prod/core/lanes.js +98 -78
  41. package/dist/prod/core/owner.js +15 -14
  42. package/dist/prod/core/scheduler.js +64 -50
  43. package/dist/prod/core/truth.js +6 -6
  44. package/dist/prod/core/verdict.js +17 -17
  45. package/dist/prod/deferred.js +40 -38
  46. package/dist/prod/from.js +248 -0
  47. package/dist/prod/index.js +4 -2
  48. package/dist/prod/map.js +14 -14
  49. package/dist/prod/reveal.js +8 -8
  50. package/dist/prod/signals.js +46 -4
  51. package/dist/prod/store/affects.js +1 -1
  52. package/dist/prod/store/hooks.js +3 -0
  53. package/dist/prod/store/optimistic.js +16 -16
  54. package/dist/prod/store/projection.js +22 -9
  55. package/dist/prod/store/reconcile.js +138 -122
  56. package/dist/prod/store/store.js +308 -331
  57. package/dist/prod/store/target.js +13 -17
  58. package/dist/prod/store/utils.js +2 -0
  59. package/dist/types/core/action.d.ts +8 -0
  60. package/dist/types/core/async.d.ts +12 -2
  61. package/dist/types/core/constants.d.ts +25 -0
  62. package/dist/types/core/core.d.ts +33 -4
  63. package/dist/types/core/dev.d.ts +1 -1
  64. package/dist/types/core/index.d.ts +1 -1
  65. package/dist/types/core/lanes.d.ts +5 -1
  66. package/dist/types/core/scheduler.d.ts +17 -3
  67. package/dist/types/from.d.ts +47 -0
  68. package/dist/types/index.d.ts +4 -1
  69. package/dist/types/signals.d.ts +37 -0
  70. package/dist/types/store/target.d.ts +2 -0
  71. package/package.json +1 -1
@@ -1,8 +1,8 @@
1
- import { STATUS_ERROR, STATUS_PENDING, STATUS_UNINITIALIZED, CONFIG_GUESS, CONFIG_HELD, REACTIVE_STAGED_READ, CONFIG_STAGED, CONFIG_STAGED_ERROR, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE, REACTIVE_DIRTY, NOT_PENDING, REACTIVE_DISPOSED, CONFIG_REDERIVE, CONFIG_VERDICT, CONFIG_OVERRIDE } from "./constants.js";
1
+ import { STATUS_ERROR, STATUS_PENDING, STATUS_NO_VALUE, STATUS_UNINITIALIZED, CONFIG_GUESS, REACTIVE_RECOMPUTING_DEPS, CONFIG_HELD, REACTIVE_STAGED_READ, CONFIG_STAGED, CONFIG_STAGED_ERROR, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE, NewSet, REACTIVE_DIRTY, CONFIG_INPUTS_PUBLISHED, NOT_PENDING, REACTIVE_DISPOSED, CONFIG_REDERIVE, CONFIG_VERDICT, CONFIG_OVERRIDE } from "./constants.js";
2
2
 
3
3
  import { attrHooks } from "./attribution-hooks.js";
4
4
 
5
- import { ext, statusNotifierOf, untrack, context, setSignal } from "./core.js";
5
+ import { ext, statusNotifierOf, context, untrack, setSignal } from "./core.js";
6
6
 
7
7
  import "./dev.js";
8
8
 
@@ -12,7 +12,7 @@ import { unobserved, trimStaleDeps } from "./graph.js";
12
12
 
13
13
  import { enqueueSub } from "./heap.js";
14
14
 
15
- import { cleanup } from "./owner.js";
15
+ import { disposeChildren, cleanup } from "./owner.js";
16
16
 
17
17
  import { passLane, joinFuture, question, MAINLINE_QUESTION, globalQueue, schedule, clock, flush, freshHook, txOf, laneStageHook, queuePendingNode } from "./scheduler.js";
18
18
 
@@ -22,22 +22,22 @@ import { passLane, joinFuture, question, MAINLINE_QUESTION, globalQueue, schedul
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, t) {
25
- if (e.T?.fe?.has(t)) return false;
26
- (ext(e).fe ??= new Set).add(t);
25
+ if (e.T?.se?.has(t)) return false;
26
+ (ext(e).se ??= new NewSet).add(t);
27
27
  return true;
28
28
  }
29
29
 
30
30
  function removePendingSource(e, t) {
31
- const n = e.T?.fe;
31
+ const n = e.T?.se;
32
32
  if (!n?.delete(t)) return false;
33
- if (!n.size) e.T.fe = undefined;
33
+ if (!n.size) e.T.se = undefined;
34
34
  return true;
35
35
  }
36
36
 
37
37
  function clearPendingSources(e) {
38
38
  // This set is node-owned and never shared; dropping the sole reference
39
39
  // releases the set and every entry without a redundant clear() walk.
40
- if (e.T !== null) e.T.fe = undefined;
40
+ if (e.T !== null) e.T.se = undefined;
41
41
  }
42
42
 
43
43
  // A rejection-pending only resolves through the settle sweep over the
@@ -48,7 +48,7 @@ function clearPendingSources(e) {
48
48
  function retryReaches(e, t) {
49
49
  for (let n = e.C; n; n = n.G) {
50
50
  const e = n.S;
51
- if (e === t || e.T?.fe?.has(t)) return true;
51
+ if (e === t || e.T?.se?.has(t)) return true;
52
52
  }
53
53
  return false;
54
54
  }
@@ -60,7 +60,7 @@ function retryReaches(e, t) {
60
60
  * with NO read-visible pending status and no downstream propagation. Commit
61
61
  * #0 keeps serving.
62
62
  */ function parkLoadingWindow(e, t) {
63
- ext(e).se = true;
63
+ ext(e).fe = true;
64
64
  if (t.source) addPendingSource(e, t.source);
65
65
  // A settled error is the node's answer ("the error stays the answer until
66
66
  // this retry can actually run") — the park must not replace it: reads
@@ -108,7 +108,7 @@ function releaseIfSettledUnobserved(e) {
108
108
  // Collect-then-release so unobserved() never unlinks under the walk.
109
109
  function releaseSettledDependents(e) {
110
110
  let t;
111
- const n = new Set;
111
+ const n = new NewSet;
112
112
  const visit = e => {
113
113
  if (n.has(e)) return;
114
114
  n.add(e);
@@ -154,9 +154,9 @@ function releaseSettledDependents(e) {
154
154
  // The node answered (an error ends the wait — no longer pending, nor
155
155
  // blocked); a shown error stays its display until the commit.
156
156
  clearPendingSources(e);
157
- if (e.T) e.T.se = false;
157
+ if (e.T) e.T.fe = false;
158
158
  if (!((e.m &= STATUS_ERROR | STATUS_UNINITIALIZED) & STATUS_ERROR)) setPendingError(e);
159
- e.ue = t instanceof StatusError ? t : new StatusError(e, t);
159
+ e.ie = t instanceof StatusError ? t : new StatusError(e, t);
160
160
  e.l |= CONFIG_STAGED | CONFIG_STAGED_ERROR;
161
161
  // Readers parked on the flight re-run and derive from the staging; the
162
162
  // verdict readers the walk wakes need a flush (#4106).
@@ -167,7 +167,7 @@ function releaseSettledDependents(e) {
167
167
 
168
168
  function settleErroredDependents(e, t) {
169
169
  let n = false;
170
- const r = new Set;
170
+ const r = new NewSet;
171
171
  const visit = e => {
172
172
  if (r.has(e)) return;
173
173
  r.add(e);
@@ -192,51 +192,51 @@ function settlePendingSource(e, t = e) {
192
192
  removePendingSource(e, t);
193
193
  let n = false;
194
194
  let r;
195
- const o = new Set;
196
- const settle = f => {
197
- if (o.has(f) ||
195
+ const o = new NewSet;
196
+ const settle = s => {
197
+ if (o.has(s) ||
198
198
  // A conditional dropped this source, but another dependency can still
199
199
  // carry it. Only retire pending state inherited through the recovered
200
200
  // branch. Deliberately NOT marked visited on this early return: the
201
201
  // carrying dependency may itself be a later branch of this same walk
202
202
  // (two unchanged memos converging), and its visit must be free to
203
203
  // re-examine this node once that branch has retired the source.
204
- t !== e && retryReaches(f, t) || !removePendingSource(f, t))
204
+ t !== e && retryReaches(s, t) || !removePendingSource(s, t))
205
205
  // A19: a verdict reader holds no pending of its own (it was re-derived
206
206
  // when the source went pending, `propagateStatus`), and a landing
207
207
  // equal to the committed value notifies nobody — the source settling
208
208
  // is its verdict changing: it runs again.
209
- return f.l & CONFIG_VERDICT && enqueueSub(f);
210
- o.add(f);
211
- f.ie = clock;
212
- const s = f.T?.fe?.values().next().value;
209
+ return s.l & CONFIG_VERDICT && enqueueSub(s);
210
+ o.add(s);
211
+ s.ae = clock;
212
+ const f = s.T?.se?.values().next().value;
213
213
  // STATUS_ERROR + pending sources only coexist via an errored loading
214
214
  // window's park (notifyStatus(STATUS_ERROR) clears pending sources
215
215
  // otherwise): the settled error stays the answer through the settle —
216
216
  // nulling it here would have reads throw `null` until the re-enqueued
217
217
  // retry lands, or lose it entirely if that retry parks again (#2989).
218
- const u = f.m & STATUS_ERROR;
219
- if (s) {
220
- if (!u) setPendingError(f, s);
218
+ const u = s.m & STATUS_ERROR;
219
+ if (f) {
220
+ if (!u) setPendingError(s, f);
221
221
  } else {
222
- f.m &= ~STATUS_PENDING;
223
- if (!u) setPendingError(f);
222
+ s.m &= ~STATUS_PENDING;
223
+ if (!u) setPendingError(s);
224
224
  // L2: a landing resumes the transaction waiting on it (maintainer: "it
225
225
  // resumes the sync frame") — a held reader's re-pass over the landed
226
226
  // value is that transaction's work, and the flush that carries it
227
227
  // parks into it.
228
- if (f.l & CONFIG_HELD) joinFuture(txOf(f));
229
- if (f.T?.se) {
230
- enqueueSub(f);
228
+ if (s.l & CONFIG_HELD) joinFuture(txOf(s));
229
+ if (s.T?.fe) {
230
+ enqueueSub(s);
231
231
  n = true;
232
232
  }
233
- if (f.T !== null) f.T.se = false;
233
+ if (s.T !== null) s.T.fe = false;
234
234
  // Fully settled with nobody watching: release candidate (#2934). Checked
235
235
  // again at release time — deferred so unobserved() can't unlink subs
236
236
  // lists this walk is still iterating.
237
- if (!f.o && f.l & CONFIG_AUTO_DISPOSE) (r ??= []).push(f);
237
+ if (!s.o && s.l & CONFIG_AUTO_DISPOSE) (r ??= []).push(s);
238
238
  }
239
- forEachDependent(f, settle);
239
+ forEachDependent(s, settle);
240
240
  };
241
241
  forEachDependent(e, settle);
242
242
  // Release before the flush schedule below: unobserved() pulls the node back
@@ -251,13 +251,41 @@ function isThenable(e) {
251
251
  }
252
252
 
253
253
  /** Fire and clear a node's iterator-flight cancellation hook (#3122). */ function releaseFlightTeardown(e) {
254
- const t = e.T?.ae;
254
+ const t = e.T?.Se;
255
255
  if (t != null) {
256
- e.T.ae = null;
256
+ e.T.Se = null;
257
257
  t();
258
258
  }
259
259
  }
260
260
 
261
+ /**
262
+ * Internal (not a package export): run `fn` when the current pass's flight
263
+ * is superseded, at the same instant as the iterator close above, instead of
264
+ * when the replacing pass commits (owner cleanups wait for that). Chains onto
265
+ * the node's teardown slot; fires once with it. A no-op unless the ambient
266
+ * owner is a computed in its own pass. `consumeIterator` fills the slot
267
+ * before its first pull, so a source opened in an async generator's body
268
+ * (`for await`) composes after the generator's close.
269
+ */ function onSupersede(e) {
270
+ const t = context;
271
+ if (t === null || t.X === undefined || !(t.R & REACTIVE_RECOMPUTING_DEPS)) return;
272
+ const n = t.T === null;
273
+ const r = ext(t);
274
+ // Allocating the extension mid-run turns `_x?._inFlight` from undefined into
275
+ // null, which recompute would read as a flight this body registered itself.
276
+ // Restore undefined here, so the fix costs nothing for code that never
277
+ // calls onSupersede.
278
+ if (n) r.Ee = undefined;
279
+ const o = r.Se;
280
+ r.Se = o === null ? e : () => {
281
+ try {
282
+ o();
283
+ } finally {
284
+ e();
285
+ }
286
+ };
287
+ }
288
+
261
289
  function handleAsync(e, t, n,
262
290
  // The flight rejected (after the node's status is set): a projection
263
291
  // tells its leaves' readers, who are not the node's dependents
@@ -265,15 +293,15 @@ function handleAsync(e, t, n,
265
293
  // pending on another source, not errored.
266
294
  r) {
267
295
  let o = false;
268
- let f = false;
296
+ let s = false;
269
297
  if (typeof t === "object" && t !== null) {
270
298
  untrack(() => {
271
299
  o = t[Symbol.asyncIterator];
272
- f = !o && isThenable(t);
300
+ s = !o && isThenable(t);
273
301
  });
274
302
  }
275
- if (!f && !o) {
276
- if (e.T !== null) e.T.Se = null;
303
+ if (!s && !o) {
304
+ if (e.T !== null) e.T.Ee = null;
277
305
  // A sync landing is the first real answer for a loadingValue node.
278
306
  e.ce = false;
279
307
  return t;
@@ -283,60 +311,67 @@ r) {
283
311
  // self-registration — runs during a recompute of `el`, which has already
284
312
  // fired _flightTeardown. A future non-recompute registration path must
285
313
  // release it here before overwriting _inFlight.
286
- ext(e).Se = t;
314
+ ext(e).Ee = t;
287
315
  // The question this flight answers (A18 provenance, lanes.ts): the action
288
316
  // whose slice asked it; mainline is always the current question.
289
- e.T.Ee = question || MAINLINE_QUESTION;
317
+ e.T.de = question || MAINLINE_QUESTION;
290
318
  // The run that asked this flight read every input without throwing: an
291
319
  // input still in flight was masked for it (an active override, A17), so
292
320
  // pending state those inputs propagated onto the node earlier does not
293
321
  // describe this answer. Drop it — the flight is the node's pending now.
294
322
  // The landing retires only the flight's own entry (landStatus, #3373), so
295
323
  // an entry that survived here would hold the node past its own answer.
296
- e.T.fe = undefined;
324
+ e.T.se = undefined;
297
325
  // Attribution hook: a new flight is registered. Fired here (not in the
298
326
  // branches below) so every flight shape — plain thenable, iterator, the
299
327
  // flattened combinations — is announced exactly once, while the recompute
300
328
  // frame that caused it is still on the engine's stack. Not inside a try
301
329
  // (#2883 — see attribution-hooks.ts).
302
330
  if (attrHooks !== null) attrHooks.flightStart(e, t);
303
- let s;
331
+ let f;
304
332
  const handleError = o => {
305
- if (e.T?.Se !== t) return;
333
+ if (e.T?.Ee !== t) return;
306
334
  // NotReadyError from rejected promises should be treated as pending, not error
307
- let f = o instanceof NotReadyError;
308
- if (f && e.ce) {
335
+ let s = o instanceof NotReadyError;
336
+ if (s && e.ce) {
309
337
  // Loading window: the flight died waiting on an unready source. Keep
310
338
  // serving commit #0 — same parking as recompute's catch for sync
311
339
  // dependency throws. The dead flight is released so the clock-gated
312
340
  // error-retry pull (updateIfNecessary) can also re-ask.
313
- if (e.T !== null) e.T.Se = null;
341
+ if (e.T !== null) e.T.Ee = null;
314
342
  parkLoadingWindow(e, o);
315
- e.ie = clock;
343
+ e.ae = clock;
316
344
  return;
317
345
  }
318
346
  // D-41: a held node's error is staged (`stageError`). A derived store's
319
347
  // readers learn its error through the family, not the derive's
320
348
  // dependents: it publishes now.
321
- if (f || n || !stageError(e, o)) notifyStatus(e, f ? STATUS_PENDING : STATUS_ERROR, o);
349
+ if (s || n || !stageError(e, o)) {
350
+ notifyStatus(e, s ? STATUS_PENDING : STATUS_ERROR, o);
351
+ // The error is this pass's answer, published now: it replaces the shown
352
+ // frame as a value's commit would (`commitPendingNode`), so the frame
353
+ // the pass parked goes instead of waiting for a later success. A no-op
354
+ // when nothing is parked.
355
+ if (!s) disposeChildren(e, false, true);
356
+ }
322
357
  // A NotReady rejection is a landing into another pending source. The
323
358
  // rejected flight will never settle its self entry, so transfer ownership
324
359
  // after notifyStatus has propagated the replacement source.
325
- if (f) settlePendingSource(e);
326
- e.ie = clock;
360
+ if (s) settlePendingSource(e);
361
+ e.ae = clock;
327
362
  // A real error settles derivatively-pending dependents (notifyStatus
328
363
  // cleared their pending sources), so stranded lazy ones release here —
329
364
  // the error twin of settlePendingSource's release (#2934).
330
- if (!f) releaseSettledDependents(e);
331
- r?.(o, f);
365
+ if (!s) releaseSettledDependents(e);
366
+ r?.(o, s);
332
367
  };
333
368
  const asyncWrite = (r, o) => {
334
- if (e.T?.Se !== t) return;
369
+ if (e.T?.Ee !== t) return;
335
370
  // If the node was dirtied by a newer write (optimistic override or regular),
336
371
  // skip this stale async result — the upcoming flush will recompute the node
337
372
  // with the new value, creating a fresh Promise that supersedes this one.
338
373
  if (e.R & REACTIVE_DIRTY) return;
339
- const f = e.m & STATUS_UNINITIALIZED;
374
+ const s = e.m & STATUS_UNINITIALIZED;
340
375
  landStatus(e);
341
376
  // Attribution hook: lets the engine snapshot state before the landing
342
377
  // branches, so it can tell whether the plain path's setSignal committed a
@@ -350,9 +385,15 @@ r) {
350
385
  return;
351
386
  }
352
387
  // A store derive's landing commits through its setter: unheld, the
353
- // shown error leaves now; held, at the derive's commit.
354
- if (!(e.l & CONFIG_HELD)) e.T.de = undefined;
355
- if (f) landStatus(e, true);
388
+ // shown error leaves now, and so does a commit-beneath-a-flight's
389
+ // CONFIG_INPUTS_PUBLISHED (its first flight's, #4269) — left standing,
390
+ // a verdict reader of the next flight would suspend instead of being
391
+ // served the screen; held, both at the derive's commit.
392
+ if (!(e.l & CONFIG_HELD)) {
393
+ e.T.Te = undefined;
394
+ e.l &= ~CONFIG_INPUTS_PUBLISHED;
395
+ }
396
+ if (s) landStatus(e, true);
356
397
  // Attribution hook: this path landed through the setter (derived
357
398
  // stores, projections), which does not go through setSignal, so the
358
399
  // plain branch's asyncEnd never ran and the flight stayed open — the
@@ -381,7 +422,7 @@ r) {
381
422
  // (`_pendingValue` set inside setSignal) is not — commitPendingNode closes
382
423
  // the window when the flush commits it, so no one-frame pulse can leak to
383
424
  // observers between the landing and its commit (#2990).
384
- if (e.ue === NOT_PENDING) {
425
+ if (e.ie === NOT_PENDING) {
385
426
  e.ce = false;
386
427
  // The landing published: the dependency tail the flight's pass left
387
428
  // linked goes now (A30, #3410). A staged landing has not replaced the
@@ -419,7 +460,7 @@ r) {
419
460
  // empty completion) — meaningful only in the live posture.
420
461
  const consumeIterator = (n, r) => {
421
462
  const o = n[Symbol.asyncIterator]();
422
- let f = false;
463
+ let s = false;
423
464
  let u = false;
424
465
  let i = !r;
425
466
  const close = () => {
@@ -433,8 +474,12 @@ r) {
433
474
  cleanup(close, r);
434
475
  // Flight-identity cancellation (#3122): the registration above is the
435
476
  // owner-death backstop; the teardown slot fires at the _inFlight release
436
- // sites so supersede stops this stream immediately.
437
- ext(e).ae = close;
477
+ // sites so supersede stops this stream immediately. Assigned, not
478
+ // chained (chaining here costs every app bytes): it replaces any
479
+ // `onSupersede` teardown this run registered earlier, which then falls
480
+ // back to the owner-cleanup channel. A returned `from()` is unaffected:
481
+ // `close` calls its `return()`, which runs its own teardown.
482
+ ext(e).Se = close;
438
483
  // Release check before each next pull: an unobserved lazy node must tear
439
484
  // down (its close above runs via disposal, closing the iterator) instead
440
485
  // of pumping the stream forever with zero subscribers (#2935).
@@ -455,11 +500,11 @@ r) {
455
500
  // use the bare form as a promise-free fast path when a value is already
456
501
  // buffered (seroval's deserialized streams do), so a bare result is
457
502
  // assimilated as an already-settled step instead of crashing on `.then`.
458
- const c = o.next();
459
- const E = isThenable(c) ? c : {
460
- then: e => void e(c)
503
+ const E = o.next();
504
+ const c = isThenable(E) ? E : {
505
+ then: e => void e(E)
461
506
  };
462
- E.then(r => {
507
+ c.then(r => {
463
508
  // The sync stash only serves the INITIAL drain (handleAsync's caller
464
509
  // consumes syncValue / throws NotReady from it). A sync-settled step
465
510
  // after an async gap — seroval buffering values between pulls, a
@@ -471,14 +516,14 @@ r) {
471
516
  n = r;
472
517
  l = true;
473
518
  if (r.done) u = true;
474
- } else if (e.T?.Se !== t) {
519
+ } else if (e.T?.Ee !== t) {
475
520
  return;
476
521
  } else if (!r.done) {
477
- f = true;
522
+ s = true;
478
523
  asyncWrite(r.value, iterateOrRelease);
479
524
  } else {
480
525
  u = true;
481
- if (f) {
526
+ if (s) {
482
527
  schedule();
483
528
  flush();
484
529
  } else {
@@ -491,7 +536,7 @@ r) {
491
536
  if (S && i) {
492
537
  r = n;
493
538
  a = true;
494
- } else if (e.T?.Se === t) {
539
+ } else if (e.T?.Ee === t) {
495
540
  u = true;
496
541
  handleError(n);
497
542
  settleAutodispose();
@@ -506,8 +551,8 @@ r) {
506
551
  return true;
507
552
  }
508
553
  if (l && !n.done) {
509
- s = n.value;
510
- f = true;
554
+ f = n.value;
555
+ s = true;
511
556
  return iterate();
512
557
  }
513
558
  return l && n.done;
@@ -515,7 +560,7 @@ r) {
515
560
  const l = iterate();
516
561
  // Later iterate() calls run from asyncWrite, where rethrowing would be unhandled.
517
562
  i = false;
518
- return f || l;
563
+ return s || l;
519
564
  };
520
565
  // Landed-synchronously verdict for a LIVE iterator drain; null when no live
521
566
  // drain ran (plain promise flight, or a deferred flatten). Drives the
@@ -534,16 +579,16 @@ r) {
534
579
  });
535
580
  }
536
581
  if (!n) return false;
537
- s = undefined;
582
+ f = undefined;
538
583
  const r = consumeIterator(e, t);
539
584
  if (!t) u = r;
540
585
  return true;
541
586
  };
542
- if (f) {
543
- let n = false, r = false, o, f = true;
587
+ if (s) {
588
+ let n = false, r = false, o, s = true;
544
589
  t.then(r => {
545
- if (f) {
546
- s = r;
590
+ if (s) {
591
+ f = r;
547
592
  n = true;
548
593
  } else {
549
594
  // Flattening consumes the stream as the value: each yield lands
@@ -556,7 +601,7 @@ r) {
556
601
  // compute catch above it: a protocol throw settles as this
557
602
  // flight's error (#4112).
558
603
  try {
559
- if (e.T?.Se === t && !(e.R & REACTIVE_DISPOSED) && flattenIfIterable(r, e)) return;
604
+ if (e.T?.Ee === t && !(e.R & REACTIVE_DISPOSED) && flattenIfIterable(r, e)) return;
560
605
  asyncWrite(r);
561
606
  } catch (e) {
562
607
  handleError(e);
@@ -564,7 +609,7 @@ r) {
564
609
  settleAutodispose();
565
610
  }
566
611
  }, e => {
567
- if (f) {
612
+ if (s) {
568
613
  o = e;
569
614
  r = true;
570
615
  } else {
@@ -572,7 +617,7 @@ r) {
572
617
  settleAutodispose();
573
618
  }
574
619
  });
575
- f = false;
620
+ s = false;
576
621
  if (r) {
577
622
  // Settle through the same status path an async rejection uses, then
578
623
  // unwind the in-progress synchronous read so the errored node isn't
@@ -580,13 +625,13 @@ r) {
580
625
  handleError(o);
581
626
  throw o;
582
627
  } else if (!n) {
583
- // Loading window: serve commit #0 instead of suspending — first-flight
584
- // work on a loadingValue node is loading-class (invisible to
585
- // boundaries); the flight itself is already registered in _inFlight and
586
- // lands through asyncWrite.
587
- if (e.ce) return e.Ie;
588
- throw new NotReadyError(context);
589
- } else if (!flattenIfIterable(s)) {
628
+ // Not landed: the shared tail below suspends, or — loading window —
629
+ // serves commit #0 (first-flight work on a loadingValue node is
630
+ // loading-class, invisible to boundaries; the flight itself is already
631
+ // registered in _inFlight and lands through asyncWrite). A promise is
632
+ // never an iterator, so nothing between here and there runs.
633
+ u = false;
634
+ } else if (!flattenIfIterable(f)) {
590
635
  // Synchronously-resolved promise: the first real answer landed.
591
636
  e.ce = false;
592
637
  }
@@ -597,20 +642,20 @@ r) {
597
642
  if (o) flattenIfIterable(t);
598
643
  if (u !== null) {
599
644
  if (!u) {
600
- // Loading window: serve commit #0 (see the promise branch above).
601
- if (e.ce) return e.Ie;
645
+ // Loading window: serve commit #0 instead of suspending.
646
+ if (e.ce) return e.le;
602
647
  throw new NotReadyError(context);
603
648
  }
604
649
  // A sync first yield (or immediate empty completion) is the first real
605
650
  // answer; async yields clear inside asyncWrite.
606
651
  e.ce = false;
607
652
  }
608
- return s;
653
+ return f;
609
654
  }
610
655
 
611
656
  function clearStatus(e, t = false) {
612
- if (e.T?.fe) clearPendingSources(e);
613
- if (e.T?.se) if (e.T !== null) e.T.se = false;
657
+ if (e.T?.se) clearPendingSources(e);
658
+ if (e.T?.fe) e.T.fe = false;
614
659
  e.m = t ? 0 : e.m & STATUS_UNINITIALIZED;
615
660
  if (e.T?.F) setPendingError(e);
616
661
  const n = statusNotifierOf(e);
@@ -642,34 +687,35 @@ function clearStatus(e, t = false) {
642
687
  * reader thrown NotReady(self) would park on a retired entry. Companions
643
688
  * keep their verdict (pending before and after; the write re-syncs them).
644
689
  */ function landStatus(e, t = false) {
645
- const n = e.T?.fe;
690
+ const n = e.T?.se;
646
691
  // (The full clear below drops the set whether or not self was retired first.)
647
692
  if (n && (n.delete(e), n.size)) {
648
- e.T.se = false;
693
+ e.T.fe = false;
649
694
  if (t) e.m = STATUS_PENDING;
650
695
  setPendingError(e, n.values().next().value);
651
696
  } else clearStatus(e, t);
652
697
  }
653
698
 
654
- function notifyStatus(e, t, n, r) {
699
+ function notifyStatus(e, t, n) {
655
700
  // Wrap regular errors to track source node
656
701
  if (t === STATUS_ERROR && !(n instanceof StatusError) && !(n instanceof NotReadyError)) n = new StatusError(e, n);
657
- const o = t === STATUS_PENDING && n instanceof NotReadyError ? n.source : undefined;
702
+ const r = t === STATUS_PENDING && n instanceof NotReadyError ? n.source : undefined;
658
703
  // (CARVE 2: the lane assignment went with the engine.)
659
- {
660
- if (t === STATUS_PENDING && o) {
661
- // D-41: a re-ask of a shown error keeps it the display (`_shownError`).
662
- if (e.m & STATUS_ERROR) e.T.de = e.T.F;
663
- addPendingSource(e, o);
664
- e.m = STATUS_PENDING | e.m & STATUS_UNINITIALIZED;
665
- // Preserve the current source on this propagation so readers park on
666
- // the distinct pending source.
667
- setPendingError(e, o, n);
668
- } else {
669
- clearPendingSources(e);
670
- e.m = t | (t !== STATUS_ERROR ? e.m & STATUS_UNINITIALIZED : 0);
671
- ext(e).F = n;
672
- }
704
+ if (t === STATUS_PENDING && r) {
705
+ // D-41: a re-ask of a shown error keeps it the display (`_shownError`).
706
+ if (e.m & STATUS_ERROR) e.T.Te = e.T.F;
707
+ addPendingSource(e, r);
708
+ // A re-ask of an error that answered the first read: still no value.
709
+ e.m = STATUS_PENDING | (e.m & STATUS_NO_VALUE ? STATUS_UNINITIALIZED : e.m & STATUS_UNINITIALIZED);
710
+ // Preserve the current source on this propagation so readers park on
711
+ // the distinct pending source.
712
+ setPendingError(e, r, n);
713
+ } else {
714
+ clearPendingSources(e);
715
+ // An error answering a node with no value marks it so; a re-ask of it
716
+ // (pending) is uninitialized again; a write (STATUS_NONE) gives it one.
717
+ e.m = t | (t === STATUS_ERROR ? e.m & (STATUS_UNINITIALIZED | STATUS_NO_VALUE) ? STATUS_NO_VALUE : 0 : t === STATUS_PENDING && e.m & STATUS_NO_VALUE ? STATUS_UNINITIALIZED : e.m & STATUS_UNINITIALIZED);
718
+ ext(e).F = n;
673
719
  }
674
720
  // Lanes: a written guess is an optimistic boundary (A17 — "the optimistic
675
721
  // future value until we know otherwise"). Its own source in flight is its
@@ -681,11 +727,9 @@ function notifyStatus(e, t, n, r) {
681
727
  if (passLane === null) joinFuture(null);
682
728
  return;
683
729
  }
684
- const f = statusNotifierOf(e);
685
- if (f) {
686
- {
687
- f.call(e);
688
- }
730
+ const o = statusNotifierOf(e);
731
+ if (o) {
732
+ o.call(e);
689
733
  return;
690
734
  }
691
735
  propagateStatus(e, t, n);
@@ -695,10 +739,10 @@ function notifyStatus(e, t, n, r) {
695
739
  * subscriber as a propagated mark (or a re-derive). On its own for a store
696
740
  * family's leaves, which are not the derive's dependents — the family
697
741
  * propagates the derive's status from each leaf to the leaf's readers
698
- * (store/projection.ts `wakeFamily`), exactly as a memo's would reach its. */ function propagateStatus(e, t, n, r) {
699
- const o = t === STATUS_PENDING && n instanceof NotReadyError ? n.source : undefined;
700
- forEachDependent(e, (r, f) => {
701
- r.ie = clock;
742
+ * (store/projection.ts `wakeFamily`), exactly as a memo's would reach its. */ function propagateStatus(e, t, n) {
743
+ const r = t === STATUS_PENDING && n instanceof NotReadyError ? n.source : undefined;
744
+ forEachDependent(e, (o, s) => {
745
+ o.ae = clock;
702
746
  // A pending mark on a kept-tail link re-derives the subscriber instead of
703
747
  // marking it (A30, #3494 review; fuzzer latest-1 #2141; #3519 review).
704
748
  // Past `_depsTail` lie the committed frame's deps, kept by A30 because
@@ -718,8 +762,8 @@ function notifyStatus(e, t, n, r) {
718
762
  // Inheriting the error would make a probe's reader errored. A boundary's
719
763
  // output (CONFIG_REDERIVE, boundaries.ts) the same: content or fallback
720
764
  // is its pass's call.
721
- if (r.l & (CONFIG_REDERIVE | CONFIG_VERDICT) || t === STATUS_PENDING && f.Te !== r.Ne) {
722
- enqueueSub(r);
765
+ if (o.l & (CONFIG_REDERIVE | CONFIG_VERDICT) || t === STATUS_PENDING && s.Ie !== o.Ne) {
766
+ enqueueSub(o);
723
767
  schedule();
724
768
  return;
725
769
  }
@@ -730,30 +774,31 @@ function notifyStatus(e, t, n, r) {
730
774
  // frame still passes through — its pass is the frame's (#3372, compiled
731
775
  // shape: the unmount it stages makes the observer a zombie whose say is
732
776
  // moot for this transaction, A15 #3463).
733
- if (t === STATUS_PENDING && o && r.T?.fe?.has(o)) {
734
- if (passLane === null && (r.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && globalQueue.q && !freshHook?.(r, o.ie === clock)) joinFuture(txOf(r));
777
+ if (t === STATUS_PENDING && r && o.T?.se?.has(r)) {
778
+ if (passLane === null && (o.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && globalQueue.q && !freshHook?.(o, r.ae === clock)) joinFuture(txOf(o));
735
779
  return;
736
780
  }
737
- if (t === STATUS_PENDING && o || t !== STATUS_PENDING && (r.T?.F !== n || r.T?.fe)) {
781
+ if (t === STATUS_PENDING && r || t !== STATUS_PENDING && (o.T?.F !== n || o.T?.se)) {
738
782
  // The marked subscriber is queued for the commit sweep so its status
739
783
  // bookkeeping (loading window, uninitialized clear) runs with the flush
740
784
  // — with the lane, when the pass propagating is the lane's work and
741
785
  // the dependent's pending is the lane's own flight (`laneStage`).
742
- {
743
- // (No answer: a pending propagation leaves a written guess a guess.)
744
- if (laneStageHook && passLane !== null) laneStageHook(r, passLane, true, e); else {
745
- queuePendingNode(r);
746
- // A15 (#3443): pending propagates onto a held memo without
747
- // recomputing it, and the propagation itself enters the memo's
748
- // transaction — the flight flows into a memo that transaction
749
- // holds, so the write that started it is held with it. A render
750
- // effect's membership is its pass's (`notify`), never sticky.
751
- if (t === STATUS_PENDING && (r.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && !r.i) joinFuture(txOf(r));
752
- }
786
+ // (No answer: a pending propagation leaves a written guess a guess.)
787
+ if (laneStageHook && passLane !== null) laneStageHook(o, passLane, true, e); else {
788
+ queuePendingNode(o);
789
+ // A15 (#3443): pending propagates onto a held memo without
790
+ // recomputing it, and the propagation itself enters the memo's
791
+ // transaction — the flight flows into a memo that transaction
792
+ // holds, so the write that started it is held with it. A render
793
+ // effect's membership is its pass's (`notify`), never sticky.
794
+ if (t === STATUS_PENDING && (o.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && !o.i) joinFuture(txOf(o));
753
795
  }
754
- notifyStatus(r, t, n);
796
+ notifyStatus(o, t, n);
797
+ // A derived store's derive (store/projection.ts): marked, not re-run —
798
+ // its leaves' readers are not its dependents, so the family walks them.
799
+ o.fam?.st(t);
755
800
  }
756
801
  });
757
802
  }
758
803
 
759
- export { addPendingSource, clearStatus, forEachDependent, handleAsync, isThenable, notifyStatus, parkLoadingWindow, propagateStatus, releaseFlightTeardown, releaseIfSettledUnobserved, releaseSettledDependents, setPendingError, settleErroredDependents, settlePendingSource, stageError };
804
+ export { addPendingSource, clearStatus, forEachDependent, handleAsync, isThenable, notifyStatus, onSupersede, parkLoadingWindow, propagateStatus, releaseFlightTeardown, releaseIfSettledUnobserved, releaseSettledDependents, setPendingError, settleErroredDependents, settlePendingSource, stageError };