@solidjs/signals 2.0.0-rc.1 → 2.0.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/dev.js +1661 -361
  2. package/dist/node.cjs +1939 -1331
  3. package/dist/prod/affects.js +16 -16
  4. package/dist/prod/boundaries.js +90 -90
  5. package/dist/prod/core/action.js +3 -3
  6. package/dist/prod/core/async.js +74 -72
  7. package/dist/prod/core/constants.js +39 -1
  8. package/dist/prod/core/core.js +332 -224
  9. package/dist/prod/core/effect.js +56 -56
  10. package/dist/prod/core/external.js +4 -4
  11. package/dist/prod/core/graph.js +65 -54
  12. package/dist/prod/core/heap.js +52 -44
  13. package/dist/prod/core/invariants.js +3 -2
  14. package/dist/prod/core/lanes.js +41 -34
  15. package/dist/prod/core/optimistic.js +63 -60
  16. package/dist/prod/core/owner.js +97 -96
  17. package/dist/prod/core/scheduler.js +243 -194
  18. package/dist/prod/core/verdict.js +184 -77
  19. package/dist/prod/map.js +104 -104
  20. package/dist/prod/signals.js +1 -1
  21. package/dist/prod/store/next/optimistic.js +31 -23
  22. package/dist/prod/store/next/projection.js +3 -3
  23. package/dist/prod/store/next/reconcile.js +78 -74
  24. package/dist/prod/store/next/store.js +357 -90
  25. package/dist/prod/store/store.js +7 -7
  26. package/dist/types/core/attribution-hooks.d.ts +52 -0
  27. package/dist/types/core/attribution.d.ts +186 -0
  28. package/dist/types/core/constants.d.ts +26 -0
  29. package/dist/types/core/core.d.ts +8 -1
  30. package/dist/types/core/dev.d.ts +20 -3
  31. package/dist/types/core/graph.d.ts +1 -0
  32. package/dist/types/core/lanes.d.ts +4 -16
  33. package/dist/types/core/scheduler.d.ts +8 -0
  34. package/dist/types/core/types.d.ts +85 -41
  35. package/dist/types/store/next/projection.d.ts +0 -16
  36. package/dist/types/store/next/store.d.ts +6 -0
  37. package/dist/types/store/next/target.d.ts +18 -0
  38. package/dist/types-cjs/core/attribution-hooks.d.cts +52 -0
  39. package/dist/types-cjs/core/attribution.d.cts +186 -0
  40. package/dist/types-cjs/core/constants.d.cts +26 -0
  41. package/dist/types-cjs/core/core.d.cts +8 -1
  42. package/dist/types-cjs/core/dev.d.cts +20 -3
  43. package/dist/types-cjs/core/graph.d.cts +1 -0
  44. package/dist/types-cjs/core/lanes.d.cts +4 -16
  45. package/dist/types-cjs/core/scheduler.d.cts +8 -0
  46. package/dist/types-cjs/core/types.d.cts +85 -41
  47. package/dist/types-cjs/store/next/projection.d.cts +0 -16
  48. package/dist/types-cjs/store/next/store.d.cts +6 -0
  49. package/dist/types-cjs/store/next/target.d.cts +18 -0
  50. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
- import { STATUS_UNINITIALIZED, 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_AUTO_DISPOSE, REACTIVE_ZOMBIE, REACTIVE_DISPOSED } from "./constants.js";
2
2
 
3
- import { untrack, setSignal, context } from "./core.js";
3
+ import { untrack, ext, setSignal, context } from "./core.js";
4
4
 
5
5
  import "./invariants.js";
6
6
 
@@ -22,20 +22,20 @@ import { GlobalQueue, schedule, flush, queuePendingNode, insertSubs, clock, curr
22
22
  // overlapping source landed beside the Set and removePendingSource refused
23
23
  // to clear it, stranding the Set members' pending forever (#2893).
24
24
  function addPendingSource(e, n) {
25
- if (e.oe?.has(n)) return false;
26
- (e.oe ??= new Set).add(n);
25
+ if (e.o?.le?.has(n)) return false;
26
+ (ext(e).le ??= new Set).add(n);
27
27
  return true;
28
28
  }
29
29
 
30
30
  function removePendingSource(e, n) {
31
- if (!e.oe?.delete(n)) return false;
32
- if (e.oe.size === 0) e.oe = undefined;
31
+ if (!e.o?.le?.delete(n)) return false;
32
+ if (e.o?.le.size === 0) if (e.o !== null) e.o.le = undefined;
33
33
  return true;
34
34
  }
35
35
 
36
36
  function clearPendingSources(e) {
37
- e.oe?.clear();
38
- e.oe = undefined;
37
+ e.o?.le?.clear();
38
+ if (e.o !== null) e.o.le = undefined;
39
39
  }
40
40
 
41
41
  /**
@@ -45,7 +45,7 @@ function clearPendingSources(e) {
45
45
  * with NO read-visible pending status, no downstream propagation, no
46
46
  * transition, no lane registration. Commit #0 keeps serving.
47
47
  */ function parkLoadingWindow(e, n) {
48
- e.le = true;
48
+ ext(e).ue = true;
49
49
  if (n.source) addPendingSource(e, n.source);
50
50
  // A settled error is the node's answer ("the error stays the answer until
51
51
  // this retry can actually run") — the park must not replace it: reads
@@ -56,24 +56,24 @@ function clearPendingSources(e) {
56
56
 
57
57
  function setPendingError(e, n, t) {
58
58
  if (!n) {
59
- e._ = null;
59
+ if (e.o !== null) e.o._ = null;
60
60
  return;
61
61
  }
62
62
  if (t instanceof NotReadyError && t.source === n) {
63
- e._ = t;
63
+ ext(e)._ = t;
64
64
  return;
65
65
  }
66
- const r = e._;
66
+ const r = e.o?._;
67
67
  if (!(r instanceof NotReadyError) || r.source !== n) {
68
- e._ = new NotReadyError(n);
68
+ ext(e)._ = new NotReadyError(n);
69
69
  }
70
70
  }
71
71
 
72
72
  function forEachDependent(e, n) {
73
- for (let t = e.o; t !== null; t = t.ue) n(t.fe, t);
73
+ for (let t = e.u; t !== null; t = t.fe) n(t.ae, t);
74
74
  // `?? null`: affects() marks route plain signals (no `_child` slot) through here.
75
- for (let t = e.u ?? null; t !== null; t = t.ae) {
76
- for (let e = t.o; e !== null; e = e.ue) n(e.fe, e);
75
+ for (let t = e.o?.l ?? null; t !== null; t = t.ce) {
76
+ for (let e = t.u; e !== null; e = e.fe) n(e.ae, e);
77
77
  }
78
78
  }
79
79
 
@@ -88,7 +88,7 @@ function forEachDependent(e, n) {
88
88
  // callbacks handle their own release (settleAutodispose in handleAsync); this
89
89
  // covers derivatively-pending dependents, which have no callbacks of their own.
90
90
  function releaseIfSettledUnobserved(e) {
91
- e.ce && e.T & CONFIG_AUTO_DISPOSE && !e.o && !(e.se & REACTIVE_ZOMBIE) && !(e.S & STATUS_PENDING) && unobserved(e);
91
+ e.Se && e.T & CONFIG_AUTO_DISPOSE && !e.u && !(e.ie & REACTIVE_ZOMBIE) && !(e.S & STATUS_PENDING) && unobserved(e);
92
92
  }
93
93
 
94
94
  // Error-path sweep: notifyStatus(STATUS_ERROR) clears dependents' pending
@@ -101,7 +101,7 @@ function releaseSettledDependents(e) {
101
101
  const visit = e => {
102
102
  if (t.has(e)) return;
103
103
  t.add(e);
104
- if (!e.o && e.T & CONFIG_AUTO_DISPOSE) (n ??= []).push(e);
104
+ if (!e.u && e.T & CONFIG_AUTO_DISPOSE) (n ??= []).push(e);
105
105
  forEachDependent(e, visit);
106
106
  };
107
107
  forEachDependent(e, visit);
@@ -129,7 +129,7 @@ function settleErroredDependents(e, n) {
129
129
  const visit = e => {
130
130
  if (r.has(e)) return;
131
131
  r.add(e);
132
- if (e._ === n) {
132
+ if (e.o?._ === n) {
133
133
  enqueueSub(e);
134
134
  t = true;
135
135
  }
@@ -144,12 +144,12 @@ function settlePendingSource(e) {
144
144
  let t;
145
145
  const r = new Set;
146
146
  // Companion updates no-op without the verdict layer (null hook).
147
- const o = GlobalQueue.Se;
147
+ const o = GlobalQueue.de;
148
148
  const settle = l => {
149
149
  if (r.has(l) || !removePendingSource(l, e)) return;
150
150
  r.add(l);
151
- l.de = clock;
152
- const u = l.oe?.values().next().value;
151
+ l.Te = clock;
152
+ const u = l.o?.le?.values().next().value;
153
153
  // STATUS_ERROR + pending sources only coexist via an errored loading
154
154
  // window's park (notifyStatus(STATUS_ERROR) clears pending sources
155
155
  // otherwise): the settled error stays the answer through the settle —
@@ -163,15 +163,15 @@ function settlePendingSource(e) {
163
163
  l.S &= ~STATUS_PENDING;
164
164
  if (!i) setPendingError(l);
165
165
  o !== null && o(l);
166
- if (l.le) {
166
+ if (l.o?.ue) {
167
167
  enqueueSub(l);
168
168
  n = true;
169
169
  }
170
- l.le = false;
170
+ if (l.o !== null) l.o.ue = false;
171
171
  // Fully settled with nobody watching: release candidate (#2934). Checked
172
172
  // again at release time — deferred so unobserved() can't unlink subs
173
173
  // lists this walk is still iterating.
174
- if (!l.o && l.T & CONFIG_AUTO_DISPOSE) (t ??= []).push(l);
174
+ if (!l.u && l.T & CONFIG_AUTO_DISPOSE) (t ??= []).push(l);
175
175
  }
176
176
  forEachDependent(l, settle);
177
177
  };
@@ -197,12 +197,12 @@ function handleAsync(e, n, t) {
197
197
  });
198
198
  }
199
199
  if (!o && !r) {
200
- e.Te = null;
200
+ if (e.o !== null) e.o.Ee = null;
201
201
  // A sync landing is the first real answer for a loadingValue node.
202
- e.Ee = false;
202
+ e.Ie = false;
203
203
  return n;
204
204
  }
205
- e.Te = n;
205
+ ext(e).Ee = n;
206
206
  let l;
207
207
  // Settle-time transition re-entry. The loading rail is invisible to
208
208
  // transactions (#2933): a boundary-caught first load never registers as an
@@ -216,42 +216,42 @@ function handleAsync(e, n, t) {
216
216
  // are the transaction's reveal machinery and always re-enter.
217
217
  const settleTransition = () => {
218
218
  const n = resolveTransition(e);
219
- if (n && e.S & STATUS_UNINITIALIZED && !currentTransition(n).Ie.has(e)) {
219
+ if (n && e.S & STATUS_UNINITIALIZED && !currentTransition(n).Ne.has(e)) {
220
220
  // Drop the stale stamp too: the plain settle write (setSignal) and the
221
221
  // stash-path restamp both re-enter the transaction through it.
222
- e.Ne = null;
222
+ e._e = null;
223
223
  return;
224
224
  }
225
225
  globalQueue.initTransition(n);
226
226
  };
227
227
  const handleError = t => {
228
- if (e.Te !== n) return;
228
+ if (e.o?.Ee !== n) return;
229
229
  // NotReadyError from rejected promises should be treated as pending, not error
230
230
  let r = t instanceof NotReadyError;
231
- if (r && e.Ee) {
231
+ if (r && e.Ie) {
232
232
  // Loading window: the flight died waiting on an unready source. Keep
233
233
  // serving commit #0 — same parking as recompute's catch for sync
234
234
  // dependency throws. The dead flight is released so the clock-gated
235
235
  // error-retry pull (updateIfNecessary) can also re-ask.
236
- e.Te = null;
236
+ if (e.o !== null) e.o.Ee = null;
237
237
  parkLoadingWindow(e, t);
238
- e.de = clock;
238
+ e.Te = clock;
239
239
  return;
240
240
  }
241
241
  settleTransition();
242
242
  notifyStatus(e, r ? STATUS_PENDING : STATUS_ERROR, t);
243
- e.de = clock;
243
+ e.Te = clock;
244
244
  // A real error settles derivatively-pending dependents (notifyStatus
245
245
  // cleared their pending sources), so stranded lazy ones release here —
246
246
  // the error twin of settlePendingSource's release (#2934).
247
247
  if (!r) releaseSettledDependents(e);
248
248
  };
249
249
  const asyncWrite = (r, o) => {
250
- if (e.Te !== n) return;
250
+ if (e.o?.Ee !== n) return;
251
251
  // If the node was dirtied by a newer write (optimistic override or regular),
252
252
  // skip this stale async result — the upcoming flush will recompute the node
253
253
  // with the new value, creating a fresh Promise that supersedes this one.
254
- if (e.se & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY)) return;
254
+ if (e.ie & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY)) return;
255
255
  settleTransition();
256
256
  const l = !!(e.S & STATUS_UNINITIALIZED);
257
257
  trimStaleDeps(e);
@@ -261,7 +261,7 @@ function handleAsync(e, n, t) {
261
261
  if (t) {
262
262
  t(r);
263
263
  if (l) clearStatus(e, true);
264
- } else if (e.De !== undefined) {
264
+ } else if (e.o?.De !== undefined) {
265
265
  // Optimistic node — resting OR covered by an active override — holds
266
266
  // through the shared pending-node path, exactly like a plain async memo,
267
267
  // so the commit clears STATUS_UNINITIALIZED (#2806) and elevation to
@@ -271,8 +271,8 @@ function handleAsync(e, n, t) {
271
271
  // (A17 — every reader sees the override); the revert reveals whatever
272
272
  // has committed by then, so corrections reveal atomically with their
273
273
  // transition rather than escaping it.
274
- if (e._e === NOT_PENDING) queuePendingNode(e);
275
- e._e = r;
274
+ if (e.Pe === NOT_PENDING) queuePendingNode(e);
275
+ e.Pe = r;
276
276
  // The hold is a companion-visible write like any other (A13/A19): the
277
277
  // clearStatus() above computed its verdict before the hold existed, so
278
278
  // isPending must re-derive (the value is not final until commit — V1)
@@ -280,9 +280,11 @@ function handleAsync(e, n, t) {
280
280
  // only notified when the hold is visible to them: under an active
281
281
  // override every reader sees the override (A17), so waking subs would
282
282
  // re-show an unchanged view — the revert is the notification point.
283
- GlobalQueue.Pe !== null && GlobalQueue.Pe(e, r);
284
- if (!hasActiveOverride(e)) insertSubs(e);
285
- e.de = clock;
283
+ GlobalQueue.Oe !== null && GlobalQueue.Oe(e, r);
284
+ if (!hasActiveOverride(e)) {
285
+ insertSubs(e);
286
+ }
287
+ e.Te = clock;
286
288
  } else if (u) {
287
289
  // Route through lane's effect queue for independent flushing
288
290
  const n = e.Re;
@@ -291,11 +293,11 @@ function handleAsync(e, n, t) {
291
293
  try {
292
294
  if (!n && l || !o || !o(r, t)) {
293
295
  e.be = r;
294
- e.de = clock;
296
+ e.Te = clock;
295
297
  // The latest() shadow write gives latest() effects independent lanes; the
296
298
  // _pendingSignal update is a no-op repeat of the clearStatus() call above
297
299
  // (computePendingState doesn't read _value).
298
- GlobalQueue.Pe !== null && GlobalQueue.Pe(e, r);
300
+ GlobalQueue.Oe !== null && GlobalQueue.Oe(e, r);
299
301
  insertSubs(e, true);
300
302
  }
301
303
  } catch (n) {
@@ -320,7 +322,7 @@ function handleAsync(e, n, t) {
320
322
  // held-value branch is window-gated, and commitPendingNode closes the
321
323
  // window when the hold commits, so no one-frame isPending pulse can leak
322
324
  // to live observers between the landing and its commit (#2990).
323
- if (e._e === NOT_PENDING) e.Ee = false;
325
+ if (e.Pe === NOT_PENDING) e.Ie = false;
324
326
  settlePendingSource(e);
325
327
  schedule();
326
328
  flush();
@@ -334,7 +336,7 @@ function handleAsync(e, n, t) {
334
336
  // Returns whether the node released, so the iterator branch can stop
335
337
  // pulling values instead of pumping an unobserved stream forever (#2935).
336
338
  const settleAutodispose = () => {
337
- if (e.T & CONFIG_AUTO_DISPOSE && !e.o && !(e.S & STATUS_PENDING)) {
339
+ if (e.T & CONFIG_AUTO_DISPOSE && !e.u && !(e.S & STATUS_PENDING)) {
338
340
  unobserved(e);
339
341
  return true;
340
342
  }
@@ -394,7 +396,7 @@ function handleAsync(e, n, t) {
394
396
  t = r;
395
397
  f = true;
396
398
  if (r.done) i = true;
397
- } else if (e.Te !== n) {
399
+ } else if (e.o?.Ee !== n) {
398
400
  return;
399
401
  } else if (!r.done) {
400
402
  u = true;
@@ -414,7 +416,7 @@ function handleAsync(e, n, t) {
414
416
  if (c && s) {
415
417
  r = t;
416
418
  a = true;
417
- } else if (e.Te === n) {
419
+ } else if (e.o?.Ee === n) {
418
420
  i = true;
419
421
  handleError(t);
420
422
  settleAutodispose();
@@ -480,7 +482,7 @@ function handleAsync(e, n, t) {
480
482
  if (u) {
481
483
  l = r;
482
484
  t = true;
483
- } else if (e.Te === n && !(e.se & REACTIVE_DISPOSED) && flattenIfIterable(r, registerDeferredClose)) ; else {
485
+ } else if (e.o?.Ee === n && !(e.ie & REACTIVE_DISPOSED) && flattenIfIterable(r, registerDeferredClose)) ; else {
484
486
  asyncWrite(r);
485
487
  settleAutodispose();
486
488
  }
@@ -505,12 +507,12 @@ function handleAsync(e, n, t) {
505
507
  // is opened — first-flight work on a loadingValue node is loading-class
506
508
  // (invisible to boundaries and transitions); the flight itself is
507
509
  // already registered in _inFlight and lands through asyncWrite.
508
- if (e.Ee) return e.be;
510
+ if (e.Ie) return e.be;
509
511
  globalQueue.initTransition(resolveTransition(e));
510
512
  throw new NotReadyError(context);
511
513
  } else if (!flattenIfIterable(l)) {
512
514
  // Synchronously-resolved promise: the first real answer landed.
513
- e.Ee = false;
515
+ e.Ie = false;
514
516
  }
515
517
  // A sync-resolved promise holding an AsyncIterable flattened LIVE (we
516
518
  // are still inside the synchronous read): full initial-drain semantics
@@ -520,31 +522,31 @@ function handleAsync(e, n, t) {
520
522
  if (u !== null) {
521
523
  if (!u) {
522
524
  // Loading window: serve commit #0 (see the promise branch above).
523
- if (e.Ee) return e.be;
525
+ if (e.Ie) return e.be;
524
526
  globalQueue.initTransition(resolveTransition(e));
525
527
  throw new NotReadyError(context);
526
528
  }
527
529
  // A sync first yield (or immediate empty completion) is the first real
528
530
  // answer; async yields clear inside asyncWrite.
529
- e.Ee = false;
531
+ e.Ie = false;
530
532
  }
531
533
  return l;
532
534
  }
533
535
 
534
536
  function clearStatus(e, n = false) {
535
- if (e.oe) clearPendingSources(e);
536
- if (e.le) e.le = false;
537
+ if (e.o?.le) clearPendingSources(e);
538
+ if (e.o?.ue) if (e.o !== null) e.o.ue = false;
537
539
  // The pending window is over; its quiet classification dies with it.
538
540
  // (Unconditional: _reask is baked into the node literals, so this is a
539
541
  // plain store to an existing slot — no shape change.)
540
- e.pe = false;
542
+ if (e.o !== null) e.o.pe = false;
541
543
  e.S = n ? 0 : e.S & STATUS_UNINITIALIZED;
542
- if (e._) setPendingError(e);
544
+ if (e.o?._) setPendingError(e);
543
545
  // Update pending signal for isPending() reactivity (companions only exist
544
546
  // once the verdict layer created them, which installs the hooks).
545
- if (e.Oe || e.ge) GlobalQueue.Se(e);
546
- if (e.u && GlobalQueue.Ge !== null) GlobalQueue.Ge(e);
547
- if (e.i) e.i();
547
+ if (e.o?.Ge || e.o?.ge) GlobalQueue.de(e);
548
+ if (e.o?.l && e.T & CONFIG_CHILD_COMPANIONS && GlobalQueue.ye !== null) GlobalQueue.ye(e);
549
+ if (e.o?.A) e.o.A.call(e);
548
550
  }
549
551
 
550
552
  function notifyStatus(e, n, t, r, o) {
@@ -552,7 +554,7 @@ function notifyStatus(e, n, t, r, o) {
552
554
  if (n === STATUS_ERROR && !(t instanceof StatusError) && !(t instanceof NotReadyError)) t = new StatusError(e, t);
553
555
  const l = n === STATUS_PENDING && t instanceof NotReadyError ? t.source : undefined;
554
556
  const u = l === e;
555
- const i = n === STATUS_PENDING && e.De !== undefined && !u;
557
+ const i = n === STATUS_PENDING && e.o?.De !== undefined && !u;
556
558
  const s = i && hasActiveOverride(e);
557
559
  if (!r) {
558
560
  if (n === STATUS_PENDING && l) {
@@ -564,41 +566,41 @@ function notifyStatus(e, n, t, r, o) {
564
566
  } else {
565
567
  clearPendingSources(e);
566
568
  e.S = n | (n !== STATUS_ERROR ? e.S & STATUS_UNINITIALIZED : 0);
567
- e._ = t;
569
+ ext(e)._ = t;
568
570
  }
569
- GlobalQueue.Se !== null && GlobalQueue.Se(e);
570
- if (e.u && GlobalQueue.Ge !== null) GlobalQueue.Ge(e);
571
+ GlobalQueue.de !== null && GlobalQueue.de(e);
572
+ if (e.o?.l && e.T & CONFIG_CHILD_COMPANIONS && GlobalQueue.ye !== null) GlobalQueue.ye(e);
571
573
  }
572
574
  if (o && !r) {
573
575
  assignOrMergeLane(e, o);
574
576
  }
575
577
  const f = r || s;
576
578
  const a = r || i ? undefined : o;
577
- if (e.i) {
579
+ if (e.o?.A) {
578
580
  if (r && n === STATUS_PENDING) {
579
581
  return;
580
582
  }
581
583
  if (f) {
582
- e.i(n, t);
584
+ e.o.A.call(e, n, t);
583
585
  } else {
584
- e.i();
586
+ e.o.A.call(e);
585
587
  }
586
588
  return;
587
589
  }
588
590
  forEachDependent(e, (e, r) => {
589
- e.de = clock;
590
- if (n === STATUS_PENDING && l && !e.oe?.has(l) || n !== STATUS_PENDING && (e._ !== t || e.oe)) {
591
+ e.Te = clock;
592
+ if (n === STATUS_PENDING && l && !e.o?.le?.has(l) || n !== STATUS_PENDING && (e.o?._ !== t || e.o?.le)) {
591
593
  // A pending-observer link is the subscription an `isPending` read created.
592
594
  // It exists so the observer re-runs when the source settles, but it must
593
595
  // not carry a real (non-NotReadyError) error — the synchronous `isPending`
594
596
  // read swallows those, and the async path must match. Re-run the observer
595
597
  // so `isPending` re-evaluates (to not-pending) instead of forwarding.
596
- if (r.ye && n !== STATUS_PENDING && !(t instanceof NotReadyError)) {
598
+ if (r.me && n !== STATUS_PENDING && !(t instanceof NotReadyError)) {
597
599
  enqueueSub(e);
598
600
  schedule();
599
601
  return;
600
602
  }
601
- if (!f && !e.Ne) queuePendingNode(e);
603
+ if (!f && !e._e) queuePendingNode(e);
602
604
  notifyStatus(e, n, t, f, a);
603
605
  }
604
606
  });
@@ -30,6 +30,15 @@ const REACTIVE_MANUAL_WRITE = 1 << 10;
30
30
  * pending window does not read as pending (question-scoped pending model).
31
31
  */ const REACTIVE_REASK = 1 << 11;
32
32
 
33
+ /**
34
+ * A dependency write landed while this subscriber was mid-recompute — a
35
+ * nested pull committed beneath one of its reads (#3037). The heap refuses
36
+ * RECOMPUTING nodes, so recompute's tail consumes this latch and reschedules:
37
+ * values the pass read before the nested commit are stale. Only set for
38
+ * links validated this pass (gen-current): a write to an untouched link is
39
+ * either re-read later in the pass (fresh) or trimmed with it (not a dep).
40
+ */ const REACTIVE_MISSED_WAKE = 1 << 12;
41
+
33
42
  // Static configuration bits packed into Owner/Computed/Signal _config.
34
43
  const CONFIG_OWNED_WRITE = 1 << 0;
35
44
 
@@ -45,6 +54,35 @@ const CONFIG_AUTO_DISPOSE = 1 << 5;
45
54
 
46
55
  const CONFIG_SYNC = 1 << 6;
47
56
 
57
+ // Presence bits (stage-3 hot-path monomorphism, DESIGN-PATCH-CHANNEL §11b):
58
+ // optional per-node slots (_overrideValue, _pendingSignal/_latestValueComputed,
59
+ // _snapshotValue, _optimisticLane) are NOT part of every node's hidden class —
60
+ // reading a missing property defeats V8's inline caches on the hottest write/
61
+ // notify loops. These bits live on the always-present `_config` so hot paths
62
+ // pay one monomorphic masked read and only touch the optional field when its
63
+ // installer flagged it. Bits are STICKY ("may be set") — the guarded field
64
+ // read remains authoritative.
65
+ const CONFIG_OPTIMISTIC = 1 << 7;
66
+
67
+ const CONFIG_HAS_COMPANIONS = 1 << 8;
68
+
69
+ const CONFIG_HAS_SNAPSHOT = 1 << 9;
70
+
71
+ const CONFIG_HAS_LANE = 1 << 10;
72
+
73
+ /** Set on a FIREWALL computed when any of its child signals creates an
74
+ * isPending()/latest() companion. Gates the post-recompute child-companion
75
+ * walk (#3038): a store computed's `_child` chain holds one node per
76
+ * materialized leaf, so walking it unconditionally makes every update cost
77
+ * O(all leaves ever read). Sticky — set at companion creation, never
78
+ * cleared; sync-only apps never set it and never pay the walk. */ const CONFIG_CHILD_COMPANIONS = 1 << 11;
79
+
80
+ /** Set on a computed when its first firewall child signal is installed
81
+ * (projection machinery). Gates markNode's firewall-children walk with one
82
+ * masked read of the always-present _config — the walk's old `_child` read
83
+ * moved into the cold extension (§12), and an unconditional `_x` deref per
84
+ * marked node measurably taxed the propagation hot path (diamond -22%). */ const CONFIG_FW_CHILDREN = 1 << 12;
85
+
48
86
  const STATUS_PENDING = 1 << 0;
49
87
 
50
88
  const STATUS_ERROR = 1 << 1;
@@ -89,4 +127,4 @@ const defaultContext = {};
89
127
  * @internal
90
128
  */ const $REFRESH = Symbol("refresh");
91
129
 
92
- export { $REFRESH, CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, CONFIG_IN_SNAPSHOT_SCOPE, CONFIG_NO_SNAPSHOT, 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_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 };
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 };