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

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.js +2494 -271
  2. package/dist/node.cjs +3691 -1595
  3. package/dist/prod/affects.js +13 -12
  4. package/dist/prod/boundaries.js +43 -35
  5. package/dist/prod/core/action.js +3 -3
  6. package/dist/prod/core/async.js +137 -106
  7. package/dist/prod/core/constants.js +55 -1
  8. package/dist/prod/core/core.js +354 -247
  9. package/dist/prod/core/effect.js +47 -50
  10. package/dist/prod/core/error.js +13 -1
  11. package/dist/prod/core/external.js +4 -4
  12. package/dist/prod/core/graph.js +88 -52
  13. package/dist/prod/core/heap.js +38 -38
  14. package/dist/prod/core/lanes.js +34 -34
  15. package/dist/prod/core/optimistic.js +61 -58
  16. package/dist/prod/core/owner.js +38 -38
  17. package/dist/prod/core/scheduler.js +401 -174
  18. package/dist/prod/core/verdict.js +132 -65
  19. package/dist/prod/index.js +7 -3
  20. package/dist/prod/map.js +106 -106
  21. package/dist/prod/signals.js +253 -25
  22. package/dist/prod/store/index.js +2 -0
  23. package/dist/prod/store/next/optimistic.js +314 -121
  24. package/dist/prod/store/next/patch-hooks.js +13 -0
  25. package/dist/prod/store/next/patch.js +614 -0
  26. package/dist/prod/store/next/projection.js +23 -19
  27. package/dist/prod/store/next/reconcile.js +307 -120
  28. package/dist/prod/store/next/store.js +440 -117
  29. package/dist/prod/store/next/target.js +13 -4
  30. package/dist/prod/store/store.js +5 -5
  31. package/dist/types/core/async.d.ts +2 -0
  32. package/dist/types/core/attribution.d.ts +9 -4
  33. package/dist/types/core/constants.d.ts +54 -0
  34. package/dist/types/core/core.d.ts +34 -21
  35. package/dist/types/core/dev.d.ts +8 -0
  36. package/dist/types/core/error.d.ts +9 -0
  37. package/dist/types/core/graph.d.ts +22 -0
  38. package/dist/types/core/index.d.ts +2 -2
  39. package/dist/types/core/scheduler.d.ts +46 -0
  40. package/dist/types/core/types.d.ts +12 -0
  41. package/dist/types/index.d.ts +3 -3
  42. package/dist/types/signals.d.ts +108 -0
  43. package/dist/types/store/index.d.ts +2 -0
  44. package/dist/types/store/next/optimistic.d.ts +13 -10
  45. package/dist/types/store/next/patch-hooks.d.ts +41 -0
  46. package/dist/types/store/next/patch.d.ts +91 -0
  47. package/dist/types/store/next/projection.d.ts +1 -1
  48. package/dist/types/store/next/reconcile.d.ts +14 -0
  49. package/dist/types/store/next/store.d.ts +52 -2
  50. package/dist/types/store/next/target.d.ts +73 -8
  51. package/dist/types-cjs/core/async.d.cts +2 -0
  52. package/dist/types-cjs/core/attribution.d.cts +9 -4
  53. package/dist/types-cjs/core/constants.d.cts +54 -0
  54. package/dist/types-cjs/core/core.d.cts +34 -21
  55. package/dist/types-cjs/core/dev.d.cts +8 -0
  56. package/dist/types-cjs/core/error.d.cts +9 -0
  57. package/dist/types-cjs/core/graph.d.cts +22 -0
  58. package/dist/types-cjs/core/index.d.cts +2 -2
  59. package/dist/types-cjs/core/scheduler.d.cts +46 -0
  60. package/dist/types-cjs/core/types.d.cts +12 -0
  61. package/dist/types-cjs/index.d.cts +3 -3
  62. package/dist/types-cjs/signals.d.cts +108 -0
  63. package/dist/types-cjs/store/index.d.cts +2 -0
  64. package/dist/types-cjs/store/next/optimistic.d.cts +13 -10
  65. package/dist/types-cjs/store/next/patch-hooks.d.cts +41 -0
  66. package/dist/types-cjs/store/next/patch.d.cts +91 -0
  67. package/dist/types-cjs/store/next/projection.d.cts +1 -1
  68. package/dist/types-cjs/store/next/reconcile.d.cts +14 -0
  69. package/dist/types-cjs/store/next/store.d.cts +52 -2
  70. package/dist/types-cjs/store/next/target.d.cts +73 -8
  71. package/package.json +2 -2
@@ -1,6 +1,6 @@
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";
1
+ import { STATUS_UNINITIALIZED, CONFIG_CHILD_COMPANIONS, STATUS_ERROR, STATUS_PENDING, REACTIVE_DIRTY, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING, CONFIG_AUTHORITATIVE_OBSERVED, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE, REACTIVE_DISPOSED } from "./constants.js";
2
2
 
3
- import { untrack, ext, setSignal, context } from "./core.js";
3
+ import { untrack, ext, statusNotifierOf, setSignal, context } from "./core.js";
4
4
 
5
5
  import "./invariants.js";
6
6
 
@@ -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
- ext(e).ue = true;
48
+ ext(e).fe = 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
@@ -70,10 +70,10 @@ function setPendingError(e, n, t) {
70
70
  }
71
71
 
72
72
  function forEachDependent(e, n) {
73
- for (let t = e.u; t !== null; t = t.fe) n(t.ae, t);
73
+ for (let t = e.u; t !== null; t = t.ae) n(t.ce, t);
74
74
  // `?? null`: affects() marks route plain signals (no `_child` slot) through here.
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);
75
+ for (let t = e.o?.i ?? null; t !== null; t = t.Se) {
76
+ for (let e = t.u; e !== null; e = e.ae) n(e.ce, 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.Se && e.T & CONFIG_AUTO_DISPOSE && !e.u && !(e.ie & REACTIVE_ZOMBIE) && !(e.S & STATUS_PENDING) && unobserved(e);
91
+ e.oe && 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
@@ -145,35 +145,35 @@ function settlePendingSource(e) {
145
145
  const r = new Set;
146
146
  // Companion updates no-op without the verdict layer (null hook).
147
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;
148
+ const settle = i => {
149
+ if (r.has(i) || !removePendingSource(i, e)) return;
150
+ r.add(i);
151
+ i.Te = clock;
152
+ const l = i.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 i = l.S & STATUS_ERROR;
159
- if (u) {
160
- if (!i) setPendingError(l, u);
161
- o !== null && o(l);
158
+ const s = i.S & STATUS_ERROR;
159
+ if (l) {
160
+ if (!s) setPendingError(i, l);
161
+ o?.(i);
162
162
  } else {
163
- l.S &= ~STATUS_PENDING;
164
- if (!i) setPendingError(l);
165
- o !== null && o(l);
166
- if (l.o?.ue) {
167
- enqueueSub(l);
163
+ i.S &= ~STATUS_PENDING;
164
+ if (!s) setPendingError(i);
165
+ o?.(i);
166
+ if (i.o?.fe) {
167
+ enqueueSub(i);
168
168
  n = true;
169
169
  }
170
- if (l.o !== null) l.o.ue = false;
170
+ if (i.o !== null) i.o.fe = 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 (!l.u && l.T & CONFIG_AUTO_DISPOSE) (t ??= []).push(l);
174
+ if (!i.u && i.T & CONFIG_AUTO_DISPOSE) (t ??= []).push(i);
175
175
  }
176
- forEachDependent(l, settle);
176
+ forEachDependent(i, settle);
177
177
  };
178
178
  forEachDependent(e, settle);
179
179
  // Release before the flush schedule below: unobserved() pulls the node back
@@ -187,6 +187,14 @@ function isThenable(e) {
187
187
  return e != null && typeof e === "object" && typeof e.then === "function";
188
188
  }
189
189
 
190
+ /** Fire and clear a node's iterator-flight cancellation hook (#3122). */ function releaseFlightTeardown(e) {
191
+ const n = e.o?.Ee;
192
+ if (n != null) {
193
+ e.o.Ee = null;
194
+ n();
195
+ }
196
+ }
197
+
190
198
  function handleAsync(e, n, t) {
191
199
  let r = false;
192
200
  let o = false;
@@ -197,13 +205,18 @@ function handleAsync(e, n, t) {
197
205
  });
198
206
  }
199
207
  if (!o && !r) {
200
- if (e.o !== null) e.o.Ee = null;
208
+ if (e.o !== null) e.o.Ie = null;
201
209
  // A sync landing is the first real answer for a loadingValue node.
202
- e.Ie = false;
210
+ e.Ne = false;
203
211
  return n;
204
212
  }
205
- ext(e).Ee = n;
206
- let l;
213
+ // Flight replacement relies on recompute's supersede release for iterator
214
+ // teardown (#3122): every handleAsync call — including the projection
215
+ // self-registration — runs during a recompute of `el`, which has already
216
+ // fired _flightTeardown. A future non-recompute registration path must
217
+ // release it here before overwriting _inFlight.
218
+ ext(e).Ie = n;
219
+ let i;
207
220
  // Settle-time transition re-entry. The loading rail is invisible to
208
221
  // transactions (#2933): a boundary-caught first load never registers as an
209
222
  // async reporter, so its settle — the boundary's fallback -> content
@@ -216,24 +229,24 @@ function handleAsync(e, n, t) {
216
229
  // are the transaction's reveal machinery and always re-enter.
217
230
  const settleTransition = () => {
218
231
  const n = resolveTransition(e);
219
- if (n && e.S & STATUS_UNINITIALIZED && !currentTransition(n).Ne.has(e)) {
232
+ if (n && e.S & STATUS_UNINITIALIZED && !currentTransition(n)._e.has(e)) {
220
233
  // Drop the stale stamp too: the plain settle write (setSignal) and the
221
234
  // stash-path restamp both re-enter the transaction through it.
222
- e._e = null;
235
+ e.Ae = null;
223
236
  return;
224
237
  }
225
238
  globalQueue.initTransition(n);
226
239
  };
227
240
  const handleError = t => {
228
- if (e.o?.Ee !== n) return;
241
+ if (e.o?.Ie !== n) return;
229
242
  // NotReadyError from rejected promises should be treated as pending, not error
230
243
  let r = t instanceof NotReadyError;
231
- if (r && e.Ie) {
244
+ if (r && e.Ne) {
232
245
  // Loading window: the flight died waiting on an unready source. Keep
233
246
  // serving commit #0 — same parking as recompute's catch for sync
234
247
  // dependency throws. The dead flight is released so the clock-gated
235
248
  // error-retry pull (updateIfNecessary) can also re-ask.
236
- if (e.o !== null) e.o.Ee = null;
249
+ if (e.o !== null) e.o.Ie = null;
237
250
  parkLoadingWindow(e, t);
238
251
  e.Te = clock;
239
252
  return;
@@ -247,21 +260,21 @@ function handleAsync(e, n, t) {
247
260
  if (!r) releaseSettledDependents(e);
248
261
  };
249
262
  const asyncWrite = (r, o) => {
250
- if (e.o?.Ee !== n) return;
263
+ if (e.o?.Ie !== n) return;
251
264
  // If the node was dirtied by a newer write (optimistic override or regular),
252
265
  // skip this stale async result — the upcoming flush will recompute the node
253
266
  // with the new value, creating a fresh Promise that supersedes this one.
254
267
  if (e.ie & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY)) return;
255
268
  settleTransition();
256
- const l = !!(e.S & STATUS_UNINITIALIZED);
269
+ const i = !!(e.S & STATUS_UNINITIALIZED);
257
270
  trimStaleDeps(e);
258
271
  clearStatus(e);
259
- const u = resolveLane(e);
260
- if (u) u.Ae.delete(e);
272
+ const l = resolveLane(e);
273
+ if (l) l.De.delete(e);
261
274
  if (t) {
262
275
  t(r);
263
- if (l) clearStatus(e, true);
264
- } else if (e.o?.De !== undefined) {
276
+ if (i) clearStatus(e, true);
277
+ } else if (e.o?.Oe !== undefined) {
265
278
  // Optimistic node — resting OR covered by an active override — holds
266
279
  // through the shared pending-node path, exactly like a plain async memo,
267
280
  // so the commit clears STATUS_UNINITIALIZED (#2806) and elevation to
@@ -280,24 +293,35 @@ function handleAsync(e, n, t) {
280
293
  // only notified when the hold is visible to them: under an active
281
294
  // override every reader sees the override (A17), so waking subs would
282
295
  // re-show an unchanged view — the revert is the notification point.
283
- GlobalQueue.Oe !== null && GlobalQueue.Oe(e, r);
296
+ GlobalQueue.Re?.(e, r);
284
297
  if (!hasActiveOverride(e)) {
285
298
  insertSubs(e);
299
+ } else if (e.T & CONFIG_AUTHORITATIVE_OBSERVED) {
300
+ // A17 silence is stated over ordinary readers; an authoritative-view
301
+ // reader (until()'s predicate) observed this node PAST its override
302
+ // and is waiting for exactly this staged truth. Without the wake the
303
+ // hold deadlocks: the landing waits on the transaction, the
304
+ // transaction on the action, the action on an until() that was never
305
+ // re-notified (#3164). Same selective wake as the equal-landing
306
+ // branch in recompute(). Optional call: the bit implies the
307
+ // optimistic engine WAS consulted, but the hook only installs with
308
+ // it — a bare-core build must not crash here.
309
+ GlobalQueue.Ue?.(e);
286
310
  }
287
311
  e.Te = clock;
288
- } else if (u) {
312
+ } else if (l) {
289
313
  // Route through lane's effect queue for independent flushing
290
- const n = e.Re;
314
+ const n = e.he;
291
315
  const t = e.be;
292
- const o = e.Ue;
316
+ const o = e.ge;
293
317
  try {
294
- if (!n && l || !o || !o(r, t)) {
318
+ if (!n && i || !o || !o(r, t)) {
295
319
  e.be = r;
296
320
  e.Te = clock;
297
321
  // The latest() shadow write gives latest() effects independent lanes; the
298
322
  // _pendingSignal update is a no-op repeat of the clearStatus() call above
299
323
  // (computePendingState doesn't read _value).
300
- GlobalQueue.Oe !== null && GlobalQueue.Oe(e, r);
324
+ GlobalQueue.Re?.(e, r);
301
325
  insertSubs(e, true);
302
326
  }
303
327
  } catch (n) {
@@ -322,7 +346,7 @@ function handleAsync(e, n, t) {
322
346
  // held-value branch is window-gated, and commitPendingNode closes the
323
347
  // window when the hold commits, so no one-frame isPending pulse can leak
324
348
  // to live observers between the landing and its commit (#2990).
325
- if (e.Pe === NOT_PENDING) e.Ie = false;
349
+ if (e.Pe === NOT_PENDING) e.Ne = false;
326
350
  settlePendingSource(e);
327
351
  schedule();
328
352
  flush();
@@ -355,18 +379,23 @@ function handleAsync(e, n, t) {
355
379
  // meaningful only in the live posture.
356
380
  const consumeIterator = (t, r) => {
357
381
  const o = t[Symbol.asyncIterator]();
358
- let u = false;
359
- let i = false;
360
- let s = !r;
382
+ let l = false;
383
+ let s = false;
384
+ let u = !r;
361
385
  const close = () => {
362
- if (i) return;
363
- i = true;
386
+ if (s) return;
387
+ s = true;
364
388
  try {
365
389
  const e = o.return?.();
366
390
  if (isThenable(e)) e.then(undefined, () => {});
367
391
  } catch {}
368
392
  };
369
393
  r ? r(close) : cleanup(close);
394
+ // Flight-identity cancellation (#3122): the registration above is the
395
+ // owner-death backstop, but its disposal list can be zombie-deferred
396
+ // until the SUPERSEDING flight settles. The teardown slot fires at the
397
+ // _inFlight release sites so supersede stops this stream immediately.
398
+ ext(e).Ee = close;
370
399
  // Release check before each next pull: an unobserved lazy node must tear
371
400
  // down (its close above runs via disposal, closing the iterator) instead
372
401
  // of pumping the stream forever with zero subscribers (#2935).
@@ -392,18 +421,18 @@ function handleAsync(e, n, t) {
392
421
  // stash: it must write through the async path or the value is
393
422
  // silently dropped. (The deferred posture never has a caller, so
394
423
  // initialRead starts false there and everything writes through.)
395
- if (c && s) {
424
+ if (c && u) {
396
425
  t = r;
397
426
  f = true;
398
- if (r.done) i = true;
399
- } else if (e.o?.Ee !== n) {
427
+ if (r.done) s = true;
428
+ } else if (e.o?.Ie !== n) {
400
429
  return;
401
430
  } else if (!r.done) {
402
- u = true;
431
+ l = true;
403
432
  asyncWrite(r.value, iterateOrRelease);
404
433
  } else {
405
- i = true;
406
- if (u) {
434
+ s = true;
435
+ if (l) {
407
436
  schedule();
408
437
  flush();
409
438
  } else {
@@ -413,11 +442,11 @@ function handleAsync(e, n, t) {
413
442
  settleAutodispose();
414
443
  }
415
444
  }, t => {
416
- if (c && s) {
445
+ if (c && u) {
417
446
  r = t;
418
447
  a = true;
419
- } else if (e.o?.Ee === n) {
420
- i = true;
448
+ } else if (e.o?.Ie === n) {
449
+ s = true;
421
450
  handleError(t);
422
451
  settleAutodispose();
423
452
  }
@@ -425,27 +454,27 @@ function handleAsync(e, n, t) {
425
454
  c = false;
426
455
  if (a) {
427
456
  // Match the promise branch, but only rethrow during the initial read.
428
- i = true;
457
+ s = true;
429
458
  handleError(r);
430
- if (s) throw r;
459
+ if (u) throw r;
431
460
  return true;
432
461
  }
433
462
  if (f && !t.done) {
434
- l = t.value;
435
- u = true;
463
+ i = t.value;
464
+ l = true;
436
465
  return iterate();
437
466
  }
438
467
  return f && t.done;
439
468
  };
440
469
  const f = iterate();
441
470
  // Later iterate() calls run from asyncWrite, where rethrowing would be unhandled.
442
- s = false;
443
- return u || f;
471
+ u = false;
472
+ return l || f;
444
473
  };
445
474
  // Landed-synchronously verdict for a LIVE iterator drain; null when no live
446
475
  // drain ran (plain promise flight, or a deferred flatten). Drives the
447
476
  // shared NotReady/loading tail below.
448
- let u = null;
477
+ let l = null;
449
478
  // Flatten one async level: a thenable that RESOLVES to an AsyncIterable —
450
479
  // the shape every async stub returning a stream produces — consumes as the
451
480
  // stream itself rather than settling on the iterable object. One level
@@ -460,11 +489,11 @@ function handleAsync(e, n, t) {
460
489
  }
461
490
  if (!t) return false;
462
491
  const r = consumeIterator(e, n);
463
- if (!n) u = r;
492
+ if (!n) l = r;
464
493
  return true;
465
494
  };
466
495
  if (o) {
467
- let t = false, r = false, o, u = true;
496
+ let t = false, r = false, o, l = true;
468
497
  // Close registration for the flattening path. Consumption starts in a
469
498
  // microtask where the ambient owner is gone (or worse, someone else's),
470
499
  // so `cleanup()` can't be used — the close targets el's disposal list
@@ -476,18 +505,18 @@ function handleAsync(e, n, t) {
476
505
  // becomes disposal-bearing, which is exactly the class a directly
477
506
  // returned iterable already occupies.
478
507
  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 ];
508
+ if (!e.pe) e.pe = n; else if (Array.isArray(e.pe)) e.pe.push(n); else e.pe = [ e.pe, n ];
480
509
  };
481
510
  n.then(r => {
482
- if (u) {
483
- l = r;
511
+ if (l) {
512
+ i = r;
484
513
  t = true;
485
- } else if (e.o?.Ee === n && !(e.ie & REACTIVE_DISPOSED) && flattenIfIterable(r, registerDeferredClose)) ; else {
514
+ } else if (e.o?.Ie === n && !(e.ie & REACTIVE_DISPOSED) && flattenIfIterable(r, registerDeferredClose)) ; else {
486
515
  asyncWrite(r);
487
516
  settleAutodispose();
488
517
  }
489
518
  }, e => {
490
- if (u) {
519
+ if (l) {
491
520
  o = e;
492
521
  r = true;
493
522
  } else {
@@ -495,7 +524,7 @@ function handleAsync(e, n, t) {
495
524
  settleAutodispose();
496
525
  }
497
526
  });
498
- u = false;
527
+ l = false;
499
528
  if (r) {
500
529
  // Settle through the same status path an async rejection uses, then
501
530
  // unwind the in-progress synchronous read so the errored node isn't
@@ -507,103 +536,105 @@ function handleAsync(e, n, t) {
507
536
  // is opened — first-flight work on a loadingValue node is loading-class
508
537
  // (invisible to boundaries and transitions); the flight itself is
509
538
  // already registered in _inFlight and lands through asyncWrite.
510
- if (e.Ie) return e.be;
539
+ if (e.Ne) return e.be;
511
540
  globalQueue.initTransition(resolveTransition(e));
512
541
  throw new NotReadyError(context);
513
- } else if (!flattenIfIterable(l)) {
542
+ } else if (!flattenIfIterable(i)) {
514
543
  // Synchronously-resolved promise: the first real answer landed.
515
- e.Ie = false;
544
+ e.Ne = false;
516
545
  }
517
546
  // A sync-resolved promise holding an AsyncIterable flattened LIVE (we
518
547
  // are still inside the synchronous read): full initial-drain semantics
519
548
  // apply and the shared tail below settles the verdict.
520
549
  }
521
550
  if (r) flattenIfIterable(n);
522
- if (u !== null) {
523
- if (!u) {
551
+ if (l !== null) {
552
+ if (!l) {
524
553
  // Loading window: serve commit #0 (see the promise branch above).
525
- if (e.Ie) return e.be;
554
+ if (e.Ne) return e.be;
526
555
  globalQueue.initTransition(resolveTransition(e));
527
556
  throw new NotReadyError(context);
528
557
  }
529
558
  // A sync first yield (or immediate empty completion) is the first real
530
559
  // answer; async yields clear inside asyncWrite.
531
- e.Ie = false;
560
+ e.Ne = false;
532
561
  }
533
- return l;
562
+ return i;
534
563
  }
535
564
 
536
565
  function clearStatus(e, n = false) {
537
566
  if (e.o?.le) clearPendingSources(e);
538
- if (e.o?.ue) if (e.o !== null) e.o.ue = false;
567
+ if (e.o?.fe) if (e.o !== null) e.o.fe = false;
539
568
  // The pending window is over; its quiet classification dies with it.
540
569
  // (Unconditional: _reask is baked into the node literals, so this is a
541
570
  // plain store to an existing slot — no shape change.)
542
- if (e.o !== null) e.o.pe = false;
571
+ if (e.o !== null) e.o.Ge = false;
543
572
  e.S = n ? 0 : e.S & STATUS_UNINITIALIZED;
544
573
  if (e.o?._) setPendingError(e);
545
574
  // Update pending signal for isPending() reactivity (companions only exist
546
575
  // once the verdict layer created them, which installs the hooks).
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);
576
+ if (e.o?.ye || e.o?.Ce) GlobalQueue.de(e);
577
+ if (e.o?.i && e.T & CONFIG_CHILD_COMPANIONS && GlobalQueue.me !== null) GlobalQueue.me(e);
578
+ const t = statusNotifierOf(e);
579
+ if (t) t.call(e);
550
580
  }
551
581
 
552
582
  function notifyStatus(e, n, t, r, o) {
553
583
  // Wrap regular errors to track source node
554
584
  if (n === STATUS_ERROR && !(t instanceof StatusError) && !(t instanceof NotReadyError)) t = new StatusError(e, t);
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);
585
+ const i = n === STATUS_PENDING && t instanceof NotReadyError ? t.source : undefined;
586
+ const l = i === e;
587
+ const s = n === STATUS_PENDING && e.o?.Oe !== undefined && !l;
588
+ const u = s && hasActiveOverride(e);
559
589
  if (!r) {
560
- if (n === STATUS_PENDING && l) {
561
- addPendingSource(e, l);
590
+ if (n === STATUS_PENDING && i) {
591
+ addPendingSource(e, i);
562
592
  e.S = STATUS_PENDING | e.S & STATUS_UNINITIALIZED;
563
593
  // Preserve the current source on this propagation so render-effect notification
564
594
  // can register every distinct pending source with the transition.
565
- setPendingError(e, l, t);
595
+ setPendingError(e, i, t);
566
596
  } else {
567
597
  clearPendingSources(e);
568
598
  e.S = n | (n !== STATUS_ERROR ? e.S & STATUS_UNINITIALIZED : 0);
569
599
  ext(e)._ = t;
570
600
  }
571
- GlobalQueue.de !== null && GlobalQueue.de(e);
572
- if (e.o?.l && e.T & CONFIG_CHILD_COMPANIONS && GlobalQueue.ye !== null) GlobalQueue.ye(e);
601
+ GlobalQueue.de?.(e);
602
+ if (e.o?.i && e.T & CONFIG_CHILD_COMPANIONS && GlobalQueue.me !== null) GlobalQueue.me(e);
573
603
  }
574
604
  if (o && !r) {
575
605
  assignOrMergeLane(e, o);
576
606
  }
577
- const f = r || s;
578
- const a = r || i ? undefined : o;
579
- if (e.o?.A) {
607
+ const f = r || u;
608
+ const a = r || s ? undefined : o;
609
+ const c = statusNotifierOf(e);
610
+ if (c) {
580
611
  if (r && n === STATUS_PENDING) {
581
612
  return;
582
613
  }
583
614
  if (f) {
584
- e.o.A.call(e, n, t);
615
+ c.call(e, n, t);
585
616
  } else {
586
- e.o.A.call(e);
617
+ c.call(e);
587
618
  }
588
619
  return;
589
620
  }
590
621
  forEachDependent(e, (e, r) => {
591
622
  e.Te = clock;
592
- if (n === STATUS_PENDING && l && !e.o?.le?.has(l) || n !== STATUS_PENDING && (e.o?._ !== t || e.o?.le)) {
623
+ if (n === STATUS_PENDING && i && !e.o?.le?.has(i) || n !== STATUS_PENDING && (e.o?._ !== t || e.o?.le)) {
593
624
  // A pending-observer link is the subscription an `isPending` read created.
594
625
  // It exists so the observer re-runs when the source settles, but it must
595
626
  // not carry a real (non-NotReadyError) error — the synchronous `isPending`
596
627
  // read swallows those, and the async path must match. Re-run the observer
597
628
  // so `isPending` re-evaluates (to not-pending) instead of forwarding.
598
- if (r.me && n !== STATUS_PENDING && !(t instanceof NotReadyError)) {
629
+ if (r.ve && n !== STATUS_PENDING && !(t instanceof NotReadyError)) {
599
630
  enqueueSub(e);
600
631
  schedule();
601
632
  return;
602
633
  }
603
- if (!f && !e._e) queuePendingNode(e);
634
+ if (!f && !e.Ae) queuePendingNode(e);
604
635
  notifyStatus(e, n, t, f, a);
605
636
  }
606
637
  });
607
638
  }
608
639
 
609
- export { addPendingSource, clearStatus, forEachDependent, handleAsync, isThenable, notifyStatus, parkLoadingWindow, releaseSettledDependents, setPendingError, settleErroredDependents, settlePendingSource };
640
+ export { addPendingSource, clearStatus, forEachDependent, handleAsync, isThenable, notifyStatus, parkLoadingWindow, releaseFlightTeardown, releaseSettledDependents, setPendingError, settleErroredDependents, settlePendingSource };
@@ -83,6 +83,60 @@ const CONFIG_HAS_LANE = 1 << 10;
83
83
  * moved into the cold extension (§12), and an unconditional `_x` deref per
84
84
  * marked node measurably taxed the propagation hot path (diamond -22%). */ const CONFIG_FW_CHILDREN = 1 << 12;
85
85
 
86
+ /** Authoritative-view reader (`until()`): while this node computes, reads
87
+ * dodge active optimistic OVERRIDES only — the predicate must observe
88
+ * arriving truth, never the caller's own tentative writes (which would
89
+ * trivially satisfy it). Everything else reads normally, INCLUDING
90
+ * transition-staged `_pendingValue`: staged data is authoritative (optimism
91
+ * lives only in override slots), and a hold that refused staged reads would
92
+ * deadlock on data the open transaction itself is holding (a refresh the
93
+ * action issued lands staged and cannot commit until the hold releases).
94
+ * read() checks the bit on the reading computation (`context`) directly — no
95
+ * ambient flag — so a shared computed the predicate pulls recomputes as
96
+ * itself (no bit) under the normal view, and its cache never forks. */ const CONFIG_AUTHORITATIVE_READ = 1 << 13;
97
+
98
+ /** Sticky mark: an authoritative-view reader read this node PAST an active
99
+ * override. The ack shape — an authoritative arrival EQUAL to the override —
100
+ * rides paths that are deliberately silent under A17 (every ordinary reader
101
+ * sees the override, so an equal landing changes nothing for them). A marked
102
+ * node notifies those readers on such paths anyway, so the landed truth is
103
+ * seen without re-firing ordinary subscribers. Never cleared — only nodes an
104
+ * until() predicate observed mid-override pay. */ const CONFIG_AUTHORITATIVE_OBSERVED = 1 << 14;
105
+
106
+ /** Promise-delivery effect (resolve()/until()): commits its computed value
107
+ * directly even when recomputing under its own held transition. These
108
+ * effects deliver applies on a microtask (#2930) instead of the stashed
109
+ * effect queues, so the value must ride the same immediate schedule — a
110
+ * staged value with an immediate apply delivers stale state (resolve) or
111
+ * deadlocks the hold (until). Safe because the node is a private leaf: no
112
+ * subscriber reads an effect's value, only its own apply does. */ const CONFIG_DIRECT_COMMIT = 1 << 15;
113
+
114
+ /** Fresh-pull reader (awaitable `refresh()`'s waiter effect): a read of a
115
+ * dirty source recomputes it inline even when the height gate defers to the
116
+ * flush. Closes the same-flush ordering race where a waiter created
117
+ * alongside a refresh() mark read the PRE-re-ask value as settled and
118
+ * delivered stale; with the pull, the waiter either parks on the re-ask's
119
+ * pending window (async — woken by the settle walk, which runs on every
120
+ * landing including equal-value ones) or serves its sync answer. resolve()
121
+ * deliberately keeps that race — its contract is "first settled value"
122
+ * (#2930), not "next quiescent state". */ const CONFIG_FRESH_READ = 1 << 16;
123
+
124
+ /** HELD truth (#3164): this node's staged `_pendingValue` is confirming
125
+ * truth riding a transaction that retains optimism, revealed only at that
126
+ * transaction's settle. Two arming sites, one meaning: the store fold
127
+ * (a landing staged into the retaining transaction) and until()'s
128
+ * flip-entanglement (a foreign carrier's staged write, stolen when it
129
+ * flipped the awaited predicate truthy). Until the reveal, ordinary
130
+ * readers — lane and speculative recomputes included — keep committed:
131
+ * the staging notified subscribers as a plain write, so without the mask
132
+ * a mid-hold recompute composes live optimism with the confirming truth,
133
+ * a frame no timeline contains (GabbeV's union tear). Authoritative
134
+ * readers (until()'s predicate) and latest() tunnel through — the
135
+ * exemption that keeps holds deadlock-free. Override-covered nodes never
136
+ * arm: the override is their display and its revert their notification
137
+ * (A17). Cleared at commit (the commit IS the reveal); subscribers masked
138
+ * during the hold are woken by finalizePureQueue's post-revert pass. */ const CONFIG_HELD_TRUTH = 1 << 17;
139
+
86
140
  const STATUS_PENDING = 1 << 0;
87
141
 
88
142
  const STATUS_ERROR = 1 << 1;
@@ -127,4 +181,4 @@ const defaultContext = {};
127
181
  * @internal
128
182
  */ const $REFRESH = Symbol("refresh");
129
183
 
130
- export { $REFRESH, CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, CONFIG_CHILD_COMPANIONS, CONFIG_FW_CHILDREN, CONFIG_HAS_COMPANIONS, CONFIG_HAS_LANE, CONFIG_HAS_SNAPSHOT, CONFIG_IN_SNAPSHOT_SCOPE, CONFIG_NO_SNAPSHOT, CONFIG_OPTIMISTIC, CONFIG_OWNED_WRITE, CONFIG_SYNC, CONFIG_TRANSPARENT, EFFECT_RENDER, EFFECT_TRACKED, EFFECT_USER, NOT_PENDING, NO_SNAPSHOT, OVERRIDE_UNDEFINED, REACTIVE_CHECK, REACTIVE_DIRTY, REACTIVE_DISPOSED, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, REACTIVE_LAZY, REACTIVE_MANUAL_WRITE, REACTIVE_MISSED_WAKE, REACTIVE_NONE, REACTIVE_OPTIMISTIC_DIRTY, REACTIVE_REASK, REACTIVE_RECOMPUTING_DEPS, REACTIVE_SNAPSHOT_STALE, REACTIVE_ZOMBIE, STATUS_ERROR, STATUS_PENDING, STATUS_UNINITIALIZED, STORE_SNAPSHOT_PROPS, SUPPORTS_PROXY, defaultContext, unwrapOverride };
184
+ export { $REFRESH, CONFIG_AUTHORITATIVE_OBSERVED, CONFIG_AUTHORITATIVE_READ, CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, CONFIG_CHILD_COMPANIONS, CONFIG_DIRECT_COMMIT, CONFIG_FRESH_READ, CONFIG_FW_CHILDREN, CONFIG_HAS_COMPANIONS, CONFIG_HAS_LANE, CONFIG_HAS_SNAPSHOT, CONFIG_HELD_TRUTH, CONFIG_IN_SNAPSHOT_SCOPE, CONFIG_NO_SNAPSHOT, CONFIG_OPTIMISTIC, CONFIG_OWNED_WRITE, CONFIG_SYNC, CONFIG_TRANSPARENT, EFFECT_RENDER, EFFECT_TRACKED, EFFECT_USER, NOT_PENDING, NO_SNAPSHOT, OVERRIDE_UNDEFINED, REACTIVE_CHECK, REACTIVE_DIRTY, REACTIVE_DISPOSED, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, REACTIVE_LAZY, REACTIVE_MANUAL_WRITE, REACTIVE_MISSED_WAKE, REACTIVE_NONE, REACTIVE_OPTIMISTIC_DIRTY, REACTIVE_REASK, REACTIVE_RECOMPUTING_DEPS, REACTIVE_SNAPSHOT_STALE, REACTIVE_ZOMBIE, STATUS_ERROR, STATUS_PENDING, STATUS_UNINITIALIZED, STORE_SNAPSHOT_PROPS, SUPPORTS_PROXY, defaultContext, unwrapOverride };