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

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 (198) hide show
  1. package/dist/dev-shared.js +6505 -0
  2. package/dist/dev.attribution.js +3484 -0
  3. package/dist/dev.js +10227 -9508
  4. package/dist/observe/affects-marks.js +86 -0
  5. package/dist/observe/affects.js +112 -0
  6. package/dist/observe/attribution.js +7 -0
  7. package/dist/observe/boundaries.js +696 -0
  8. package/dist/observe/core/action.js +182 -0
  9. package/dist/observe/core/async.js +824 -0
  10. package/dist/observe/core/attribution-costs.js +66 -0
  11. package/dist/observe/core/attribution-feedback.js +280 -0
  12. package/dist/observe/core/attribution-hooks.js +83 -0
  13. package/dist/observe/core/attribution-queries.js +43 -0
  14. package/dist/observe/core/attribution.js +3079 -0
  15. package/dist/observe/core/constants.js +269 -0
  16. package/dist/observe/core/context.js +63 -0
  17. package/dist/observe/core/copies.js +10 -0
  18. package/dist/observe/core/core.js +1707 -0
  19. package/dist/observe/core/dev.js +394 -0
  20. package/dist/observe/core/effect.js +193 -0
  21. package/dist/observe/core/error-hooks.js +71 -0
  22. package/dist/observe/core/error.js +82 -0
  23. package/dist/observe/core/external.js +97 -0
  24. package/dist/observe/core/graph.js +162 -0
  25. package/dist/observe/core/heap.js +124 -0
  26. package/dist/observe/core/identities.js +62 -0
  27. package/dist/observe/core/invariants.js +12 -0
  28. package/dist/observe/core/lanes.js +972 -0
  29. package/dist/observe/core/owner.js +371 -0
  30. package/dist/observe/core/scheduler.js +1157 -0
  31. package/dist/observe/core/truth.js +47 -0
  32. package/dist/observe/core/verdict.js +470 -0
  33. package/dist/observe/deferred.js +186 -0
  34. package/dist/observe/flatten.js +79 -0
  35. package/dist/observe/from.js +248 -0
  36. package/dist/observe/index.js +71 -0
  37. package/dist/observe/map.js +593 -0
  38. package/dist/observe/reveal.js +212 -0
  39. package/dist/observe/signals.js +717 -0
  40. package/dist/observe/store/affects.js +142 -0
  41. package/dist/observe/store/edges.js +39 -0
  42. package/dist/observe/store/hooks.js +16 -0
  43. package/dist/observe/store/index.js +47 -0
  44. package/dist/observe/store/optimistic.js +713 -0
  45. package/dist/observe/store/projection.js +345 -0
  46. package/dist/observe/store/reconcile.js +281 -0
  47. package/dist/observe/store/store.js +2127 -0
  48. package/dist/observe/store/storePath.js +103 -0
  49. package/dist/observe/store/target.js +46 -0
  50. package/dist/observe/store/types.js +83 -0
  51. package/dist/observe/store/utils.js +1063 -0
  52. package/dist/prod/affects-marks.js +86 -0
  53. package/dist/prod/affects.js +98 -112
  54. package/dist/prod/attribution.js +45 -0
  55. package/dist/prod/boundaries.js +578 -468
  56. package/dist/prod/core/action.js +92 -59
  57. package/dist/prod/core/async.js +522 -336
  58. package/dist/prod/core/attribution-hooks.js +3 -0
  59. package/dist/prod/core/constants.js +168 -44
  60. package/dist/prod/core/context.js +12 -16
  61. package/dist/prod/core/copies.js +10 -0
  62. package/dist/prod/core/core.js +1345 -627
  63. package/dist/prod/core/dev.js +123 -1
  64. package/dist/prod/core/effect.js +113 -80
  65. package/dist/prod/core/error-hooks.js +71 -0
  66. package/dist/prod/core/error.js +18 -4
  67. package/dist/prod/core/external.js +6 -7
  68. package/dist/prod/core/graph.js +119 -72
  69. package/dist/prod/core/heap.js +85 -109
  70. package/dist/prod/core/identities.js +62 -0
  71. package/dist/prod/core/invariants.js +1 -32
  72. package/dist/prod/core/lanes.js +931 -132
  73. package/dist/prod/core/owner.js +192 -129
  74. package/dist/prod/core/scheduler.js +948 -642
  75. package/dist/prod/core/truth.js +47 -0
  76. package/dist/prod/core/verdict.js +409 -420
  77. package/dist/prod/deferred.js +184 -0
  78. package/dist/prod/flatten.js +77 -0
  79. package/dist/prod/from.js +248 -0
  80. package/dist/prod/index.js +40 -16
  81. package/dist/prod/map.js +481 -211
  82. package/dist/prod/reveal.js +212 -0
  83. package/dist/prod/signals.js +401 -84
  84. package/dist/prod/store/affects.js +142 -0
  85. package/dist/prod/store/edges.js +39 -0
  86. package/dist/prod/store/hooks.js +16 -0
  87. package/dist/prod/store/index.js +27 -18
  88. package/dist/prod/store/optimistic.js +707 -0
  89. package/dist/prod/store/projection.js +331 -0
  90. package/dist/prod/store/reconcile.js +281 -0
  91. package/dist/prod/store/store.js +1952 -267
  92. package/dist/prod/store/storePath.js +1 -1
  93. package/dist/prod/store/target.js +43 -0
  94. package/dist/prod/store/types.js +83 -0
  95. package/dist/prod/store/utils.js +997 -138
  96. package/dist/types/affects-marks.d.ts +14 -0
  97. package/dist/types/affects.d.ts +12 -30
  98. package/dist/types/attribution.d.ts +19 -0
  99. package/dist/types/attribution.prod.d.ts +22 -0
  100. package/dist/types/boundaries.d.ts +99 -124
  101. package/dist/types/carved.d.ts +1 -0
  102. package/dist/types/core/action.d.ts +23 -9
  103. package/dist/types/core/async.d.ts +34 -6
  104. package/dist/types/core/attribution-costs.d.ts +35 -0
  105. package/dist/types/core/attribution-feedback.d.ts +133 -0
  106. package/dist/types/core/attribution-hooks.d.ts +307 -8
  107. package/dist/types/core/attribution-queries.d.ts +24 -0
  108. package/dist/types/core/attribution.d.ts +802 -72
  109. package/dist/types/core/constants.d.ts +195 -38
  110. package/dist/types/core/copies.d.ts +54 -0
  111. package/dist/types/core/core.d.ts +295 -65
  112. package/dist/types/core/dev.d.ts +509 -22
  113. package/dist/types/core/effect.d.ts +1 -1
  114. package/dist/types/core/error-hooks.d.ts +71 -0
  115. package/dist/types/core/error.d.ts +18 -1
  116. package/dist/types/core/graph.d.ts +40 -1
  117. package/dist/types/core/heap.d.ts +5 -5
  118. package/dist/types/core/identities.d.ts +17 -0
  119. package/dist/types/core/index.d.ts +11 -7
  120. package/dist/types/core/invariants.d.ts +1 -26
  121. package/dist/types/core/lanes.d.ts +74 -43
  122. package/dist/types/core/owner.d.ts +27 -9
  123. package/dist/types/core/scheduler.d.ts +353 -102
  124. package/dist/types/core/truth.d.ts +18 -0
  125. package/dist/types/core/types.d.ts +97 -71
  126. package/dist/types/core/verdict.d.ts +35 -0
  127. package/dist/types/deferred.d.ts +28 -0
  128. package/dist/types/flatten.d.ts +8 -0
  129. package/dist/types/from.d.ts +47 -0
  130. package/dist/types/index.d.ts +33 -7
  131. package/dist/types/map.d.ts +4 -2
  132. package/dist/types/reveal.d.ts +19 -0
  133. package/dist/types/signals.d.ts +196 -45
  134. package/dist/types/store/affects.d.ts +12 -0
  135. package/dist/types/store/edges.d.ts +9 -0
  136. package/dist/types/store/hooks.d.ts +62 -0
  137. package/dist/types/store/index.d.ts +42 -15
  138. package/dist/types/store/optimistic.d.ts +24 -45
  139. package/dist/types/store/projection.d.ts +17 -68
  140. package/dist/types/store/reconcile.d.ts +3 -46
  141. package/dist/types/store/store.d.ts +133 -136
  142. package/dist/types/store/storePath.d.ts +1 -1
  143. package/dist/types/store/target.d.ts +166 -0
  144. package/dist/types/store/types.d.ts +105 -0
  145. package/dist/types/store/utils.d.ts +188 -0
  146. package/package.json +31 -26
  147. package/dist/node.cjs +0 -8745
  148. package/dist/prod/core/optimistic.js +0 -265
  149. package/dist/prod/store/next/optimistic.js +0 -385
  150. package/dist/prod/store/next/projection.js +0 -172
  151. package/dist/prod/store/next/reconcile.js +0 -331
  152. package/dist/prod/store/next/store.js +0 -1499
  153. package/dist/prod/store/next/target.js +0 -20
  154. package/dist/types/core/optimistic.d.ts +0 -6
  155. package/dist/types/store/next/optimistic.d.ts +0 -20
  156. package/dist/types/store/next/projection.d.ts +0 -8
  157. package/dist/types/store/next/reconcile.d.ts +0 -3
  158. package/dist/types/store/next/store.d.ts +0 -77
  159. package/dist/types/store/next/target.d.ts +0 -117
  160. package/dist/types-cjs/affects.d.cts +0 -47
  161. package/dist/types-cjs/boundaries.d.cts +0 -175
  162. package/dist/types-cjs/core/action.d.cts +0 -64
  163. package/dist/types-cjs/core/async.d.cts +0 -21
  164. package/dist/types-cjs/core/attribution-hooks.d.cts +0 -52
  165. package/dist/types-cjs/core/attribution.d.cts +0 -186
  166. package/dist/types-cjs/core/constants.d.cts +0 -97
  167. package/dist/types-cjs/core/context.d.cts +0 -36
  168. package/dist/types-cjs/core/core.d.cts +0 -163
  169. package/dist/types-cjs/core/dev.d.cts +0 -81
  170. package/dist/types-cjs/core/effect.d.cts +0 -30
  171. package/dist/types-cjs/core/error.d.cts +0 -47
  172. package/dist/types-cjs/core/external.d.cts +0 -15
  173. package/dist/types-cjs/core/graph.d.cts +0 -6
  174. package/dist/types-cjs/core/heap.d.cts +0 -22
  175. package/dist/types-cjs/core/index.d.cts +0 -13
  176. package/dist/types-cjs/core/invariants.d.cts +0 -59
  177. package/dist/types-cjs/core/lanes.d.cts +0 -44
  178. package/dist/types-cjs/core/optimistic.d.cts +0 -6
  179. package/dist/types-cjs/core/owner.d.cts +0 -124
  180. package/dist/types-cjs/core/scheduler.d.cts +0 -180
  181. package/dist/types-cjs/core/types.d.cts +0 -192
  182. package/dist/types-cjs/core/verdict.d.cts +0 -2
  183. package/dist/types-cjs/index.d.cts +0 -10
  184. package/dist/types-cjs/map.d.cts +0 -69
  185. package/dist/types-cjs/package.json +0 -3
  186. package/dist/types-cjs/signals.d.cts +0 -624
  187. package/dist/types-cjs/store/index.d.cts +0 -19
  188. package/dist/types-cjs/store/next/optimistic.d.cts +0 -20
  189. package/dist/types-cjs/store/next/projection.d.cts +0 -8
  190. package/dist/types-cjs/store/next/reconcile.d.cts +0 -3
  191. package/dist/types-cjs/store/next/store.d.cts +0 -77
  192. package/dist/types-cjs/store/next/target.d.cts +0 -117
  193. package/dist/types-cjs/store/optimistic.d.cts +0 -45
  194. package/dist/types-cjs/store/projection.d.cts +0 -70
  195. package/dist/types-cjs/store/reconcile.d.cts +0 -46
  196. package/dist/types-cjs/store/store.d.cts +0 -138
  197. package/dist/types-cjs/store/storePath.d.cts +0 -58
  198. package/dist/types-cjs/store/utils.d.cts +0 -74
@@ -1,32 +1,26 @@
1
- import { REACTIVE_DISPOSED, REACTIVE_ZOMBIE, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, CONFIG_TRANSPARENT, defaultContext, CONFIG_AUTO_DISPOSE } from "./constants.js";
1
+ import { defaultContext, CONFIG_TRANSPARENT, REACTIVE_NONE, REACTIVE_DISPOSED, CONFIG_AUTO_DISPOSE, STATUS_PENDING, REACTIVE_FRAME_READ, REACTIVE_ZOMBIE } from "./constants.js";
2
2
 
3
- import { context, runWithOwner, pendingCheckActive, latestReadActive, tracking } from "./core.js";
3
+ import { context, runWithOwner, tracking } from "./core.js";
4
4
 
5
- import { clearDeps, unobserved } from "./graph.js";
5
+ import { unobserved, clearDeps } from "./graph.js";
6
6
 
7
- import { deleteFromHeap, queueFor, insertIntoHeap, insertIntoHeapHeight } from "./heap.js";
7
+ import { deleteFromHeap } from "./heap.js";
8
8
 
9
- import { GlobalQueue, zombieQueue, dirtyQueue, globalQueue } from "./scheduler.js";
9
+ import { globalQueue, pendingNodes, schedule, dirtyQueue } from "./scheduler.js";
10
10
 
11
- const PENDING_OWNER = {};
12
-
13
- // Dummy owner to trigger store's read() path
11
+ // L2 (step 2): the parked frame. A pass parks the previous frame's children
12
+ // and `_disposal` list on `_x._pendingFirstChild` / `_x._pendingDisposal`
13
+ // (`parkChildren`, core.ts); they keep reacting until the node's commit
14
+ // disposes them (`zombie` walk below) or the owner dies. `markDisposal` flags
15
+ // the parked subtree REACTIVE_ZOMBIE: the splice guard and the reawaken path
16
+ // key off it, and the heap defers a zombie's recompute past the seam (no
17
+ // second heap — the deferral list in scheduler.ts).
14
18
  function markDisposal(e) {
15
- let t = e.ke;
16
- while (t) {
17
- const e = t.ie;
18
- t.ie = e | REACTIVE_ZOMBIE;
19
- // migrate height-adjust entries too, not just recompute entries: every
20
- // `deleteFromHeap` call site picks the queue from the zombie flag, so a
21
- // node left physically linked in `dirtyQueue` after being zombified gets
22
- // unlinked from the wrong queue on dispose, corrupting the bucket and
23
- // livelocking the next `runHeap` that reaches it (#2759)
24
- if (e & (REACTIVE_IN_HEAP | REACTIVE_IN_HEAP_HEIGHT)) {
25
- deleteFromHeap(t, e & REACTIVE_ZOMBIE ? zombieQueue : dirtyQueue);
26
- if (e & REACTIVE_IN_HEAP) insertIntoHeap(t, zombieQueue); else insertIntoHeapHeight(t, zombieQueue);
27
- }
28
- markDisposal(t);
29
- t = t.Ve;
19
+ let n = e.pe;
20
+ while (n) {
21
+ n.R |= REACTIVE_ZOMBIE;
22
+ markDisposal(n);
23
+ n = n.Ge;
30
24
  }
31
25
  }
32
26
 
@@ -37,28 +31,59 @@ function dispose(e) {
37
31
  // left queued would be recomputed and resurrected by the next flush (#2983)
38
32
  // — dep unlinking, child disposal) is exactly unobserved()'s body; only
39
33
  // this flag distinguishes death from dormancy.
40
- e.T &= ~CONFIG_AUTO_DISPOSE;
34
+ e.l &= ~CONFIG_AUTO_DISPOSE;
41
35
  unobserved(e);
42
36
  }
43
37
 
44
- function disposeChildren(e, t = false, n) {
45
- const i = e.ie;
46
- if (i & REACTIVE_DISPOSED) return;
47
- if (t) {
48
- e.ie = i | REACTIVE_DISPOSED;
49
- // Companions are created detached and outlive their owner, but a verdict
50
- // must not: a disposed source can never settle, so an isPending companion
51
- // latched `true` here would hold a spinner forever (INV-9, the PR #2845
52
- // edge). Snap runs after the DISPOSED flag is set so the oracle reads
53
- // false, and notifies subscribers still watching the companion.
54
- const t = e;
55
- if (t.o?.Ge || t.o?.ge) GlobalQueue.un(t);
38
+ // The first error a cleanup threw, boxed so a thrown `undefined` still
39
+ // counts. A throwing cleanup stops neither its siblings nor the rest of the
40
+ // tree: every cleanup runs. The error is rethrown once the teardown is over:
41
+ // by a root's dispose call outside a flush (`disposeRootSelf`), otherwise when
42
+ // the flush ends (`flush`; `runCleanup` schedules one), so a flush is never
43
+ // left half-applied. A halt drops it: the error that halted is the one
44
+ // reported, and a stale one must not surface from a later, unrelated dispose.
45
+ let cleanupError;
46
+
47
+ /** Runs a cleanup or `unobserved` hook; a throw is collected, not raised. @internal */ function runCleanup(e) {
48
+ try {
49
+ e();
50
+ } catch (e) {
51
+ cleanupError ||= [ e ];
52
+ schedule();
53
+ }
54
+ }
55
+
56
+ /** Rethrows the error `runCleanup` collected, once; `drop` discards it. @internal */ function rethrowCleanupError(e) {
57
+ const n = cleanupError;
58
+ cleanupError = undefined;
59
+ if (n && !e) throw n[0];
60
+ }
61
+
62
+ function disposeChildren(e, n = false, r) {
63
+ const t = e.R;
64
+ if (t & REACTIVE_DISPOSED) return;
65
+ // A parked frame dies with its owner (#3024): the commit that would retire
66
+ // it never comes once the node is DISPOSED, so it drains here or its
67
+ // cleanups never run and it stays subscribed, rerunning in a torn-down
68
+ // tree (#3561). Death only (`self`): a re-pass leaves the frame reacting
69
+ // until the commit.
70
+ if (n && !r && e.T !== null && (e.T.me !== null || e.T.Le !== null)) disposeChildren(e, false, true);
71
+ if (n) {
72
+ e.R = t | REACTIVE_DISPOSED;
73
+ }
74
+ if (n && e.X) {
75
+ if (e.T !== null) e.T.Ee = null;
76
+ // A death that can unblock the future (L2, `futureBlocked`): a held
77
+ // flight's — a transaction's or a lane's (its `_x._transaction`) — or a
78
+ // stale reader's (REACTIVE_FRAME_READ — a frame that derived from one).
79
+ // An idle pending queue would also pin it until an unrelated flush.
80
+ // Nothing else re-examines a dead node, so make sure a seam comes.
81
+ if (e.m & STATUS_PENDING && e.T?.Re != null || t & REACTIVE_FRAME_READ || pendingNodes.length) schedule();
56
82
  }
57
- if (t && e.Se && e.o !== null) e.o.Ee = null;
58
- let o = n ? e.o?.qe ?? null : e.ke;
59
- while (o) {
60
- const e = o.Ve;
61
- const t = o;
83
+ let l = r ? e.T.me : e.pe;
84
+ if (r) e.T.me = null; else e.pe = null;
85
+ while (l) {
86
+ const e = l;
62
87
  // Owner teardown is death regardless of the child's own lifecycle
63
88
  // (#3024): strip AUTO_DISPOSE so a post-disposal read freezes at the
64
89
  // last committed value instead of reawakening in a torn-down tree.
@@ -66,7 +91,7 @@ function disposeChildren(e, t = false, n) {
66
91
  // disposeChildren call early-returns on REACTIVE_DISPOSED) die too.
67
92
  // Only unobserved()'s own node keeps its dormancy — it is never in
68
93
  // this loop; its children are rebuilt fresh on reawaken.
69
- t.T &= ~CONFIG_AUTO_DISPOSE;
94
+ e.l &= ~CONFIG_AUTO_DISPOSE;
70
95
  // Heap removal must not be gated on `_deps`: a dependency-free
71
96
  // computation queued by refresh() has a null dep list but still sits in
72
97
  // the dirty heap, and left there the post-disposal flush recomputes it —
@@ -74,58 +99,78 @@ function disposeChildren(e, t = false, n) {
74
99
  // comes back to life (post-unmount runs, leaked cleanups, #2983).
75
100
  // deleteFromHeap self-guards on the in-heap flags (and tolerates plain
76
101
  // Owners, whose _flags is undefined), so no gate here.
77
- deleteFromHeap(t, queueFor(t));
78
- clearDeps(t);
79
- disposeChildren(o, true);
80
- o = e;
81
- }
82
- if (n) {
83
- if (e.o !== null) e.o.qe = null;
84
- } else {
85
- e.ke = null;
86
- e.Me = 0;
102
+ deleteFromHeap(e, dirtyQueue);
103
+ clearDeps(e);
104
+ // The chain is detached above so a node a cleanup links mid-drain lands
105
+ // on the fresh head and survives. Pointing each drained child's prev at
106
+ // itself routes its later splice onto the detached chain, never the head,
107
+ // and keeps the dev owner-chain-head invariant honest for those children.
108
+ l.we = l;
109
+ disposeChildren(l, true);
110
+ // Read after, not before: a sibling this disposal made dormant spliced
111
+ // itself out of the detached chain, and a cleanup may then have linked
112
+ // it at the fresh head, which rewrote the `_nextSibling` a pre-read
113
+ // would still be holding.
114
+ l = l.Ge;
87
115
  }
116
+ if (!r) e.be = 0;
88
117
  // O(1) splice out of parent's chain on individual dispose. Skipped during
89
- // batch dispose (parent already disposed) and zombie disposal (node sits on
90
- // parent's _pendingFirstChild). We leave node._nextSibling intact so outer
91
- // walks that already advanced past us still reach later siblings.
92
- if (t && !n && !(i & REACTIVE_ZOMBIE) && e.ve !== null && !(e.ve.ie & REACTIVE_DISPOSED)) {
93
- const t = e.ct;
94
- const n = e.Ve;
95
- if (t !== null) t.Ve = n; else e.ve.ke = n;
96
- if (n !== null) n.ct = t;
97
- e.ct = null;
118
+ // batch dispose (parent already disposed) and for a zombie (it sits on the
119
+ // parent's parked frame, drained whole by the commit). We leave
120
+ // node._nextSibling intact so outer walks that already advanced past us
121
+ // still reach later siblings.
122
+ if (n && !r && !(t & REACTIVE_ZOMBIE) && e._parent !== null && !(e._parent.R & REACTIVE_DISPOSED)) {
123
+ const n = e.we;
124
+ const r = e.Ge;
125
+ if (n !== null) n.Ge = r; else e._parent.pe = r;
126
+ if (r !== null) r.we = n;
127
+ e.we = null;
98
128
  }
99
- runDisposal(e, n);
129
+ runDisposal(e, r);
100
130
  // Final effect-returned cleanup fires at true disposal, after `_disposal`
101
131
  // to mirror rerun ordering (compute-phase teardown first, cleanup last).
102
- if (t && e.At) {
103
- const t = e.At;
104
- e.At = undefined;
105
- t();
132
+ if (n && e.ze) {
133
+ const n = e.ze;
134
+ e.ze = undefined;
135
+ runCleanup(n);
106
136
  }
107
137
  }
108
138
 
109
- function runDisposal(e, t) {
110
- let n = t ? e.o?.We : e.he;
111
- if (!n) return;
112
- if (Array.isArray(n)) {
113
- for (let e = 0; e < n.length; e++) {
114
- const t = n[e];
115
- t.call(t);
139
+ function linkChild(e, n) {
140
+ const r = e.pe;
141
+ n.we = null;
142
+ n.Ge = r;
143
+ if (r !== null) r.we = n;
144
+ e.pe = n;
145
+ }
146
+
147
+ function runDisposal(e, n) {
148
+ // Detach the list BEFORE running it (#3601), as `_cleanup` is (#2813). A
149
+ // cleanup that disposes an ancestor re-enters this node through the death
150
+ // walk while the loop is still running; with the list still attached, that
151
+ // walk ran every entry a second time. Detached, the re-entrant drain finds
152
+ // nothing. The same shape latched a throwing cleanup: the list survived the
153
+ // throw and every later drain re-ran and re-threw it.
154
+ const r = n ? e.T.Le : e.ke;
155
+ if (!r) return;
156
+ if (n) e.T.Le = null; else e.ke = null;
157
+ if (Array.isArray(r)) {
158
+ // Unwind order (#3572, restores 1.x #1562): later registrations run
159
+ // before earlier ones. Children have already been disposed by the caller,
160
+ // so with LIFO a body that registers cleanup before creating its children
161
+ // tears down after them — the same order a per-component owner gives.
162
+ for (let e = r.length - 1; e >= 0; e--) {
163
+ runCleanup(r[e]);
116
164
  }
117
165
  } else {
118
- n.call(n);
166
+ runCleanup(r);
119
167
  }
120
- if (t) {
121
- if (e.o !== null) e.o.We = null;
122
- } else e.he = null;
123
168
  }
124
169
 
125
- function childId(e, t) {
126
- let n = e;
127
- while (n.T & CONFIG_TRANSPARENT && n.ve) n = n.ve;
128
- if (n.id != null) return formatId(n.id, t ? n.Me++ : n.Me);
170
+ function childId(e, n) {
171
+ let r = e;
172
+ while (r.l & CONFIG_TRANSPARENT && r._parent) r = r._parent;
173
+ if (r.id != null) return formatId(r.id, n ? r.be++ : r.be);
129
174
  throw new Error("");
130
175
  }
131
176
 
@@ -142,8 +187,8 @@ function childId(e, t) {
142
187
  * The id a freshly-created node inherits: an explicit `options.id` wins;
143
188
  * transparent nodes share their parent's id; otherwise the parent's next
144
189
  * child id is consumed (or `undefined` outside an id-carrying tree).
145
- */ function inheritId(e, t, n) {
146
- return e?.id ?? (t ? n?.id : n?.id != null ? getNextChildId(n) : undefined);
190
+ */ function inheritId(e, n, r) {
191
+ return e?.id ?? (n ? r?.id : r?.id != null ? getNextChildId(r) : undefined);
147
192
  }
148
193
 
149
194
  /**
@@ -155,9 +200,9 @@ function childId(e, t) {
155
200
  return childId(e, false);
156
201
  }
157
202
 
158
- function formatId(e, t) {
159
- const n = t.toString(36), i = n.length - 1;
160
- return e + (i ? String.fromCharCode(64 + i) : "") + n;
203
+ function formatId(e, n) {
204
+ const r = n.toString(36), t = r.length - 1;
205
+ return e + (t ? String.fromCharCode(64 + t) : "") + r;
161
206
  }
162
207
 
163
208
  /**
@@ -176,7 +221,6 @@ function formatId(e, t) {
176
221
  * }
177
222
  * ```
178
223
  */ function getObserver() {
179
- if (pendingCheckActive || latestReadActive) return PENDING_OWNER;
180
224
  return tracking ? context : null;
181
225
  }
182
226
 
@@ -203,17 +247,19 @@ function formatId(e, t) {
203
247
  /**
204
248
  * Low-level: registers `fn` as a disposal callback on the current owner.
205
249
  * Most code should use `onCleanup()` from `solid-js`, which adds dev-mode
206
- * checks. `cleanup()` is the unchecked primitive used by internals.
207
- */ function cleanup(e) {
208
- if (!context) return e;
209
- if (!context.he) context.he = e; else if (Array.isArray(context.he)) context.he.push(e); else context.he = [ context.he, e ];
250
+ * checks. `cleanup()` is the unchecked primitive used by internals; `owner`
251
+ * (internal) targets a specific owner's disposal list instead of the ambient
252
+ * one.
253
+ */ function cleanup(e, n = context) {
254
+ if (!n) return e;
255
+ if (!n.ke) n.ke = e; else if (Array.isArray(n.ke)) n.ke.push(e); else n.ke = [ n.ke, e ];
210
256
  return e;
211
257
  }
212
258
 
213
259
  /**
214
- * Returns `true` if the owner has been disposed (or marked zombie pending
215
- * disposal). Pair with a captured owner to bail out of late callbacks whose
216
- * surrounding component already unmounted.
260
+ * Returns `true` if the owner has been disposed. Pair with a captured owner
261
+ * to bail out of late callbacks whose surrounding component already
262
+ * unmounted.
217
263
  *
218
264
  * @example
219
265
  * ```ts
@@ -226,11 +272,14 @@ function formatId(e, t) {
226
272
  * }
227
273
  * ```
228
274
  */ function isDisposed(e) {
229
- return !!(e.ie & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE));
275
+ return !!(e.R & REACTIVE_DISPOSED);
230
276
  }
231
277
 
232
278
  function disposeRootSelf(e = true) {
233
279
  disposeChildren(this, e);
280
+ // Outside a flush the dispose call rethrows; inside one, the flush does.
281
+ // Kept out of `disposeChildren`, whose hot path it slows.
282
+ if (cleanupError && !globalQueue.Y) rethrowCleanupError();
234
283
  }
235
284
 
236
285
  /**
@@ -241,41 +290,47 @@ function disposeRootSelf(e = true) {
241
290
  *
242
291
  * @internal
243
292
  */ function createOwner(e) {
244
- const t = context;
245
- const n = e?.transparent ?? false;
246
- const i = {
247
- id: inheritId(e, n, t),
248
- T: n ? CONFIG_TRANSPARENT : 0,
249
- Ct: true,
250
- Ot: t?.Ct ? t.Ot : t,
293
+ const n = context;
294
+ const r = e?.transparent ?? false;
295
+ // Prod and observe boilerplates (see core.ts computed()). The observe
296
+ // literal carries the `_name` and `_loc` slots the rendering layer fills
297
+ // with the component label and JSX site (`owner._name = "<App>"`,
298
+ // `owner._loc = "src/App.tsx:3:5"`) — slots, so labelling a root is a
299
+ // plain store rather than a shape fork between labelled and plain roots.
300
+ const t = {
301
+ id: inheritId(e, r, n),
302
+ l: r ? CONFIG_TRANSPARENT : 0,
303
+ // Present from birth: disposeChildren marks death here on roots too, and
304
+ // a first store of a missing property is a shape transition plus a
305
+ // properties-store allocation per root — on a 100×4 table teardown that
306
+ // was 12 KB and most of disposal's time (2026-10-17).
307
+ R: REACTIVE_NONE,
308
+ Qe: true,
309
+ Ke: n?.Qe ? n.Ke : n,
310
+ pe: null,
311
+ Ge: null,
312
+ we: null,
251
313
  ke: null,
252
- Ve: null,
253
- ct: null,
254
- he: null,
255
- C: t?.C ?? globalQueue,
256
- we: t?.we || defaultContext,
257
- Me: 0,
258
- o: null,
259
- ve: t,
314
+ A: n?.A || defaultContext,
315
+ be: 0,
316
+ T: null,
317
+ _parent: n,
260
318
  dispose: disposeRootSelf
261
319
  };
262
- if (t) {
263
- const e = t.ke;
264
- if (e === null) {
265
- t.ke = i;
266
- } else {
267
- i.Ve = e;
268
- e.ct = i;
269
- t.ke = i;
270
- }
271
- }
272
- return i;
320
+ if (n) linkChild(n, t);
321
+ return t;
273
322
  }
274
323
 
275
324
  /**
276
- * Creates a detached reactive root. The callback receives a `dispose()`
277
- * function which, when called, tears down every signal, memo, effect, and
278
- * `onCleanup` registered inside the root.
325
+ * Creates a reactive root — an owner scope with its own `dispose()`. A root
326
+ * created inside an existing owner is owned by it and is disposed when the
327
+ * parent is disposed; call `dispose()` to tear it down earlier. To create a
328
+ * root that outlives its creator, detach explicitly:
329
+ * `runWithOwner(null, () => createRoot(...))`. Pass `id` to seed hydration
330
+ * ids for the tree it owns.
331
+ *
332
+ * `dispose()` tears down every signal, memo, effect, and `onCleanup`
333
+ * registered inside the root.
279
334
  *
280
335
  * Use this to host long-lived reactive scopes outside of a component (custom
281
336
  * controllers, app bootstrapping, tests). Inside a component, prefer
@@ -283,6 +338,7 @@ function disposeRootSelf(e = true) {
283
338
  *
284
339
  * @example
285
340
  * ```ts
341
+ * // At module level there is no owner, so this root lives until disposed.
286
342
  * const dispose = createRoot(dispose => {
287
343
  * const [n, setN] = createSignal(0);
288
344
  * createEffect(() => n(), value => console.log(value));
@@ -292,12 +348,19 @@ function disposeRootSelf(e = true) {
292
348
  *
293
349
  * // Later, to tear everything down:
294
350
  * dispose();
351
+ *
352
+ * // Inside an owner (component, effect, another root), detach explicitly
353
+ * // if the root must outlive its creator:
354
+ * const detached = runWithOwner(null, () => createRoot(d => d));
295
355
  * ```
296
356
  *
297
- * @description https://docs.solidjs.com/reference/reactive-utilities/create-root
298
- */ function createRoot(e, t) {
299
- const n = createOwner(t);
300
- return runWithOwner(n, () => e(() => n.dispose()));
357
+ * @description https://v2.solidjs.com/reference/solid-js/advanced/owner-introspection/create-root
358
+ */ function createRoot(e, n) {
359
+ const r = createOwner(n);
360
+ // A bound function, not an arrow: an arrow would share this scope, which
361
+ // holds `init`, so keeping `dispose` would keep `init` and all it closes
362
+ // over alive (#4095).
363
+ return runWithOwner(r, () => e(disposeRootSelf.bind(r, true)));
301
364
  }
302
365
 
303
- export { cleanup, createOwner, createRoot, dispose, disposeChildren, getNextChildId, getObserver, getOwner, inheritId, isDisposed, markDisposal, peekNextChildId };
366
+ export { cleanup, createOwner, createRoot, dispose, disposeChildren, getNextChildId, getObserver, getOwner, inheritId, isDisposed, linkChild, markDisposal, peekNextChildId, rethrowCleanupError, runCleanup };