@solidjs/signals 2.0.0-rc.15 → 2.0.0-rc.17

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 (90) hide show
  1. package/dist/dev-shared.js +2268 -1734
  2. package/dist/dev.attribution.js +75 -52
  3. package/dist/dev.js +1348 -658
  4. package/dist/observe/affects-marks.js +86 -0
  5. package/dist/observe/affects.js +22 -73
  6. package/dist/observe/boundaries.js +192 -190
  7. package/dist/observe/core/async.js +177 -131
  8. package/dist/observe/core/attribution-queries.js +1 -1
  9. package/dist/observe/core/attribution.js +176 -159
  10. package/dist/observe/core/constants.js +17 -3
  11. package/dist/observe/core/context.js +5 -3
  12. package/dist/observe/core/core.js +520 -364
  13. package/dist/observe/core/dev.js +66 -38
  14. package/dist/observe/core/effect.js +50 -48
  15. package/dist/observe/core/error.js +5 -3
  16. package/dist/observe/core/external.js +5 -6
  17. package/dist/observe/core/graph.js +60 -44
  18. package/dist/observe/core/heap.js +33 -33
  19. package/dist/observe/core/identities.js +62 -0
  20. package/dist/observe/core/lanes.js +238 -260
  21. package/dist/observe/core/owner.js +117 -79
  22. package/dist/observe/core/scheduler.js +381 -217
  23. package/dist/observe/core/truth.js +45 -0
  24. package/dist/observe/core/verdict.js +91 -50
  25. package/dist/observe/deferred.js +184 -0
  26. package/dist/observe/index.js +7 -1
  27. package/dist/observe/map.js +345 -260
  28. package/dist/observe/reveal.js +76 -76
  29. package/dist/observe/signals.js +47 -31
  30. package/dist/observe/store/affects.js +18 -18
  31. package/dist/observe/store/edges.js +39 -0
  32. package/dist/observe/store/optimistic.js +146 -100
  33. package/dist/observe/store/projection.js +68 -55
  34. package/dist/observe/store/store.js +180 -118
  35. package/dist/prod/affects-marks.js +86 -0
  36. package/dist/prod/affects.js +23 -72
  37. package/dist/prod/boundaries.js +156 -154
  38. package/dist/prod/core/async.js +173 -127
  39. package/dist/prod/core/constants.js +17 -3
  40. package/dist/prod/core/context.js +5 -3
  41. package/dist/prod/core/core.js +495 -343
  42. package/dist/prod/core/effect.js +70 -68
  43. package/dist/prod/core/error.js +5 -3
  44. package/dist/prod/core/external.js +5 -6
  45. package/dist/prod/core/graph.js +60 -44
  46. package/dist/prod/core/heap.js +38 -38
  47. package/dist/prod/core/identities.js +62 -0
  48. package/dist/prod/core/lanes.js +230 -252
  49. package/dist/prod/core/owner.js +114 -77
  50. package/dist/prod/core/scheduler.js +327 -163
  51. package/dist/prod/core/truth.js +45 -0
  52. package/dist/prod/core/verdict.js +90 -49
  53. package/dist/prod/deferred.js +182 -0
  54. package/dist/prod/index.js +7 -1
  55. package/dist/prod/map.js +360 -275
  56. package/dist/prod/reveal.js +76 -76
  57. package/dist/prod/signals.js +42 -26
  58. package/dist/prod/store/affects.js +6 -6
  59. package/dist/prod/store/edges.js +39 -0
  60. package/dist/prod/store/optimistic.js +143 -98
  61. package/dist/prod/store/projection.js +25 -19
  62. package/dist/prod/store/store.js +157 -100
  63. package/dist/types/affects-marks.d.ts +14 -0
  64. package/dist/types/boundaries.d.ts +1 -1
  65. package/dist/types/core/async.d.ts +10 -0
  66. package/dist/types/core/constants.d.ts +13 -1
  67. package/dist/types/core/core.d.ts +54 -5
  68. package/dist/types/core/dev.d.ts +51 -13
  69. package/dist/types/core/error.d.ts +9 -1
  70. package/dist/types/core/graph.d.ts +11 -0
  71. package/dist/types/core/identities.d.ts +17 -0
  72. package/dist/types/core/index.d.ts +2 -1
  73. package/dist/types/core/lanes.d.ts +2 -14
  74. package/dist/types/core/owner.d.ts +9 -3
  75. package/dist/types/core/scheduler.d.ts +70 -32
  76. package/dist/types/core/truth.d.ts +16 -0
  77. package/dist/types/core/types.d.ts +21 -0
  78. package/dist/types/core/verdict.d.ts +32 -0
  79. package/dist/types/deferred.d.ts +28 -0
  80. package/dist/types/index.d.ts +6 -0
  81. package/dist/types/map.d.ts +2 -2
  82. package/dist/types/signals.d.ts +28 -7
  83. package/dist/types/store/edges.d.ts +9 -0
  84. package/dist/types/store/index.d.ts +10 -3
  85. package/dist/types/store/optimistic.d.ts +3 -2
  86. package/dist/types/store/projection.d.ts +4 -3
  87. package/dist/types/store/store.d.ts +1 -1
  88. package/dist/types/store/target.d.ts +16 -8
  89. package/dist/types/store/types.d.ts +14 -0
  90. package/package.json +1 -1
@@ -1,12 +1,12 @@
1
- import { defaultContext, CONFIG_TRANSPARENT, REACTIVE_DISPOSED, STATUS_PENDING, REACTIVE_FRAME_READ, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE } 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
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
7
  import { deleteFromHeap } from "./heap.js";
8
8
 
9
- import { schedule, dirtyQueue } from "./scheduler.js";
9
+ import { globalQueue, schedule, dirtyQueue } from "./scheduler.js";
10
10
 
11
11
  // L2 (step 2): the parked frame. A pass parks the previous frame's children
12
12
  // and `_disposal` list on `_x._pendingFirstChild` / `_x._pendingDisposal`
@@ -16,11 +16,11 @@ import { schedule, dirtyQueue } from "./scheduler.js";
16
16
  // key off it, and the heap defers a zombie's recompute past the seam (no
17
17
  // second heap — the deferral list in scheduler.ts).
18
18
  function markDisposal(e) {
19
- let n = e.Ve;
19
+ let n = e.De;
20
20
  while (n) {
21
21
  n.R |= REACTIVE_ZOMBIE;
22
22
  markDisposal(n);
23
- n = n.ge;
23
+ n = n.pe;
24
24
  }
25
25
  }
26
26
 
@@ -31,32 +31,56 @@ function dispose(e) {
31
31
  // left queued would be recomputed and resurrected by the next flush (#2983)
32
32
  // — dep unlinking, child disposal) is exactly unobserved()'s body; only
33
33
  // this flag distinguishes death from dormancy.
34
- e.i &= ~CONFIG_AUTO_DISPOSE;
34
+ e.l &= ~CONFIG_AUTO_DISPOSE;
35
35
  unobserved(e);
36
36
  }
37
37
 
38
- function disposeChildren(e, n = false, t) {
39
- const i = e.R;
40
- if (i & REACTIVE_DISPOSED) return;
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;
41
65
  // A parked frame dies with its owner (#3024): the commit that would retire
42
66
  // it never comes once the node is DISPOSED, so it drains here or its
43
67
  // cleanups never run and it stays subscribed, rerunning in a torn-down
44
68
  // tree (#3561). Death only (`self`): a re-pass leaves the frame reacting
45
69
  // until the commit.
46
- if (n && !t && e.m !== null && (e.m.Qe !== null || e.m.ve !== null)) disposeChildren(e, false, true);
70
+ if (n && !r && e.T !== null && (e.T.Ue !== null || e.T.me !== null)) disposeChildren(e, false, true);
47
71
  if (n) {
48
- e.R = i | REACTIVE_DISPOSED;
72
+ e.R = t | REACTIVE_DISPOSED;
49
73
  }
50
- if (n && e.ee) {
51
- if (e.m !== null) e.m.Te = null;
74
+ if (n && e.X) {
75
+ if (e.T !== null) e.T.Se = null;
52
76
  // A death that can unblock the future (L2, `futureBlocked`): a held
53
77
  // flight's — a transaction's or a lane's (its `_x._transaction`) — or a
54
78
  // stale reader's (REACTIVE_FRAME_READ — a frame that derived from one).
55
79
  // Nothing else re-examines a dead node, so make sure a seam comes.
56
- if (e.S & STATUS_PENDING && e.m?.He != null || i & REACTIVE_FRAME_READ) schedule();
80
+ if (e.m & STATUS_PENDING && e.T?.Re != null || t & REACTIVE_FRAME_READ) schedule();
57
81
  }
58
- let l = t ? e.m.Qe : e.Ve;
59
- if (t) e.m.Qe = null; else e.Ve = null;
82
+ let l = r ? e.T.Ue : e.De;
83
+ if (r) e.T.Ue = null; else e.De = null;
60
84
  while (l) {
61
85
  const e = l;
62
86
  // Owner teardown is death regardless of the child's own lifecycle
@@ -66,7 +90,7 @@ function disposeChildren(e, n = false, t) {
66
90
  // disposeChildren call early-returns on REACTIVE_DISPOSED) die too.
67
91
  // Only unobserved()'s own node keeps its dormancy — it is never in
68
92
  // this loop; its children are rebuilt fresh on reawaken.
69
- e.i &= ~CONFIG_AUTO_DISPOSE;
93
+ e.l &= ~CONFIG_AUTO_DISPOSE;
70
94
  // Heap removal must not be gated on `_deps`: a dependency-free
71
95
  // computation queued by refresh() has a null dep list but still sits in
72
96
  // the dirty heap, and left there the post-disposal flush recomputes it —
@@ -80,43 +104,43 @@ function disposeChildren(e, n = false, t) {
80
104
  // on the fresh head and survives. Pointing each drained child's prev at
81
105
  // itself routes its later splice onto the detached chain, never the head,
82
106
  // and keeps the dev owner-chain-head invariant honest for those children.
83
- l.Ke = l;
107
+ l.Ze = l;
84
108
  disposeChildren(l, true);
85
109
  // Read after, not before: a sibling this disposal made dormant spliced
86
110
  // itself out of the detached chain, and a cleanup may then have linked
87
111
  // it at the fresh head, which rewrote the `_nextSibling` a pre-read
88
112
  // would still be holding.
89
- l = l.ge;
113
+ l = l.pe;
90
114
  }
91
- if (!t) e.Be = 0;
115
+ if (!r) e.ye = 0;
92
116
  // O(1) splice out of parent's chain on individual dispose. Skipped during
93
117
  // batch dispose (parent already disposed) and for a zombie (it sits on the
94
118
  // parent's parked frame, drained whole by the commit). We leave
95
119
  // node._nextSibling intact so outer walks that already advanced past us
96
120
  // still reach later siblings.
97
- if (n && !t && !(i & REACTIVE_ZOMBIE) && e._parent !== null && !(e._parent.R & REACTIVE_DISPOSED)) {
98
- const n = e.Ke;
99
- const t = e.ge;
100
- if (n !== null) n.ge = t; else e._parent.Ve = t;
101
- if (t !== null) t.Ke = n;
102
- e.Ke = null;
121
+ if (n && !r && !(t & REACTIVE_ZOMBIE) && e._parent !== null && !(e._parent.R & REACTIVE_DISPOSED)) {
122
+ const n = e.Ze;
123
+ const r = e.pe;
124
+ if (n !== null) n.pe = r; else e._parent.De = r;
125
+ if (r !== null) r.Ze = n;
126
+ e.Ze = null;
103
127
  }
104
- runDisposal(e, t);
128
+ runDisposal(e, r);
105
129
  // Final effect-returned cleanup fires at true disposal, after `_disposal`
106
130
  // to mirror rerun ordering (compute-phase teardown first, cleanup last).
107
- if (n && e.st) {
108
- const n = e.st;
109
- e.st = undefined;
110
- n();
131
+ if (n && e.Je) {
132
+ const n = e.Je;
133
+ e.Je = undefined;
134
+ runCleanup(n);
111
135
  }
112
136
  }
113
137
 
114
138
  function linkChild(e, n) {
115
- const t = e.Ve;
116
- n.Ke = null;
117
- n.ge = t;
118
- if (t !== null) t.Ke = n;
119
- e.Ve = n;
139
+ const r = e.De;
140
+ n.Ze = null;
141
+ n.pe = r;
142
+ if (r !== null) r.Ze = n;
143
+ e.De = n;
120
144
  }
121
145
 
122
146
  function runDisposal(e, n) {
@@ -126,27 +150,26 @@ function runDisposal(e, n) {
126
150
  // walk ran every entry a second time. Detached, the re-entrant drain finds
127
151
  // nothing. The same shape latched a throwing cleanup: the list survived the
128
152
  // throw and every later drain re-ran and re-threw it.
129
- const t = n ? e.m.ve : e.Re;
130
- if (!t) return;
131
- if (n) e.m.ve = null; else e.Re = null;
132
- if (Array.isArray(t)) {
153
+ const r = n ? e.T.me : e.He;
154
+ if (!r) return;
155
+ if (n) e.T.me = null; else e.He = null;
156
+ if (Array.isArray(r)) {
133
157
  // Unwind order (#3572, restores 1.x #1562): later registrations run
134
158
  // before earlier ones. Children have already been disposed by the caller,
135
159
  // so with LIFO a body that registers cleanup before creating its children
136
160
  // tears down after them — the same order a per-component owner gives.
137
- for (let e = t.length - 1; e >= 0; e--) {
138
- const n = t[e];
139
- n.call(n);
161
+ for (let e = r.length - 1; e >= 0; e--) {
162
+ runCleanup(r[e]);
140
163
  }
141
164
  } else {
142
- t.call(t);
165
+ runCleanup(r);
143
166
  }
144
167
  }
145
168
 
146
169
  function childId(e, n) {
147
- let t = e;
148
- while (t.i & CONFIG_TRANSPARENT && t._parent) t = t._parent;
149
- if (t.id != null) return formatId(t.id, n ? t.Be++ : t.Be);
170
+ let r = e;
171
+ while (r.l & CONFIG_TRANSPARENT && r._parent) r = r._parent;
172
+ if (r.id != null) return formatId(r.id, n ? r.ye++ : r.ye);
150
173
  throw new Error("");
151
174
  }
152
175
 
@@ -163,8 +186,8 @@ function childId(e, n) {
163
186
  * The id a freshly-created node inherits: an explicit `options.id` wins;
164
187
  * transparent nodes share their parent's id; otherwise the parent's next
165
188
  * child id is consumed (or `undefined` outside an id-carrying tree).
166
- */ function inheritId(e, n, t) {
167
- return e?.id ?? (n ? t?.id : t?.id != null ? getNextChildId(t) : undefined);
189
+ */ function inheritId(e, n, r) {
190
+ return e?.id ?? (n ? r?.id : r?.id != null ? getNextChildId(r) : undefined);
168
191
  }
169
192
 
170
193
  /**
@@ -177,8 +200,8 @@ function childId(e, n) {
177
200
  }
178
201
 
179
202
  function formatId(e, n) {
180
- const t = n.toString(36), i = t.length - 1;
181
- return e + (i ? String.fromCharCode(64 + i) : "") + t;
203
+ const r = n.toString(36), t = r.length - 1;
204
+ return e + (t ? String.fromCharCode(64 + t) : "") + r;
182
205
  }
183
206
 
184
207
  /**
@@ -223,10 +246,12 @@ function formatId(e, n) {
223
246
  /**
224
247
  * Low-level: registers `fn` as a disposal callback on the current owner.
225
248
  * Most code should use `onCleanup()` from `solid-js`, which adds dev-mode
226
- * checks. `cleanup()` is the unchecked primitive used by internals.
227
- */ function cleanup(e) {
228
- if (!context) return e;
229
- if (!context.Re) context.Re = e; else if (Array.isArray(context.Re)) context.Re.push(e); else context.Re = [ context.Re, e ];
249
+ * checks. `cleanup()` is the unchecked primitive used by internals; `owner`
250
+ * (internal) targets a specific owner's disposal list instead of the ambient
251
+ * one.
252
+ */ function cleanup(e, n = context) {
253
+ if (!n) return e;
254
+ if (!n.He) n.He = e; else if (Array.isArray(n.He)) n.He.push(e); else n.He = [ n.He, e ];
230
255
  return e;
231
256
  }
232
257
 
@@ -251,6 +276,9 @@ function formatId(e, n) {
251
276
 
252
277
  function disposeRootSelf(e = true) {
253
278
  disposeChildren(this, e);
279
+ // Outside a flush the dispose call rethrows; inside one, the flush does.
280
+ // Kept out of `disposeChildren`, whose hot path it slows.
281
+ if (cleanupError && !globalQueue.q) rethrowCleanupError();
254
282
  }
255
283
 
256
284
  /**
@@ -262,28 +290,34 @@ function disposeRootSelf(e = true) {
262
290
  * @internal
263
291
  */ function createOwner(e) {
264
292
  const n = context;
265
- const t = e?.transparent ?? false;
293
+ const r = e?.transparent ?? false;
266
294
  // Prod and observe boilerplates (see core.ts computed()). The observe
267
- // literal carries the `_name` slot the rendering layer fills with the
268
- // component label (`owner._name = "<App>"`) — a slot, so labelling a root
269
- // is a plain store rather than a shape fork between labelled and plain roots.
270
- const i = {
271
- id: inheritId(e, t, n),
272
- i: t ? CONFIG_TRANSPARENT : 0,
273
- $e: true,
274
- et: n?.$e ? n.et : n,
275
- Ve: null,
276
- ge: null,
277
- Ke: null,
278
- Re: null,
279
- D: n?.D || defaultContext,
280
- Be: 0,
281
- m: null,
295
+ // literal carries the `_name` and `_loc` slots the rendering layer fills
296
+ // with the component label and JSX site (`owner._name = "<App>"`,
297
+ // `owner._loc = "src/App.tsx:3:5"`) — slots, so labelling a root is a
298
+ // plain store rather than a shape fork between labelled and plain roots.
299
+ const t = {
300
+ id: inheritId(e, r, n),
301
+ l: r ? CONFIG_TRANSPARENT : 0,
302
+ // Present from birth: disposeChildren marks death here on roots too, and
303
+ // a first store of a missing property is a shape transition plus a
304
+ // properties-store allocation per root — on a 100×4 table teardown that
305
+ // was 12 KB and most of disposal's time (2026-10-17).
306
+ R: REACTIVE_NONE,
307
+ Qe: true,
308
+ Ye: n?.Qe ? n.Ye : n,
309
+ De: null,
310
+ pe: null,
311
+ Ze: null,
312
+ He: null,
313
+ A: n?.A || defaultContext,
314
+ ye: 0,
315
+ T: null,
282
316
  _parent: n,
283
317
  dispose: disposeRootSelf
284
318
  };
285
- if (n) linkChild(n, i);
286
- return i;
319
+ if (n) linkChild(n, t);
320
+ return t;
287
321
  }
288
322
 
289
323
  /**
@@ -319,10 +353,13 @@ function disposeRootSelf(e = true) {
319
353
  * const detached = runWithOwner(null, () => createRoot(d => d));
320
354
  * ```
321
355
  *
322
- * @description https://docs.solidjs.com/reference/reactive-utilities/create-root
356
+ * @description https://v2.solidjs.com/reference/solid-js/advanced/owner-introspection/create-root
323
357
  */ function createRoot(e, n) {
324
- const t = createOwner(n);
325
- return runWithOwner(t, () => e(() => t.dispose()));
358
+ const r = createOwner(n);
359
+ // A bound function, not an arrow: an arrow would share this scope, which
360
+ // holds `init`, so keeping `dispose` would keep `init` and all it closes
361
+ // over alive (#4095).
362
+ return runWithOwner(r, () => e(disposeRootSelf.bind(r, true)));
326
363
  }
327
364
 
328
- export { cleanup, createOwner, createRoot, dispose, disposeChildren, getNextChildId, getObserver, getOwner, inheritId, isDisposed, linkChild, markDisposal, peekNextChildId };
365
+ export { cleanup, createOwner, createRoot, dispose, disposeChildren, getNextChildId, getObserver, getOwner, inheritId, isDisposed, linkChild, markDisposal, peekNextChildId, rethrowCleanupError, runCleanup };