@solidjs/signals 2.0.0-rc.11 → 2.0.0-rc.13

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.
@@ -105,10 +105,10 @@ function releaseSnapshotScope(e) {
105
105
  }
106
106
 
107
107
  function releaseSubtree(e) {
108
- let t = e._n;
108
+ let t = e.On;
109
109
  while (t) {
110
110
  if (t.ot) {
111
- t = t.Fn;
111
+ t = t._n;
112
112
  continue;
113
113
  }
114
114
  if (t.Se) {
@@ -122,7 +122,7 @@ function releaseSubtree(e) {
122
122
  }
123
123
  }
124
124
  releaseSubtree(t);
125
- t = t.Fn;
125
+ t = t._n;
126
126
  }
127
127
  }
128
128
 
@@ -155,8 +155,8 @@ function recompute(e, t = false) {
155
155
  let i = false;
156
156
  if (attrHooks !== null) attrHooks.recomputeStart(e, t);
157
157
  // Lane posture is resolved BEFORE the previous frame is parked below: a
158
- // lane pass on an effect direct-commits (#3662, see the parking site), so
159
- // the decision must be known there. `lane` is applied to
158
+ // lane pass parks a lane frame, not a transaction zombie (#3662, #3698;
159
+ // see the parking site), so the decision must be known there. `lane` is applied to
160
160
  // `currentOptimisticLane` further down, once the previous posture is saved.
161
161
  let l = !!(e.oe & REACTIVE_OPTIMISTIC_DIRTY);
162
162
  let u = null;
@@ -204,29 +204,39 @@ function recompute(e, t = false) {
204
204
  // Tracked effects run after finalizePureQueue, so dispose immediately
205
205
  // instead of deferring. Children built by an uncommitted recompute
206
206
  // (CONFIG_HELD_CHILDREN) die immediately too: no frame ever showed them.
207
- // Everything else is the committed frame's and is deferred as zombies
208
- // until this node's commit — a transaction-owned node included (#3404):
209
- // a parked node's children predate the hold, and tearing them down when
210
- // the source lands ran cleanups before the transaction's atomic reveal.
211
- // A lane pass on an effect parks a LANE frame instead (CONFIG_LANE_FRAME,
212
- // #3662; A15 lanes corollary): the frame it replaces leaves the screen
213
- // when the lane's queue applies this run (A30) — not at the action's
214
- // commit — and a held lane defers that with the frame still displayed.
215
- // The drain is the lane's first render entry for this pass, pushed before
216
- // the pass builds the new frame: cleanups before side effects — the
217
- // retired frame's `onCleanup`s run ahead of every effect callback of the
218
- // new frame. While the frame waits, the live children were never shown:
219
- // a superseding pass disposes them here like held children, and the
220
- // parked frame stays.
221
- if (n === EFFECT_TRACKED || e.C & (CONFIG_HELD_CHILDREN | CONFIG_LANE_FRAME)) disposeChildren(e); else if (e._n !== null || e.Ve !== null) {
207
+ // Everything else is the committed frame's. Which frame retires it is
208
+ // decided by who owns the pass, never by the node's kind (A15 lane work
209
+ // and transaction work, ruled 2026-09-28, #3698). Transaction work — a
210
+ // pass under a held transaction — defers them as zombies until this
211
+ // node's commit, a transaction-owned node included (#3404): a parked
212
+ // node's children predate the hold, and tearing them down when the
213
+ // source lands ran cleanups before the transaction's atomic reveal.
214
+ // Lane work — a pass over a lane, effect or memo alike — parks a LANE
215
+ // frame instead (CONFIG_LANE_FRAME, #3662, #3698): the frame it replaces
216
+ // leaves the screen when the lane's queue applies this pass (A30) — not
217
+ // at the action's commit — and a held lane defers that with the frame
218
+ // still displayed. The drain is the lane's first render entry for this
219
+ // pass, pushed before the pass builds the new frame: cleanups before
220
+ // side effects — the retired frame's `onCleanup`s run ahead of every
221
+ // effect callback of the new frame. While the frame waits, the live
222
+ // children were never shown: a superseding pass disposes them here like
223
+ // held children, and the parked frame stays. Zombies exist for
224
+ // transaction work only: #3662 gated the lane frame on `isEffect`, and a
225
+ // memo's lane pass (its value a derived override, A17) parked a
226
+ // transaction zombie that made the memo a pending node of the action,
227
+ // stamped it, and sent its next mainline recompute through the
228
+ // stamped-memo arm above — a `Show` whose `when` getter owns a
229
+ // compiler-emitted memo held an unrelated sync write for the action's
230
+ // lifetime, against the #3460 ruling that a lane never holds a sync write.
231
+ if (n === EFFECT_TRACKED || e.C & (CONFIG_HELD_CHILDREN | CONFIG_LANE_FRAME)) disposeChildren(e); else if (e.On !== null || e.Ve !== null) {
222
232
  markDisposal(e);
223
233
  const t = ext(e);
224
234
  t.Et = e.Ve;
225
- t.dt = e._n;
235
+ t.dt = e.On;
226
236
  e.Ve = null;
227
- e._n = null;
237
+ e.On = null;
228
238
  e.Nt = 0;
229
- if (n && u) {
239
+ if (u) {
230
240
  e.C |= CONFIG_LANE_FRAME;
231
241
  findLane(u).It[0].push(() => {
232
242
  e.C &= ~CONFIG_LANE_FRAME;
@@ -798,9 +808,9 @@ function computed(e, t) {
798
808
  u: null,
799
809
  Ht: null,
800
810
  _parent: context,
801
- Fn: null,
802
- kt: null,
803
811
  _n: null,
812
+ kt: null,
813
+ On: null,
804
814
  oe: t?.lazy ? REACTIVE_LAZY : REACTIVE_NONE,
805
815
  S: i ? 0 : STATUS_UNINITIALIZED,
806
816
  Ge: clock,
@@ -880,9 +890,9 @@ function computed(e, t) {
880
890
  u: null,
881
891
  Ht: null,
882
892
  _parent: context,
883
- Fn: null,
884
- kt: null,
885
893
  _n: null,
894
+ kt: null,
895
+ On: null,
886
896
  oe: REACTIVE_LAZY,
887
897
  S: STATUS_UNINITIALIZED,
888
898
  Ge: clock,
@@ -1398,7 +1408,7 @@ function enterStagedRead(e, t = e.me) {
1398
1408
  * False means the reader derives from the staged value and enters its
1399
1409
  * transaction (enterStagedRead, A29).
1400
1410
  */ function readerSeesCommitted(e, t, n, i) {
1401
- return !!(!t || currentOptimisticLane !== null && GlobalQueue.On(e, n, t) || e._e === NOT_PENDING || t.C & CONFIG_CHILDREN_FORBIDDEN || stale && !i && heldFromStale(e, t) || e.C & CONFIG_HELD_TRUTH && currentOptimisticLane !== null && !latestReadActive && !(t.C & CONFIG_AUTHORITATIVE_READ));
1411
+ return !!(!t || currentOptimisticLane !== null && GlobalQueue.An(e, n, t) || e._e === NOT_PENDING || t.C & CONFIG_CHILDREN_FORBIDDEN || stale && !i && heldFromStale(e, t) || e.C & CONFIG_HELD_TRUTH && currentOptimisticLane !== null && !latestReadActive && !(t.C & CONFIG_AUTHORITATIVE_READ));
1402
1412
  }
1403
1413
 
1404
1414
  /** A28 — set when a node is staged (queuePendingNode) or a held node rewritten
@@ -1522,7 +1532,7 @@ function read(e) {
1522
1532
  // Checked before isPending so that isPending(() => latest(x)) checks
1523
1533
  // the _pendingSignal of _latestValueComputed (async in flight) rather
1524
1534
  // than the original node (which stays "pending" while held in a transition).
1525
- if (latestReadActive) return GlobalQueue.An(e);
1535
+ if (latestReadActive) return GlobalQueue.Cn(e);
1526
1536
  let t = context;
1527
1537
  if (t?.Zt) t = t.Yt;
1528
1538
  const n = e;
@@ -1532,7 +1542,7 @@ function read(e) {
1532
1542
  // Probe mode is suspended while preparing the node so nested reads during a
1533
1543
  // recompute don't collect into the probe.
1534
1544
  if (pendingCheckActive) {
1535
- GlobalQueue.Cn(e, t, l, i);
1545
+ GlobalQueue.Rn(e, t, l, i);
1536
1546
  } else if (typeof n.Se === "function") {
1537
1547
  prepareComputed(e, false);
1538
1548
  }
@@ -1584,13 +1594,13 @@ function read(e) {
1584
1594
  // (firewall-backed store reads always did; plain memos since the #3043
1585
1595
  // port): falling through served `undefined` as if settled and stranded
1586
1596
  // the reader outside both transactions, so it never re-ran.
1587
- if (t && !(stale && !(l.S & STATUS_UNINITIALIZED) && !(l.C & CONFIG_INPUTS_PUBLISHED) && !(l.C & CONFIG_HAS_LANE && GlobalQueue.Rn(l)) && heldFromStale(l, t))) {
1597
+ if (t && !(stale && !(l.S & STATUS_UNINITIALIZED) && !(l.C & CONFIG_INPUTS_PUBLISHED) && !(l.C & CONFIG_HAS_LANE && GlobalQueue.Gn(l)) && heldFromStale(l, t))) {
1588
1598
  // Per-lane suspension lives with the engine (a non-null lane implies it
1589
1599
  // is installed): under a lane, only same-lane pending async without an
1590
1600
  // active override throws — plus uninitialized sources regardless of
1591
1601
  // lane (#3276); that check rides laneSuspends so floor bundles don't
1592
1602
  // pay for it.
1593
- if (currentOptimisticLane === null || GlobalQueue.Gn(l)) {
1603
+ if (currentOptimisticLane === null || GlobalQueue.Dn(l)) {
1594
1604
  // An untracked read of a pending node still re-runs its reader when
1595
1605
  // the node settles — unless nobody is reading (`spectating`, #3528)
1596
1606
  // or the reader is the node itself.
@@ -1694,7 +1704,7 @@ function read(e) {
1694
1704
  // a node whose own source answered with a DIFFERENT value hands a
1695
1705
  // tracked reader the staged truth (A18 supersession, #3331). Untracked
1696
1706
  // reads display the override.
1697
- if (t && e.C & (CONFIG_HAS_LANE | CONFIG_OVERRIDE_SUPERSEDED)) return GlobalQueue.Dn(e, t);
1707
+ if (t && e.C & (CONFIG_HAS_LANE | CONFIG_OVERRIDE_SUPERSEDED)) return GlobalQueue.Pn(e, t);
1698
1708
  return unwrapOverride(e.o?.we);
1699
1709
  }
1700
1710
  e.C |= CONFIG_AUTHORITATIVE_OBSERVED;
@@ -1706,7 +1716,7 @@ function read(e) {
1706
1716
  // _pendingValue for correct fetching. The sub is recorded for replay at commit
1707
1717
  // so it re-runs with the new committed view. (Gate details live with the
1708
1718
  // engine — a non-null lane implies it is installed.)
1709
- if (currentOptimisticLane !== null && activeTransition !== null && t !== null && GlobalQueue.Pn(e, n, t)) {
1719
+ if (currentOptimisticLane !== null && activeTransition !== null && t !== null && GlobalQueue.Fn(e, n, t)) {
1710
1720
  return i;
1711
1721
  }
1712
1722
  // In optimistic lane context, return _value for optimistic/lane-assigned signals
@@ -68,20 +68,31 @@ function recordsChannel() {
68
68
  const e = globalThis;
69
69
  if (e[RECORDS]) return e[RECORDS];
70
70
  const n = new Map;
71
+ const t = new Map;
71
72
  return e[RECORDS] = {
72
- subscribe(e, t) {
73
- const o = n.get(e);
74
- // Set semantics: one entry per function, however often it is passed.
75
- if (o === undefined) n.set(e, [ t ]); else if (!o.includes(t)) n.set(e, [ ...o, t ]);
73
+ subscribe(e, o, r) {
74
+ const i = n.get(e);
75
+ // Set semantics: one entry per function, however often it is passed —
76
+ // the first subscription's options stand for it.
77
+ if (i === undefined || !i.includes(o)) {
78
+ n.set(e, i === undefined ? [ o ] : [ ...i, o ]);
79
+ if (r !== undefined && r.bodies) {
80
+ let n = t.get(e);
81
+ if (n === undefined) t.set(e, n = new Set);
82
+ n.add(o);
83
+ }
84
+ }
76
85
  return () => {
77
- const o = n.get(e);
78
- if (o === undefined) return;
79
- const r = o.filter(e => e !== t);
80
- if (r.length > 0) n.set(e, r); else n.delete(e);
86
+ const r = n.get(e);
87
+ if (r === undefined || !r.includes(o)) return;
88
+ const i = r.filter(e => e !== o);
89
+ if (i.length > 0) n.set(e, i); else n.delete(e);
90
+ const s = t.get(e);
91
+ if (s !== undefined && s.delete(o) && s.size === 0) t.delete(e);
81
92
  };
82
93
  },
83
- observed(e) {
84
- return n.has(e);
94
+ observed(e, o) {
95
+ return o === "bodies" ? t.has(e) : n.has(e);
85
96
  },
86
97
  emit(e, t, o) {
87
98
  const r = n.get(e);
@@ -108,9 +119,14 @@ const OBSERVE = {
108
119
  // empty HERE and gains its members by augmentation downstream.
109
120
  server: {},
110
121
  exclude(e) {
111
- excludedOwners.add(e);
122
+ markedOwners.set(e, true);
112
123
  hasExclusions = true;
113
124
  },
125
+ include(e) {
126
+ // No flag flip: with nothing excluded there is nothing to re-admit,
127
+ // and the walk stays short-circuited.
128
+ markedOwners.set(e, false);
129
+ },
114
130
  isExcluded: isExcluded,
115
131
  ownerPath: ownerPath
116
132
  };
@@ -119,9 +135,13 @@ const OBSERVE = {
119
135
 
120
136
  // An observer that lives inside the observed app (an adapter's panel,
121
137
  // devtools) marks its root; both channels check the subject's owner chain —
122
- // the same walk `ownerPath` already makes — and stay silent under it. The
123
- // flag short-circuits the walk for the common case of no exclusions.
124
- const excludedOwners = new WeakSet;
138
+ // the same walk `ownerPath` already makes — and stay silent under it. An
139
+ // observer that WRAPS the app (a toolbar around it) excludes its own root
140
+ // and includes the app's back: one map, `true` excluded / `false` included,
141
+ // and the nearest marker up the chain decides. The flag short-circuits the
142
+ // walk for the common case of no exclusions (an include with nothing
143
+ // excluded changes no verdict, so it does not flip it).
144
+ const markedOwners = new WeakMap;
125
145
 
126
146
  let hasExclusions = false;
127
147
 
@@ -130,7 +150,10 @@ let hasExclusions = false;
130
150
  function isExcluded(e) {
131
151
  if (!hasExclusions || !e) return false;
132
152
  let n = "_parent" in e ? e : e.ie ?? null;
133
- for (;n !== null; n = n._parent) if (excludedOwners.has(n)) return true;
153
+ for (;n !== null; n = n._parent) {
154
+ const e = markedOwners.get(n);
155
+ if (e !== undefined) return e;
156
+ }
134
157
  return false;
135
158
  }
136
159
 
@@ -653,13 +653,13 @@ function trackOptimisticStore(e) {
653
653
  GlobalQueue.Kn = runLaneEffects;
654
654
  GlobalQueue.Qe = supersedeOverride;
655
655
  GlobalQueue.zn = endOptimism;
656
- GlobalQueue.Dn = overrideRead;
656
+ GlobalQueue.Pn = overrideRead;
657
657
  GlobalQueue.je = laneOverride;
658
658
  GlobalQueue.Vn = landOnOverride;
659
- GlobalQueue.Pn = gatedRead;
660
- GlobalQueue.Gn = laneSuspends;
661
- GlobalQueue.Rn = laneLive;
662
- GlobalQueue.On = laneReadsCommitted;
659
+ GlobalQueue.Fn = gatedRead;
660
+ GlobalQueue.Dn = laneSuspends;
661
+ GlobalQueue.Gn = laneLive;
662
+ GlobalQueue.An = laneReadsCommitted;
663
663
  GlobalQueue._t = recomputeLane;
664
664
  GlobalQueue.Ot = laneAsyncPending;
665
665
  GlobalQueue.St = laneAsyncSettled;
@@ -14,7 +14,7 @@ const PENDING_OWNER = {};
14
14
 
15
15
  // Dummy owner to trigger store's read() path
16
16
  function markDisposal(e) {
17
- let n = e._n;
17
+ let n = e.On;
18
18
  while (n) {
19
19
  const e = n.oe;
20
20
  n.oe = e | REACTIVE_ZOMBIE;
@@ -28,7 +28,7 @@ function markDisposal(e) {
28
28
  if (e & REACTIVE_IN_HEAP) insertIntoHeap(n, zombieQueue); else insertIntoHeapHeight(n, zombieQueue);
29
29
  }
30
30
  markDisposal(n);
31
- n = n.Fn;
31
+ n = n._n;
32
32
  }
33
33
  }
34
34
 
@@ -76,8 +76,8 @@ function disposeChildren(e, n = false, t) {
76
76
  schedule();
77
77
  }
78
78
  if (n && e.Se && e.o !== null) e.o.pe = null;
79
- let o = t ? e.o?.dt ?? null : e._n;
80
- if (!t) e._n = null;
79
+ let o = t ? e.o?.dt ?? null : e.On;
80
+ if (!t) e.On = null;
81
81
  while (o) {
82
82
  const e = o;
83
83
  // Owner teardown is death regardless of the child's own lifecycle
@@ -107,7 +107,7 @@ function disposeChildren(e, n = false, t) {
107
107
  // itself out of the detached chain, and a cleanup may then have linked
108
108
  // it at the fresh head, which rewrote the `_nextSibling` a pre-read
109
109
  // would still be holding.
110
- o = o.Fn;
110
+ o = o._n;
111
111
  }
112
112
  if (t) {
113
113
  if (e.o !== null) e.o.dt = null;
@@ -118,8 +118,8 @@ function disposeChildren(e, n = false, t) {
118
118
  // walks that already advanced past us still reach later siblings.
119
119
  if (n && !t && !(i & REACTIVE_ZOMBIE) && e._parent !== null && !(e._parent.oe & REACTIVE_DISPOSED)) {
120
120
  const n = e.kt;
121
- const t = e.Fn;
122
- if (n !== null) n.Fn = t; else e._parent._n = t;
121
+ const t = e._n;
122
+ if (n !== null) n._n = t; else e._parent.On = t;
123
123
  if (t !== null) t.kt = n;
124
124
  e.kt = null;
125
125
  }
@@ -134,11 +134,11 @@ function disposeChildren(e, n = false, t) {
134
134
  }
135
135
 
136
136
  function linkChild(e, n) {
137
- const t = e._n;
137
+ const t = e.On;
138
138
  n.kt = null;
139
- n.Fn = t;
139
+ n._n = t;
140
140
  if (t !== null) t.kt = n;
141
- e._n = n;
141
+ e.On = n;
142
142
  }
143
143
 
144
144
  function runDisposal(e, n) {
@@ -295,8 +295,8 @@ function disposeRootSelf(e = true) {
295
295
  C: t ? CONFIG_TRANSPARENT : 0,
296
296
  Zt: true,
297
297
  Yt: n?.Zt ? n.Yt : n,
298
+ On: null,
298
299
  _n: null,
299
- Fn: null,
300
300
  kt: null,
301
301
  Ve: null,
302
302
  T: n?.T ?? globalQueue,
@@ -554,8 +554,8 @@ class GlobalQueue extends Queue {
554
554
  static Ue=null;
555
555
  static qe=null;
556
556
  static Qn=null;
557
- static An=null;
558
557
  static Cn=null;
558
+ static Rn=null;
559
559
  static Hn=null;
560
560
  static At=null;
561
561
  static k=null;
@@ -579,14 +579,14 @@ class GlobalQueue extends Queue {
579
579
  * apply at lane-effect timing — visible in flight, unlike the regular
580
580
  * effect queues an action stashes. Injected; null when unused. */
581
581
  static Zn=null;
582
- static Pn=null;
583
- static Gn=null;
582
+ static Fn=null;
583
+ static Dn=null;
584
584
  /** Is the node routed through a LIVE lane (`resolveLane`)? read()'s reveal
585
585
  * carve-out asks before showing a foreign-held pending node's committed
586
586
  * value: a lane-derived flight's inputs are already revealed through the
587
587
  * lane (#3334). Gated on CONFIG_HAS_LANE, which only the engine sets. */
588
- static Rn=null;
589
- static On=null;
588
+ static Gn=null;
589
+ static An=null;
590
590
  static _t=null;
591
591
  static Ot=null;
592
592
  /** Authoritative-view reader wakeup: installed by until() and refresh() before
@@ -618,7 +618,7 @@ class GlobalQueue extends Queue {
618
618
  * transaction's committed value over its staged write. */
619
619
  /** A tracked read of an active override: the lane outside-view rule
620
620
  * (#3460) and the A18 supersession selection (#3331) — see optimistic.ts. */
621
- static Dn=null;
621
+ static Pn=null;
622
622
  /** A lane pass's publish for a memo (#3479, lanes stage): the speculative
623
623
  * result becomes a DERIVED override, `_value` stays committed — see
624
624
  * optimistic.ts laneOverride. Set with the engine, which a lane implies. */
@@ -657,9 +657,9 @@ GlobalQueue.qe = updateChildCompanions;
657
657
 
658
658
  GlobalQueue.Qn = snapCompanionsToState;
659
659
 
660
- GlobalQueue.An = latestRead;
660
+ GlobalQueue.Cn = latestRead;
661
661
 
662
- GlobalQueue.Cn = pendingCheckRead;
662
+ GlobalQueue.Rn = pendingCheckRead;
663
663
 
664
664
  GlobalQueue.Hn = recordFreshRead;
665
665