@solidjs/signals 2.0.0-rc.18 → 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 (65) hide show
  1. package/dist/dev-shared.js +168 -88
  2. package/dist/dev.attribution.js +16 -13
  3. package/dist/dev.js +272 -129
  4. package/dist/observe/boundaries.js +88 -67
  5. package/dist/observe/core/action.js +8 -0
  6. package/dist/observe/core/async.js +46 -34
  7. package/dist/observe/core/attribution.js +30 -28
  8. package/dist/observe/core/constants.js +21 -1
  9. package/dist/observe/core/core.js +164 -136
  10. package/dist/observe/core/effect.js +6 -6
  11. package/dist/observe/core/graph.js +3 -3
  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 +10 -9
  15. package/dist/observe/core/scheduler.js +38 -35
  16. package/dist/observe/core/truth.js +6 -6
  17. package/dist/observe/core/verdict.js +13 -13
  18. package/dist/observe/deferred.js +39 -40
  19. package/dist/observe/index.js +2 -2
  20. package/dist/observe/map.js +22 -22
  21. package/dist/observe/reveal.js +5 -5
  22. package/dist/observe/signals.js +57 -15
  23. package/dist/observe/store/affects.js +1 -1
  24. package/dist/observe/store/hooks.js +3 -0
  25. package/dist/observe/store/optimistic.js +11 -11
  26. package/dist/observe/store/projection.js +34 -21
  27. package/dist/observe/store/reconcile.js +2 -2
  28. package/dist/observe/store/store.js +63 -53
  29. package/dist/observe/store/utils.js +7 -2
  30. package/dist/prod/boundaries.js +153 -132
  31. package/dist/prod/core/action.js +8 -0
  32. package/dist/prod/core/async.js +55 -43
  33. package/dist/prod/core/constants.js +14 -1
  34. package/dist/prod/core/core.js +162 -134
  35. package/dist/prod/core/effect.js +9 -9
  36. package/dist/prod/core/graph.js +3 -3
  37. package/dist/prod/core/heap.js +12 -12
  38. package/dist/prod/core/lanes.js +98 -78
  39. package/dist/prod/core/owner.js +15 -14
  40. package/dist/prod/core/scheduler.js +43 -40
  41. package/dist/prod/core/truth.js +6 -6
  42. package/dist/prod/core/verdict.js +17 -17
  43. package/dist/prod/deferred.js +39 -40
  44. package/dist/prod/index.js +2 -2
  45. package/dist/prod/map.js +14 -14
  46. package/dist/prod/reveal.js +8 -8
  47. package/dist/prod/signals.js +46 -4
  48. package/dist/prod/store/affects.js +1 -1
  49. package/dist/prod/store/hooks.js +3 -0
  50. package/dist/prod/store/optimistic.js +16 -16
  51. package/dist/prod/store/projection.js +22 -9
  52. package/dist/prod/store/reconcile.js +2 -2
  53. package/dist/prod/store/store.js +262 -262
  54. package/dist/prod/store/utils.js +2 -0
  55. package/dist/types/core/action.d.ts +8 -0
  56. package/dist/types/core/constants.d.ts +20 -0
  57. package/dist/types/core/core.d.ts +14 -1
  58. package/dist/types/core/dev.d.ts +1 -1
  59. package/dist/types/core/index.d.ts +1 -1
  60. package/dist/types/core/lanes.d.ts +5 -1
  61. package/dist/types/core/scheduler.d.ts +2 -0
  62. package/dist/types/index.d.ts +3 -1
  63. package/dist/types/signals.d.ts +37 -0
  64. package/dist/types/store/target.d.ts +2 -0
  65. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { STATUS_PENDING, STATUS_ERROR, CONFIG_REDERIVE, STATUS_UNINITIALIZED, CONFIG_HELD, EFFECT_RENDER, CONFIG_VERDICT, REACTIVE_DISPOSED, REACTIVE_LANE_READ, CONFIG_OVERRIDE, EFFECT_USER, EFFECT_TRACKED, REACTIVE_ZOMBIE, CAUGHT_REARMED } from "./core/constants.js";
1
+ import { STATUS_PENDING, STATUS_ERROR, CONFIG_REDERIVE, CONFIG_HELD, CONFIG_FRAME, STATUS_UNINITIALIZED, EFFECT_RENDER, CONFIG_VERDICT, REACTIVE_DISPOSED, REACTIVE_LANE_READ, CONFIG_OVERRIDE, EFFECT_USER, EFFECT_TRACKED, REACTIVE_ZOMBIE, CAUGHT_REARMED } from "./core/constants.js";
2
2
 
3
3
  import { signal, runWithOwner, computed, read, setSignal, recompute } from "./core/core.js";
4
4
 
@@ -14,7 +14,7 @@ import { enqueueSub } from "./core/heap.js";
14
14
 
15
15
  import { createOwner, getOwner, cleanup, dispose } from "./core/owner.js";
16
16
 
17
- import { globalQueue, haltReactivityAt, haltReactivity, passLane, joinFuture, GlobalQueue, flushTransaction, heldUnder, setPassLane, passTx, resolveTx, schedule, txOf, unhold, laneStageHook, installBoundaryHooks } from "./core/scheduler.js";
17
+ import { globalQueue, haltReactivityAt, haltReactivity, schedule, passLane, joinFuture, GlobalQueue, flushTransaction, heldUnder, setPassLane, passTx, resolveTx, txOf, unhold, laneStageHook, installBoundaryHooks } from "./core/scheduler.js";
18
18
 
19
19
  import { attrHooks } from "./core/attribution-hooks.js";
20
20
 
@@ -69,6 +69,9 @@ import { accessor } from "./signals.js";
69
69
  * slot of (reveal.ts); a boundary clears it for its content — only direct
70
70
  * children are slots. */ const REVEAL = Symbol("");
71
71
 
72
+ // Every call site checks the hooks themselves first (not only the slot or
73
+ // controller, which are never set without them): a build that does not
74
+ // import `createRevealOrder` folds the reveal-order arms away.
72
75
  let revealHooks = null;
73
76
 
74
77
  function setRevealHooks(e) {
@@ -98,9 +101,9 @@ const boundaryOf = e => e.A[BOUNDARY];
98
101
  // the same node.
99
102
  n ??= r.T?.F;
100
103
  r = n?.source ?? r;
101
- const t = unwrapStatusError(n);
102
- setSignal(e.F, t);
103
- reportClientError(t, e.H, r);
104
+ const o = unwrapStatusError(n);
105
+ setSignal(e.F, o);
106
+ reportClientError(o, e.H, r);
104
107
  }
105
108
  record(e, r);
106
109
  if (e.h) flip(e);
@@ -108,7 +111,7 @@ const boundaryOf = e => e.A[BOUNDARY];
108
111
  // nothing on screen. The fallback is instantiated once per show — a later
109
112
  // error reaches it through the `error` accessor, which is why it is one.
110
113
  if (!e.L) redraw(e);
111
- if (e.v !== null) revealHooks.P(e.v);
114
+ if (revealHooks && e.v !== null) revealHooks.P(e.v);
112
115
  }
113
116
 
114
117
  /** A re-armed boundary found something pending: fresh again, and the swap
@@ -120,7 +123,7 @@ const boundaryOf = e => e.A[BOUNDARY];
120
123
  e.V = e.B;
121
124
  const r = e.j;
122
125
  if (!e.V && r.l & CONFIG_HELD && txOf(r) !== flushTransaction) unhold(r);
123
- if (e.V !== null && e.j !== null) laneStageHook(e.j, e.V, true, false);
126
+ if (laneStageHook && e.V !== null && e.j !== null) laneStageHook(e.j, e.V, true, false);
124
127
  }
125
128
 
126
129
  function redraw(e) {
@@ -143,17 +146,17 @@ function redraw(e) {
143
146
  // on screen changes, the fallback stays as it is.
144
147
  if (collecting.size !== 0) for (let r = boundaryOf(e); r; r = r._parent) if (!(e.m & unsettled(r)) && r.U.delete(e) && r.U.size === 0) {
145
148
  if (r.L) redraw(r);
146
- if (r.v !== null) revealHooks.P(r.v);
149
+ if (revealHooks && r.v !== null) revealHooks.P(r.v);
147
150
  }
148
151
  return false;
149
152
  }
150
- for (let t = boundaryOf(e); t; t = t._parent) {
151
- if (!(t.i & r)) continue;
152
- if (isCollecting(t)) {
153
- caught(t, e, n);
154
- return t.h ? CAUGHT_REARMED : true;
153
+ for (let o = boundaryOf(e); o; o = o._parent) {
154
+ if (!(o.i & r)) continue;
155
+ if (isCollecting(o)) {
156
+ caught(o, e, n);
157
+ return o.h ? CAUGHT_REARMED : true;
155
158
  }
156
- record(t, e);
159
+ record(o, e);
157
160
  }
158
161
  return false;
159
162
  }
@@ -182,19 +185,19 @@ function redraw(e) {
182
185
  * still showing its fallback holds it again then. */ function heldRun(e) {
183
186
  const r = hiddenBy(e);
184
187
  if (r === undefined) return false;
185
- (r.Z ??= new Set).add(e);
188
+ (r.M ??= new Set).add(e);
186
189
  return true;
187
190
  }
188
191
 
189
192
  /** The reveal: the runs held behind the fallback are this flush's, each in
190
193
  * its own phase — render updates with the frame that attaches the content,
191
194
  * user effects after it. */ function release(e) {
192
- if (e.Z === null) return;
193
- for (const r of e.Z) {
195
+ if (e.M === null) return;
196
+ for (const r of e.M) {
194
197
  const e = r.i;
195
- globalQueue.enqueue(e === EFFECT_TRACKED ? EFFECT_USER : e, e === EFFECT_TRACKED ? r.M : r.W ??= GlobalQueue.K.bind(null, r));
198
+ globalQueue.enqueue(e === EFFECT_TRACKED ? EFFECT_USER : e, e === EFFECT_TRACKED ? r.Z : r.W ??= GlobalQueue.K.bind(null, r));
196
199
  }
197
- e.Z = null;
200
+ e.M = null;
198
201
  }
199
202
 
200
203
  /** Drop the readers that settled or died. Settled: a loading reader no
@@ -206,9 +209,9 @@ function redraw(e) {
206
209
 
207
210
  function prune(e, r) {
208
211
  const n = unsettled(e);
209
- for (const t of e.U) {
210
- if (t.R & REACTIVE_DISPOSED) e.U.delete(t); else if (!(t.m & n)) {
211
- if ((t !== e.Y || e.h) && (t.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD) {
212
+ for (const o of e.U) {
213
+ if (o.R & REACTIVE_DISPOSED) e.U.delete(o); else if (!(o.m & n)) {
214
+ if ((o !== e.Y || e.h) && (o.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD) {
212
215
  // Landed, held: the content is that transaction's — the output's
213
216
  // pass enters it and the reveal lands with the reader's run. The
214
217
  // seam, past its park decision, waits for the commit instead. (A
@@ -217,9 +220,9 @@ function prune(e, r) {
217
220
  // the reader is committed (#3540: what a fresh boundary catches holds
218
221
  // nothing outside it).
219
222
  if (!r || !e.D) continue;
220
- joinFuture(txOf(t));
223
+ joinFuture(txOf(o));
221
224
  }
222
- e.U.delete(t);
225
+ e.U.delete(o);
223
226
  }
224
227
  }
225
228
  return e.U.size;
@@ -252,7 +255,7 @@ function prune(e, r) {
252
255
  if (e.U.size === 0) {
253
256
  collecting.delete(e);
254
257
  if (e.L) redraw(e);
255
- if (e.v !== null) revealHooks.P(e.v);
258
+ if (revealHooks && e.v !== null) revealHooks.P(e.v);
256
259
  }
257
260
  }
258
261
  }
@@ -278,6 +281,8 @@ function prune(e, r) {
278
281
  return;
279
282
  }
280
283
  e.h = true;
284
+ // The swap is the change's work: the output joins it (D-13, #3540).
285
+ e.j.l &= ~CONFIG_FRAME;
281
286
  // A verdict read in `on` (`isPending`, or a memo of one — #3528's
282
287
  // `createMemo(() => isPending(m2))`: the mark carries, core.ts `read`)
283
288
  // whose frame has no transaction yet: the swap is still display-ahead — the frame gets one now (it lands
@@ -313,12 +318,12 @@ function prune(e, r) {
313
318
  schedule();
314
319
  }
315
320
 
316
- function createBoundary(e, r, n, t) {
317
- const o = createOwner();
321
+ function createBoundary(e, r, n, o) {
322
+ const t = createOwner();
318
323
  const l = {
319
324
  i: e,
320
- _parent: boundaryOf(o) ?? null,
321
- H: o,
325
+ _parent: boundaryOf(t) ?? null,
326
+ H: t,
322
327
  Y: null,
323
328
  j: null,
324
329
  U: new Set,
@@ -327,7 +332,7 @@ function createBoundary(e, r, n, t) {
327
332
  B: null,
328
333
  V: null,
329
334
  L: false,
330
- Z: null,
335
+ M: null,
331
336
  v: null,
332
337
  $: false,
333
338
  ee: false,
@@ -335,21 +340,21 @@ function createBoundary(e, r, n, t) {
335
340
  re: n,
336
341
  ne: null
337
342
  };
338
- const u = e === STATUS_PENDING ? o.A[REVEAL] : null;
343
+ const u = e === STATUS_PENDING ? t.A[REVEAL] : null;
339
344
  if (getOwner()) cleanup(() => {
340
345
  collecting.delete(l);
341
- if (u) revealHooks.te(u, l);
346
+ if (revealHooks && u) revealHooks.oe(u, l);
342
347
  });
343
348
  // The `on` dependencies live OUTSIDE the boundary, as the condition of a
344
349
  // `<Show>` wrapping it would: created before the owner's context names it.
345
350
  // A source of it going pending re-runs it (CONFIG_REDERIVE) rather than
346
351
  // marking it: the pass catches the NotReady — it is a notification, it
347
352
  // does not suspend the parent — and re-arms.
348
- if (t) {
353
+ if (o) {
349
354
  let e = false;
350
- runWithOwner(o, () => computed(() => {
355
+ runWithOwner(t, () => computed(() => {
351
356
  try {
352
- t();
357
+ o();
353
358
  } catch (e) {
354
359
  if (!(e instanceof NotReadyError)) {
355
360
  if (!globalQueue.notify(getOwner(), STATUS_ERROR, STATUS_ERROR, e)) {
@@ -363,12 +368,12 @@ function createBoundary(e, r, n, t) {
363
368
  })).l |= CONFIG_REDERIVE;
364
369
  }
365
370
  const a = {
366
- ...o.A,
371
+ ...t.A,
367
372
  [BOUNDARY]: l
368
373
  };
369
374
  if (revealHooks !== null) a[REVEAL] = null;
370
- o.A = a;
371
- const i = runWithOwner(o, () => {
375
+ t.A = a;
376
+ const s = runWithOwner(t, () => {
372
377
  const e = true ? computed(r, {
373
378
  name: "children"
374
379
  }) : computed(r);
@@ -376,23 +381,30 @@ function createBoundary(e, r, n, t) {
376
381
  name: "boundary"
377
382
  }) : computed(() => flatten(read(e)));
378
383
  });
379
- l.Y = i;
384
+ l.Y = s;
380
385
  // A slot of the reveal order in its context (reveal.ts): gated until the
381
386
  // controller releases it — before the output's first pass.
382
- if (u) revealHooks.oe(u, l);
383
- const s = computed(() => {
387
+ if (revealHooks && u) revealHooks.te(u, l);
388
+ const i = computed(() => {
389
+ // Structure, not a derivation of its content (CONFIG_FRAME, #4283): it
390
+ // reads a hold it is not part of as the screen, as the render effect
391
+ // flattening the same children without a boundary would. Not while a
392
+ // re-arm's swap is the change's work (`arm`), until that swap commits
393
+ // (D-13, #3540).
394
+ const r = getOwner();
395
+ if (!l.h && !(r.l & CONFIG_HELD)) r.l |= CONFIG_FRAME;
384
396
  // Reveal order (reveal.ts): a gated slot shows its fallback — or, in a
385
397
  // collapsed tail, nothing — whatever its content's state; the
386
398
  // controller is told the pass ran (its readiness may have changed) and
387
399
  // releases it in turn.
388
- if (l.$) {
400
+ if (l.$) {
389
401
  // Linked without being read: the tree settling (or changing)
390
402
  // re-derives this pass, which is how the controller hears of it.
391
403
  // It may release this very slot (the frontier whose content just
392
404
  // settled): the pass goes on to show it — the redraw it asks for is
393
405
  // refused mid-pass and not needed.
394
- link(i, getOwner());
395
- if (l.v !== null) revealHooks.P(l.v);
406
+ link(s, r);
407
+ if (revealHooks && l.v !== null) revealHooks.P(l.v);
396
408
  if (l.$) {
397
409
  if (l.ee) {
398
410
  l.L = true;
@@ -408,21 +420,21 @@ function createBoundary(e, r, n, t) {
408
420
  // display-ahead `on` causes is the lane's, and must still be the
409
421
  // fallback. Linked, so its recovery re-derives this pass; read again
410
422
  // once clean.
411
- const r = i.m & (STATUS_PENDING | STATUS_ERROR);
412
- if (r !== 0) {
413
- link(i, getOwner());
414
- if (r & e) {
423
+ const n = s.m & (STATUS_PENDING | STATUS_ERROR);
424
+ if (n !== 0) {
425
+ link(s, r);
426
+ if (n & e) {
415
427
  if (isCollecting(l)) {
416
- caught(l, i, undefined);
428
+ caught(l, s, undefined);
417
429
  return fallback(l);
418
430
  }
419
431
  // Forwarded: a loading boundary showing content keeps it and
420
432
  // holds the frame like any reader (recorded for its `on`).
421
- record(l, i);
433
+ record(l, s);
422
434
  }
423
435
  // A pending passes an error boundary by (A6); an error, a loading
424
436
  // one; a loading boundary showing content forwards its pending.
425
- throw i.T.F;
437
+ throw s.T.F;
426
438
  }
427
439
  if (isCollecting(l)) {
428
440
  // A29's boundary exemption (#3540): a boundary MOUNTED over a held
@@ -436,24 +448,23 @@ function createBoundary(e, r, n, t) {
436
448
  // enters: the outside sees its committed value until the landing,
437
449
  // which reveals the content — a fallback staged earlier is replaced
438
450
  // ahead of the commit and never shown.
439
- const e = getOwner();
440
- if (l.U.size !== 0 && e.m & STATUS_UNINITIALIZED && !(e.l & CONFIG_HELD)) return fallback(l);
451
+ if (l.U.size !== 0 && r.m & STATUS_UNINITIALIZED && !(r.l & CONFIG_HELD)) return fallback(l);
441
452
  // Readers under it still unready: the fallback, the tree untouched.
442
453
  // The seam re-derives this pass when they settle.
443
454
  if (prune(l, true) !== 0) return fallback(l);
444
455
  }
445
- let n;
456
+ let o;
446
457
  try {
447
- n = read(i);
458
+ o = read(s);
448
459
  } catch (r) {
449
460
  if (r instanceof NotReadyError ? e === STATUS_PENDING : e === STATUS_ERROR) {
450
461
  if (isCollecting(l)) {
451
- caught(l, i, r);
462
+ caught(l, s, r);
452
463
  return fallback(l);
453
464
  }
454
465
  // Forwarded: a loading boundary showing content keeps it and
455
466
  // holds the frame like any reader (recorded for its `on`).
456
- record(l, i);
467
+ record(l, s);
457
468
  }
458
469
  // A pending passes an error boundary by (A6).
459
470
  throw r;
@@ -471,34 +482,44 @@ function createBoundary(e, r, n, t) {
471
482
  // landing when the frame parks (the run is held with it, as the old
472
483
  // children of a held pass are) — not now: a held swap keeps the
473
484
  // fallback on screen, live, until it commits. A pass back to the
474
- // fallback before then finds it still built and keeps it.
485
+ // fallback before then finds it still built and keeps it. A fallback
486
+ // the screen does not show (a swap staged with a held frame, replaced
487
+ // before it commits) dies now: what it reads holds nothing (S-22 row
488
+ // 3′, #4268).
475
489
  const t = l.ne;
476
- if (t !== null) globalQueue.enqueue(EFFECT_RENDER, () => {
490
+ const drop = () => {
477
491
  if (!l.L && l.ne === t) {
492
+ // A fallback that threw (a rethrow the boundary does not
493
+ // handle) is what an enclosing boundary waits on: it died, and
494
+ // the seam of another round drops it there (#4270).
495
+ if (t.m & STATUS_ERROR) schedule();
478
496
  dispose(t);
479
497
  l.ne = null;
480
498
  }
481
- });
499
+ };
500
+ if (t !== null) l.j.le !== t.le ? drop() : globalQueue.enqueue(EFFECT_RENDER, drop);
482
501
  release(l);
483
502
  // Shown: the slot leaves its reveal order (a later re-arm gates nobody
484
503
  // else; the frontier moves on).
485
- if (l.v !== null) {
504
+ if (revealHooks && l.v !== null) {
486
505
  const e = l.v;
487
506
  l.v = null;
488
507
  revealHooks.P(e);
489
508
  }
490
- return n;
509
+ return o;
491
510
  },
492
511
  // Boundary structure, not a user source: its value is fallback-or-content
493
512
  // and legitimately swaps mid-hydration (resume), so it must never be
494
513
  // frozen by snapshot capture.
495
514
  {
496
515
  name: "value",
497
- le: true
516
+ ue: true
498
517
  });
499
- s.l |= CONFIG_REDERIVE;
500
- l.j = s;
501
- return accessor(s);
518
+ i.l |= CONFIG_REDERIVE;
519
+ // The tree is structure too (the output's pass marks the output).
520
+ s.l |= CONFIG_FRAME;
521
+ l.j = i;
522
+ return accessor(i);
502
523
  }
503
524
 
504
525
  function fallback(e) {
@@ -541,7 +562,7 @@ function fallback(e) {
541
562
 
542
563
  const ERROR_SIGNAL = {
543
564
  ownedWrite: true,
544
- le: true
565
+ ue: true
545
566
  };
546
567
 
547
568
  /**
@@ -70,6 +70,14 @@ import { attrHooks } from "./attribution-hooks.js";
70
70
  * yield until(() => todos.some(t => t.id === saved.id));
71
71
  * ```
72
72
  *
73
+ * The same holds inside an async helper the action yields
74
+ * (`yield (async () => { ... })()`): everything after its first `await` runs
75
+ * outside the transaction. An `await refresh(x)` or `await until(...)` there
76
+ * waits for this action's commit, which waits on the helper, so it hangs
77
+ * whenever `x` re-ran during the action (#4263) and works otherwise. Call
78
+ * `refresh(x)` in the generator once the helper returns; `yield` it only when
79
+ * the action needs the fresh value.
80
+ *
73
81
  * (For the same reason, don't call `flush()` inside an action body — it
74
82
  * cannot reveal the held writes and is refused.)
75
83
  *
@@ -1,4 +1,4 @@
1
- import { STATUS_ERROR, STATUS_PENDING, 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, 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
 
@@ -156,7 +156,7 @@ function releaseSettledDependents(e) {
156
156
  clearPendingSources(e);
157
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).
@@ -208,7 +208,7 @@ function settlePendingSource(e, t = e) {
208
208
  // is its verdict changing: it runs again.
209
209
  return s.l & CONFIG_VERDICT && enqueueSub(s);
210
210
  o.add(s);
211
- s.ie = clock;
211
+ s.ae = clock;
212
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
@@ -251,9 +251,9 @@ 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
  }
@@ -275,9 +275,9 @@ function isThenable(e) {
275
275
  // null, which recompute would read as a flight this body registered itself.
276
276
  // Restore undefined here, so the fix costs nothing for code that never
277
277
  // calls onSupersede.
278
- if (n) r.Se = undefined;
279
- const o = r.ae;
280
- r.ae = o === null ? e : () => {
278
+ if (n) r.Ee = undefined;
279
+ const o = r.Se;
280
+ r.Se = o === null ? e : () => {
281
281
  try {
282
282
  o();
283
283
  } finally {
@@ -301,7 +301,7 @@ r) {
301
301
  });
302
302
  }
303
303
  if (!s && !o) {
304
- if (e.T !== null) e.T.Se = null;
304
+ if (e.T !== null) e.T.Ee = null;
305
305
  // A sync landing is the first real answer for a loadingValue node.
306
306
  e.ce = false;
307
307
  return t;
@@ -311,10 +311,10 @@ r) {
311
311
  // self-registration — runs during a recompute of `el`, which has already
312
312
  // fired _flightTeardown. A future non-recompute registration path must
313
313
  // release it here before overwriting _inFlight.
314
- ext(e).Se = t;
314
+ ext(e).Ee = t;
315
315
  // The question this flight answers (A18 provenance, lanes.ts): the action
316
316
  // whose slice asked it; mainline is always the current question.
317
- e.T.Ee = question || MAINLINE_QUESTION;
317
+ e.T.de = question || MAINLINE_QUESTION;
318
318
  // The run that asked this flight read every input without throwing: an
319
319
  // input still in flight was masked for it (an active override, A17), so
320
320
  // pending state those inputs propagated onto the node earlier does not
@@ -330,7 +330,7 @@ r) {
330
330
  if (attrHooks !== null) attrHooks.flightStart(e, t);
331
331
  let f;
332
332
  const handleError = o => {
333
- if (e.T?.Se !== t) return;
333
+ if (e.T?.Ee !== t) return;
334
334
  // NotReadyError from rejected promises should be treated as pending, not error
335
335
  let s = o instanceof NotReadyError;
336
336
  if (s && e.ce) {
@@ -338,9 +338,9 @@ r) {
338
338
  // serving commit #0 — same parking as recompute's catch for sync
339
339
  // dependency throws. The dead flight is released so the clock-gated
340
340
  // error-retry pull (updateIfNecessary) can also re-ask.
341
- if (e.T !== null) e.T.Se = null;
341
+ if (e.T !== null) e.T.Ee = null;
342
342
  parkLoadingWindow(e, o);
343
- e.ie = clock;
343
+ e.ae = clock;
344
344
  return;
345
345
  }
346
346
  // D-41: a held node's error is staged (`stageError`). A derived store's
@@ -358,7 +358,7 @@ r) {
358
358
  // rejected flight will never settle its self entry, so transfer ownership
359
359
  // after notifyStatus has propagated the replacement source.
360
360
  if (s) settlePendingSource(e);
361
- e.ie = clock;
361
+ e.ae = clock;
362
362
  // A real error settles derivatively-pending dependents (notifyStatus
363
363
  // cleared their pending sources), so stranded lazy ones release here —
364
364
  // the error twin of settlePendingSource's release (#2934).
@@ -366,7 +366,7 @@ r) {
366
366
  r?.(o, s);
367
367
  };
368
368
  const asyncWrite = (r, o) => {
369
- if (e.T?.Se !== t) return;
369
+ if (e.T?.Ee !== t) return;
370
370
  // If the node was dirtied by a newer write (optimistic override or regular),
371
371
  // skip this stale async result — the upcoming flush will recompute the node
372
372
  // with the new value, creating a fresh Promise that supersedes this one.
@@ -385,8 +385,14 @@ r) {
385
385
  return;
386
386
  }
387
387
  // A store derive's landing commits through its setter: unheld, the
388
- // shown error leaves now; held, at the derive's commit.
389
- if (!(e.l & CONFIG_HELD)) e.T.de = undefined;
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
+ }
390
396
  if (s) landStatus(e, true);
391
397
  // Attribution hook: this path landed through the setter (derived
392
398
  // stores, projections), which does not go through setSignal, so the
@@ -416,7 +422,7 @@ r) {
416
422
  // (`_pendingValue` set inside setSignal) is not — commitPendingNode closes
417
423
  // the window when the flush commits it, so no one-frame pulse can leak to
418
424
  // observers between the landing and its commit (#2990).
419
- if (e.ue === NOT_PENDING) {
425
+ if (e.ie === NOT_PENDING) {
420
426
  e.ce = false;
421
427
  // The landing published: the dependency tail the flight's pass left
422
428
  // linked goes now (A30, #3410). A staged landing has not replaced the
@@ -473,7 +479,7 @@ r) {
473
479
  // `onSupersede` teardown this run registered earlier, which then falls
474
480
  // back to the owner-cleanup channel. A returned `from()` is unaffected:
475
481
  // `close` calls its `return()`, which runs its own teardown.
476
- ext(e).ae = close;
482
+ ext(e).Se = close;
477
483
  // Release check before each next pull: an unobserved lazy node must tear
478
484
  // down (its close above runs via disposal, closing the iterator) instead
479
485
  // of pumping the stream forever with zero subscribers (#2935).
@@ -494,11 +500,11 @@ r) {
494
500
  // use the bare form as a promise-free fast path when a value is already
495
501
  // buffered (seroval's deserialized streams do), so a bare result is
496
502
  // assimilated as an already-settled step instead of crashing on `.then`.
497
- const c = o.next();
498
- const E = isThenable(c) ? c : {
499
- then: e => void e(c)
503
+ const E = o.next();
504
+ const c = isThenable(E) ? E : {
505
+ then: e => void e(E)
500
506
  };
501
- E.then(r => {
507
+ c.then(r => {
502
508
  // The sync stash only serves the INITIAL drain (handleAsync's caller
503
509
  // consumes syncValue / throws NotReady from it). A sync-settled step
504
510
  // after an async gap — seroval buffering values between pulls, a
@@ -510,7 +516,7 @@ r) {
510
516
  n = r;
511
517
  l = true;
512
518
  if (r.done) u = true;
513
- } else if (e.T?.Se !== t) {
519
+ } else if (e.T?.Ee !== t) {
514
520
  return;
515
521
  } else if (!r.done) {
516
522
  s = true;
@@ -530,7 +536,7 @@ r) {
530
536
  if (S && i) {
531
537
  r = n;
532
538
  a = true;
533
- } else if (e.T?.Se === t) {
539
+ } else if (e.T?.Ee === t) {
534
540
  u = true;
535
541
  handleError(n);
536
542
  settleAutodispose();
@@ -595,7 +601,7 @@ r) {
595
601
  // compute catch above it: a protocol throw settles as this
596
602
  // flight's error (#4112).
597
603
  try {
598
- 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;
599
605
  asyncWrite(r);
600
606
  } catch (e) {
601
607
  handleError(e);
@@ -637,7 +643,7 @@ r) {
637
643
  if (u !== null) {
638
644
  if (!u) {
639
645
  // Loading window: serve commit #0 instead of suspending.
640
- if (e.ce) return e.Ie;
646
+ if (e.ce) return e.le;
641
647
  throw new NotReadyError(context);
642
648
  }
643
649
  // A sync first yield (or immediate empty completion) is the first real
@@ -697,15 +703,18 @@ function notifyStatus(e, t, n) {
697
703
  // (CARVE 2: the lane assignment went with the engine.)
698
704
  if (t === STATUS_PENDING && r) {
699
705
  // D-41: a re-ask of a shown error keeps it the display (`_shownError`).
700
- if (e.m & STATUS_ERROR) e.T.de = e.T.F;
706
+ if (e.m & STATUS_ERROR) e.T.Te = e.T.F;
701
707
  addPendingSource(e, r);
702
- e.m = STATUS_PENDING | e.m & STATUS_UNINITIALIZED;
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);
703
710
  // Preserve the current source on this propagation so readers park on
704
711
  // the distinct pending source.
705
712
  setPendingError(e, r, n);
706
713
  } else {
707
714
  clearPendingSources(e);
708
- e.m = t | (t !== STATUS_ERROR ? e.m & STATUS_UNINITIALIZED : 0);
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);
709
718
  ext(e).F = n;
710
719
  }
711
720
  // Lanes: a written guess is an optimistic boundary (A17 — "the optimistic
@@ -733,7 +742,7 @@ function notifyStatus(e, t, n) {
733
742
  * (store/projection.ts `wakeFamily`), exactly as a memo's would reach its. */ function propagateStatus(e, t, n) {
734
743
  const r = t === STATUS_PENDING && n instanceof NotReadyError ? n.source : undefined;
735
744
  forEachDependent(e, (o, s) => {
736
- o.ie = clock;
745
+ o.ae = clock;
737
746
  // A pending mark on a kept-tail link re-derives the subscriber instead of
738
747
  // marking it (A30, #3494 review; fuzzer latest-1 #2141; #3519 review).
739
748
  // Past `_depsTail` lie the committed frame's deps, kept by A30 because
@@ -753,7 +762,7 @@ function notifyStatus(e, t, n) {
753
762
  // Inheriting the error would make a probe's reader errored. A boundary's
754
763
  // output (CONFIG_REDERIVE, boundaries.ts) the same: content or fallback
755
764
  // is its pass's call.
756
- if (o.l & (CONFIG_REDERIVE | CONFIG_VERDICT) || t === STATUS_PENDING && s.Te !== o.Ne) {
765
+ if (o.l & (CONFIG_REDERIVE | CONFIG_VERDICT) || t === STATUS_PENDING && s.Ie !== o.Ne) {
757
766
  enqueueSub(o);
758
767
  schedule();
759
768
  return;
@@ -766,7 +775,7 @@ function notifyStatus(e, t, n) {
766
775
  // shape: the unmount it stages makes the observer a zombie whose say is
767
776
  // moot for this transaction, A15 #3463).
768
777
  if (t === STATUS_PENDING && r && o.T?.se?.has(r)) {
769
- if (passLane === null && (o.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && globalQueue.q && !freshHook?.(o, r.ie === clock)) joinFuture(txOf(o));
778
+ if (passLane === null && (o.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && globalQueue.q && !freshHook?.(o, r.ae === clock)) joinFuture(txOf(o));
770
779
  return;
771
780
  }
772
781
  if (t === STATUS_PENDING && r || t !== STATUS_PENDING && (o.T?.F !== n || o.T?.se)) {
@@ -785,6 +794,9 @@ function notifyStatus(e, t, n) {
785
794
  if (t === STATUS_PENDING && (o.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && !o.i) joinFuture(txOf(o));
786
795
  }
787
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);
788
800
  }
789
801
  });
790
802
  }