@solidjs/signals 2.0.0-rc.0 → 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 (70) hide show
  1. package/README.md +1 -1
  2. package/dist/dev.js +4287 -2803
  3. package/dist/node.cjs +4563 -3756
  4. package/dist/prod/affects.js +16 -16
  5. package/dist/prod/boundaries.js +90 -90
  6. package/dist/prod/core/action.js +3 -3
  7. package/dist/prod/core/async.js +234 -178
  8. package/dist/prod/core/constants.js +39 -1
  9. package/dist/prod/core/core.js +362 -243
  10. package/dist/prod/core/effect.js +56 -56
  11. package/dist/prod/core/external.js +4 -4
  12. package/dist/prod/core/graph.js +65 -54
  13. package/dist/prod/core/heap.js +47 -39
  14. package/dist/prod/core/invariants.js +3 -2
  15. package/dist/prod/core/lanes.js +41 -34
  16. package/dist/prod/core/optimistic.js +78 -58
  17. package/dist/prod/core/owner.js +101 -100
  18. package/dist/prod/core/scheduler.js +249 -192
  19. package/dist/prod/core/verdict.js +185 -78
  20. package/dist/prod/index.js +7 -7
  21. package/dist/prod/map.js +108 -106
  22. package/dist/prod/signals.js +20 -1
  23. package/dist/prod/store/index.js +36 -0
  24. package/dist/prod/store/next/optimistic.js +385 -0
  25. package/dist/prod/store/next/projection.js +172 -0
  26. package/dist/prod/store/next/reconcile.js +331 -0
  27. package/dist/prod/store/next/store.js +1499 -0
  28. package/dist/prod/store/next/target.js +20 -0
  29. package/dist/prod/store/store.js +126 -882
  30. package/dist/prod/store/utils.js +59 -186
  31. package/dist/types/core/attribution-hooks.d.ts +52 -0
  32. package/dist/types/core/attribution.d.ts +186 -0
  33. package/dist/types/core/constants.d.ts +26 -0
  34. package/dist/types/core/core.d.ts +16 -1
  35. package/dist/types/core/dev.d.ts +20 -3
  36. package/dist/types/core/graph.d.ts +1 -0
  37. package/dist/types/core/lanes.d.ts +4 -16
  38. package/dist/types/core/scheduler.d.ts +12 -2
  39. package/dist/types/core/types.d.ts +85 -41
  40. package/dist/types/signals.d.ts +19 -0
  41. package/dist/types/store/index.d.ts +15 -5
  42. package/dist/types/store/next/optimistic.d.ts +20 -0
  43. package/dist/types/store/next/projection.d.ts +8 -0
  44. package/dist/types/store/next/reconcile.d.ts +3 -0
  45. package/dist/types/store/next/store.d.ts +77 -0
  46. package/dist/types/store/next/target.d.ts +117 -0
  47. package/dist/types/store/store.d.ts +26 -101
  48. package/dist/types/store/utils.d.ts +0 -40
  49. package/dist/types-cjs/core/attribution-hooks.d.cts +52 -0
  50. package/dist/types-cjs/core/attribution.d.cts +186 -0
  51. package/dist/types-cjs/core/constants.d.cts +26 -0
  52. package/dist/types-cjs/core/core.d.cts +16 -1
  53. package/dist/types-cjs/core/dev.d.cts +20 -3
  54. package/dist/types-cjs/core/graph.d.cts +1 -0
  55. package/dist/types-cjs/core/lanes.d.cts +4 -16
  56. package/dist/types-cjs/core/scheduler.d.cts +12 -2
  57. package/dist/types-cjs/core/types.d.cts +85 -41
  58. package/dist/types-cjs/signals.d.cts +19 -0
  59. package/dist/types-cjs/store/index.d.cts +15 -5
  60. package/dist/types-cjs/store/next/optimistic.d.cts +20 -0
  61. package/dist/types-cjs/store/next/projection.d.cts +8 -0
  62. package/dist/types-cjs/store/next/reconcile.d.cts +3 -0
  63. package/dist/types-cjs/store/next/store.d.cts +77 -0
  64. package/dist/types-cjs/store/next/target.d.cts +117 -0
  65. package/dist/types-cjs/store/store.d.cts +26 -101
  66. package/dist/types-cjs/store/utils.d.cts +0 -40
  67. package/package.json +2 -1
  68. package/dist/prod/store/optimistic.js +0 -217
  69. package/dist/prod/store/projection.js +0 -231
  70. package/dist/prod/store/reconcile.js +0 -707
package/dist/prod/map.js CHANGED
@@ -16,28 +16,30 @@ import { accessor } from "./signals.js";
16
16
 
17
17
  import { $TRACK } from "./store/store.js";
18
18
 
19
+ import "./store/next/store.js";
20
+
19
21
  function mapArray(t, s, i) {
20
22
  const e = typeof i?.keyed === "function" ? i.keyed : undefined;
21
23
  const r = s.length > 1;
22
24
  const n = s;
23
25
  const h = {
24
- wt: createOwner(),
25
- jt: 0,
26
- Wt: t,
27
- Mt: [],
28
- Kt: n,
29
- xt: [],
30
- Ut: [],
31
- $t: e,
32
- qt: e || i?.keyed === false ? [] : undefined,
33
- zt: r && i?.keyed !== false ? [] : undefined,
34
- Bt: i?.keyed === false,
35
- Ht: i?.fallback
26
+ jt: createOwner(),
27
+ Wt: 0,
28
+ xt: t,
29
+ Kt: [],
30
+ $t: n,
31
+ qt: [],
32
+ zt: [],
33
+ Jt: e,
34
+ Xt: e || i?.keyed === false ? [] : undefined,
35
+ Yt: r && i?.keyed !== false ? [] : undefined,
36
+ Zt: i?.keyed === false,
37
+ ts: i?.fallback
36
38
  };
37
39
  const o = computed(updateKeyedMap.bind(h));
38
40
  // Untracked reads inside the internal owner resolve via _parentComputed; routing
39
41
  // them through node lets store-proxy lookups see pending writes (not stale _value).
40
- h.wt.dt = o;
42
+ h.jt.Ot = o;
41
43
  o.T &= ~CONFIG_AUTO_DISPOSE;
42
44
  return accessor(o);
43
45
  }
@@ -56,52 +58,52 @@ const pureOptions = {
56
58
  // strong-abort ordering: removed rows now dispose AFTER the pass's new rows
57
59
  // are created (you cannot destroy state before knowing the pass will land).
58
60
  function updateKeyedMap() {
59
- const t = this.Wt() || [], s = t.length;
61
+ const t = this.xt() || [], s = t.length;
60
62
  t[$TRACK];
61
63
  // top level tracking
62
- runWithOwner(this.wt, () => {
64
+ runWithOwner(this.jt, () => {
63
65
  let i, e, r, n,
64
66
  // Mappers write freshly-created row/index signals into the STAGE
65
67
  // arrays (`rows`/`indexes`), never into `this._rows`/`this._indexes`.
66
- h = this.qt ? this.Bt ? () => {
68
+ h = this.Xt ? this.Zt ? () => {
67
69
  r[e] = signal(t[e], pureOptions);
68
- return this.Kt(accessor(r[e]), e);
70
+ return this.$t(accessor(r[e]), e);
69
71
  } : () => {
70
72
  r[e] = signal(t[e], pureOptions);
71
73
  n && (n[e] = signal(e, pureOptions));
72
- return this.Kt(accessor(r[e]), n ? accessor(n[e]) : undefined);
73
- } : this.zt ? () => {
74
+ return this.$t(accessor(r[e]), n ? accessor(n[e]) : undefined);
75
+ } : this.Yt ? () => {
74
76
  const s = t[e];
75
77
  n[e] = signal(e, pureOptions);
76
- return this.Kt(s, accessor(n[e]));
78
+ return this.$t(s, accessor(n[e]));
77
79
  } : () => {
78
80
  const s = t[e];
79
- return this.Kt(s);
81
+ return this.$t(s);
80
82
  };
81
83
  // fast path for empty arrays
82
84
  if (s === 0) {
83
- if (this.jt !== 0) {
84
- this.wt.dispose(false);
85
- this.Ut = [];
86
- this.Mt = [];
87
- this.xt = [];
88
- this.jt = 0;
89
- this.qt && (this.qt = []);
90
- this.zt && (this.zt = []);
85
+ if (this.Wt !== 0) {
86
+ this.jt.dispose(false);
87
+ this.zt = [];
88
+ this.Kt = [];
89
+ this.qt = [];
90
+ this.Wt = 0;
91
+ this.Xt && (this.Xt = []);
92
+ this.Yt && (this.Yt = []);
91
93
  }
92
- if (this.Ht && !this.xt[0]) {
94
+ if (this.ts && !this.qt[0]) {
93
95
  // an aborted fallback attempt leaves an owner without a mapping;
94
96
  // dispose it before re-creating
95
- this.Ut[0]?.dispose();
96
- this.xt[0] = runWithOwner(this.Ut[0] = createOwner(), this.Ht);
97
+ this.zt[0]?.dispose();
98
+ this.qt[0] = runWithOwner(this.zt[0] = createOwner(), this.ts);
97
99
  }
98
100
  }
99
101
  // fast path for new create
100
- else if (this.jt === 0) {
102
+ else if (this.Wt === 0) {
101
103
  const o = new Array(s);
102
104
  const c = new Array(s);
103
- r = this.qt && new Array(s);
104
- n = this.zt && new Array(s);
105
+ r = this.Xt && new Array(s);
106
+ n = this.Yt && new Array(s);
105
107
  try {
106
108
  for (e = 0; e < s; e++) o[e] = runWithOwner(c[e] = createOwner(), h);
107
109
  } catch (t) {
@@ -109,43 +111,43 @@ function updateKeyedMap() {
109
111
  throw t;
110
112
  }
111
113
  // commit
112
- if (this.Ut[0]) this.Ut[0].dispose();
114
+ if (this.zt[0]) this.zt[0].dispose();
113
115
  // previous fallback
114
- this.xt = o;
115
- this.Ut = c;
116
- r && (this.qt = r);
117
- n && (this.zt = n);
118
- this.Mt = t.slice(0);
119
- this.jt = s;
116
+ this.qt = o;
117
+ this.zt = c;
118
+ r && (this.Xt = r);
119
+ n && (this.Yt = n);
120
+ this.Kt = t.slice(0);
121
+ this.Wt = s;
120
122
  } else {
121
123
  let o, c, a, f, u, p, w, l, d;
122
124
  // skip common prefix
123
- for (o = 0, c = Math.min(this.jt, s); o < c && (this.Mt[o] === t[o] || this.qt && compare(this.$t, this.Mt[o], t[o])); o++) {
124
- if (this.qt) setSignal(this.qt[o], t[o]);
125
+ for (o = 0, c = Math.min(this.Wt, s); o < c && (this.Kt[o] === t[o] || this.Xt && compare(this.Jt, this.Kt[o], t[o])); o++) {
126
+ if (this.Xt) setSignal(this.Xt[o], t[o]);
125
127
  }
126
128
  // skip common suffix — counted only; retained entries land in one pass
127
129
  // at commit instead of being staged and copied twice
128
- for (c = this.jt - 1, a = s - 1; c >= o && a >= o && (this.Mt[c] === t[a] || this.qt && compare(this.$t, this.Mt[c], t[a])); c--,
130
+ for (c = this.Wt - 1, a = s - 1; c >= o && a >= o && (this.Kt[c] === t[a] || this.Xt && compare(this.Jt, this.Kt[c], t[a])); c--,
129
131
  a--) ;
130
132
  // no structural change (every position matched in place at equal
131
133
  // length — the common post-reconcile shape): keep the same mapped
132
134
  // array identity so downstream consumers don't re-run at all
133
- if (o === s && this.jt === s) {
134
- this.Mt = t.slice(0);
135
+ if (o === s && this.Wt === s) {
136
+ this.Kt = t.slice(0);
135
137
  return;
136
138
  }
137
- const O = s - this.jt;
139
+ const O = s - this.Wt;
138
140
  const m = new Array(s);
139
141
  const _ = new Array(s);
140
- r = this.qt ? new Array(s) : undefined;
141
- n = this.zt ? new Array(s) : undefined;
142
+ r = this.Xt ? new Array(s) : undefined;
143
+ n = this.Yt ? new Array(s) : undefined;
142
144
  // 0) prepare a map of all indices in the changed window of newItems,
143
145
  // scanning backwards so we encounter them in natural order
144
146
  p = new Map;
145
147
  w = new Array(a + 1);
146
148
  for (e = a; e >= o; e--) {
147
149
  f = t[e];
148
- u = this.$t ? this.$t(f) : f;
150
+ u = this.Jt ? this.Jt(f) : f;
149
151
  i = p.get(u);
150
152
  w[e] = i === undefined ? -1 : i;
151
153
  p.set(u, e);
@@ -154,17 +156,17 @@ function updateKeyedMap() {
154
156
  // in the new set; if so, stage them at their new positions; if not,
155
157
  // queue them for disposal at commit
156
158
  for (i = o; i <= c; i++) {
157
- f = this.Mt[i];
158
- u = this.$t ? this.$t(f) : f;
159
+ f = this.Kt[i];
160
+ u = this.Jt ? this.Jt(f) : f;
159
161
  e = p.get(u);
160
162
  if (e !== undefined && e !== -1) {
161
- m[e] = this.xt[i];
162
- _[e] = this.Ut[i];
163
- r && (r[e] = this.qt[i]);
164
- n && (n[e] = this.zt[i]);
163
+ m[e] = this.qt[i];
164
+ _[e] = this.zt[i];
165
+ r && (r[e] = this.Xt[i]);
166
+ n && (n[e] = this.Yt[i]);
165
167
  e = w[e];
166
168
  p.set(u, e);
167
- } else (l ??= []).push(this.Ut[i]);
169
+ } else (l ??= []).push(this.zt[i]);
168
170
  }
169
171
  // 2) create new rows into the temp arrays; an abort disposes only these
170
172
  try {
@@ -181,38 +183,38 @@ function updateKeyedMap() {
181
183
  // into the fresh arrays, swap them in (new identity for downstream
182
184
  // change propagation), then dispose exited rows
183
185
  for (i = 0; i < o; i++) {
184
- m[i] = this.xt[i];
185
- _[i] = this.Ut[i];
186
- r && (r[i] = this.qt[i]);
187
- n && (n[i] = this.zt[i]);
186
+ m[i] = this.qt[i];
187
+ _[i] = this.zt[i];
188
+ r && (r[i] = this.Xt[i]);
189
+ n && (n[i] = this.Yt[i]);
188
190
  }
189
191
  for (e = o; e <= a; e++) {
190
192
  if (r) setSignal(r[e], t[e]);
191
193
  if (n) setSignal(n[e], e);
192
194
  }
193
195
  for (e = a + 1; e < s; e++) {
194
- m[e] = this.xt[e - O];
195
- _[e] = this.Ut[e - O];
196
+ m[e] = this.qt[e - O];
197
+ _[e] = this.zt[e - O];
196
198
  if (r) {
197
- r[e] = this.qt[e - O];
199
+ r[e] = this.Xt[e - O];
198
200
  setSignal(r[e], t[e]);
199
201
  }
200
202
  if (n) {
201
- n[e] = this.zt[e - O];
203
+ n[e] = this.Yt[e - O];
202
204
  if (O !== 0) setSignal(n[e], e);
203
205
  }
204
206
  }
205
- this.xt = m;
206
- this.Ut = _;
207
- r && (this.qt = r);
208
- n && (this.zt = n);
209
- this.jt = s;
207
+ this.qt = m;
208
+ this.zt = _;
209
+ r && (this.Xt = r);
210
+ n && (this.Yt = n);
211
+ this.Wt = s;
210
212
  // save a copy of the mapped items for the next update
211
- this.Mt = t.slice(0);
213
+ this.Kt = t.slice(0);
212
214
  if (l) for (i = 0; i < l.length; i++) l[i].dispose();
213
215
  }
214
216
  });
215
- return this.xt;
217
+ return this.qt;
216
218
  }
217
219
 
218
220
  /**
@@ -232,21 +234,21 @@ function updateKeyedMap() {
232
234
  */ function repeat(t, s, i) {
233
235
  const e = s;
234
236
  const r = {
235
- wt: createOwner(),
236
- jt: 0,
237
- Jt: 0,
238
- Vt: t,
239
- Kt: e,
240
- Ut: [],
241
- xt: [],
242
- Xt: i?.from,
243
- Ht: i?.fallback
237
+ jt: createOwner(),
238
+ Wt: 0,
239
+ ss: 0,
240
+ es: t,
241
+ $t: e,
242
+ zt: [],
243
+ qt: [],
244
+ rs: i?.from,
245
+ ts: i?.fallback
244
246
  };
245
247
  const n = computed(updateRepeat.bind(r));
246
248
  // Same as mapArray: untracked reads inside the internal owner resolve via
247
249
  // _parentComputed, so async reads in row callbacks register with the node
248
250
  // (pending tracking + post-settle retry) instead of vanishing.
249
- r.wt.dt = n;
251
+ r.jt.Ot = n;
250
252
  n.T &= ~CONFIG_AUTO_DISPOSE;
251
253
  return accessor(n);
252
254
  }
@@ -259,55 +261,55 @@ function updateKeyedMap() {
259
261
  // for the post-settle retry. The overlap math also subsumes the previous
260
262
  // disjoint-window/front-clear/end-clear/shift special cases.
261
263
  function updateRepeat() {
262
- const t = this.Vt();
263
- const s = this.Xt?.() || 0;
264
- runWithOwner(this.wt, () => {
264
+ const t = this.es();
265
+ const s = this.rs?.() || 0;
266
+ runWithOwner(this.jt, () => {
265
267
  if (t === 0) {
266
- if (this.jt !== 0) {
267
- this.wt.dispose(false);
268
- this.Ut = [];
269
- this.xt = [];
270
- this.jt = 0;
268
+ if (this.Wt !== 0) {
269
+ this.jt.dispose(false);
270
+ this.zt = [];
271
+ this.qt = [];
272
+ this.Wt = 0;
271
273
  // Reset offset to match the cleared data (#2767, repro 2).
272
- this.Jt = 0;
274
+ this.ss = 0;
273
275
  }
274
- if (this.Ht && !this.xt[0]) {
276
+ if (this.ts && !this.qt[0]) {
275
277
  // an aborted fallback attempt leaves an owner without a mapping;
276
278
  // dispose it before re-creating
277
- this.Ut[0]?.dispose();
278
- this.xt[0] = runWithOwner(this.Ut[0] = createOwner(), this.Ht);
279
+ this.zt[0]?.dispose();
280
+ this.qt[0] = runWithOwner(this.zt[0] = createOwner(), this.ts);
279
281
  }
280
282
  return;
281
283
  }
282
284
  const i = s + t;
283
- const e = this.Jt + this.jt;
285
+ const e = this.ss + this.Wt;
284
286
  // Retained overlap [keepStart, keepEnd) in global indexes; empty when the
285
287
  // windows are disjoint or when coming from empty/fallback.
286
- const r = Math.max(s, this.Jt);
288
+ const r = Math.max(s, this.ss);
287
289
  const n = Math.min(i, e);
288
290
  const h = new Array(t);
289
291
  const o = new Array(t);
290
292
  for (let t = r; t < n; t++) {
291
- o[t - s] = this.Ut[t - this.Jt];
292
- h[t - s] = this.xt[t - this.Jt];
293
+ o[t - s] = this.zt[t - this.ss];
294
+ h[t - s] = this.qt[t - this.ss];
293
295
  }
294
296
  try {
295
297
  for (let t = s; t < i; t++) {
296
298
  if (t >= r && t < n) continue;
297
- h[t - s] = runWithOwner(o[t - s] = createOwner(), () => this.Kt(t));
299
+ h[t - s] = runWithOwner(o[t - s] = createOwner(), () => this.$t(t));
298
300
  }
299
301
  } catch (t) {
300
302
  for (let t = s; t < i; t++) if ((t < r || t >= n) && o[t - s]) o[t - s].dispose();
301
303
  throw t;
302
304
  }
303
305
  // commit: dispose the previous fallback or the rows leaving the window
304
- if (this.jt === 0) this.Ut[0]?.dispose(); else for (let t = this.Jt; t < e; t++) if (t < s || t >= i) this.Ut[t - this.Jt].dispose();
305
- this.xt = h;
306
- this.Ut = o;
307
- this.Jt = s;
308
- this.jt = t;
306
+ if (this.Wt === 0) this.zt[0]?.dispose(); else for (let t = this.ss; t < e; t++) if (t < s || t >= i) this.zt[t - this.ss].dispose();
307
+ this.qt = h;
308
+ this.zt = o;
309
+ this.ss = s;
310
+ this.Wt = t;
309
311
  });
310
- return this.xt;
312
+ return this.qt;
311
313
  }
312
314
 
313
315
  function compare(t, s, i) {
@@ -123,6 +123,12 @@ function createEffect(e, t, n) {
123
123
  * may run multiple times for a single change or show tearing (reading inconsistent
124
124
  * state). Use only when dynamic subscription patterns require same-scope tracking.
125
125
  *
126
+ * The callback runs during the flush itself: writes made inside it are queued
127
+ * into the same flush's continuation and are never visible to the callback's
128
+ * own reads (reads return settled values, as in every effect-phase scope), and
129
+ * `flush()` cannot be called from inside it (dev throws; production is a
130
+ * no-op) — defer with `queueMicrotask(() => flush())` if needed.
131
+ *
126
132
  * ```typescript
127
133
  * createTrackedEffect(compute, options?: { name?: string });
128
134
  * ```
@@ -245,7 +251,7 @@ function createEffect(e, t, n) {
245
251
  // route through the inherited queue.
246
252
  const r = getOwner();
247
253
  const o = new MicrotaskQueue;
248
- o.ke = r.C;
254
+ o.ve = r.C;
249
255
  // notify() forwards up the normal chain
250
256
  r.C = o;
251
257
  // A user effect rather than a bare computed: computeds are pull-based and
@@ -303,6 +309,19 @@ function createOptimistic(e, t) {
303
309
  * Reactive reads inside the callback are *not* tracked — to react to
304
310
  * subsequent settles, register a new `onSettled` each time.
305
311
  *
312
+ * The callback runs during the settle flush itself, which gives it the same
313
+ * write semantics as every other effect-phase scope (the effect half of
314
+ * `createEffect`, event handlers):
315
+ *
316
+ * - **Writes** are queued into the same flush's continuation — dependent memos
317
+ * and effects update before the flush returns — but reads inside the
318
+ * callback keep returning the settled (pre-write) values. A callback never
319
+ * observes its own unsettled write. Functional setters still compose:
320
+ * `set(v => v + 1)` twice increments twice.
321
+ * - **`flush()` cannot be called** from inside the callback — the flush is
322
+ * already running (dev throws; production is a no-op). To force a drain
323
+ * after this settle, defer it: `queueMicrotask(() => flush())`.
324
+ *
306
325
  * `onCleanup` is **not** allowed inside the callback — return a cleanup
307
326
  * function instead. The returned cleanup runs on owner disposal.
308
327
  *
@@ -0,0 +1,36 @@
1
+ import "../core/core.js";
2
+
3
+ import "../core/scheduler.js";
4
+
5
+ import "../core/invariants.js";
6
+
7
+ import "../core/verdict.js";
8
+
9
+ import "../core/effect.js";
10
+
11
+ import { createStoreNext, deepNext, snapshotNext } from "./next/store.js";
12
+
13
+ import { reconcileNextState } from "./next/reconcile.js";
14
+
15
+ import { createStoreDerivedNext } from "./next/projection.js";
16
+
17
+ export { createProjectionNext as createProjection } from "./next/projection.js";
18
+
19
+ function createStore(e, t, r) {
20
+ if (typeof e === "function") return createStoreDerivedNext(e, t, r);
21
+ return createStoreNext(e, !!t?.shallow);
22
+ }
23
+
24
+ function reconcile(e, t = "id") {
25
+ return r => reconcileNextState(e, r, t);
26
+ }
27
+
28
+ function snapshot(e) {
29
+ return snapshotNext(e);
30
+ }
31
+
32
+ function deep(e) {
33
+ return deepNext(e);
34
+ }
35
+
36
+ export { createStore, deep, reconcile, snapshot };