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