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

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 (89) hide show
  1. package/dist/dev-shared.js +785 -367
  2. package/dist/dev.attribution.js +75 -52
  3. package/dist/dev.js +1120 -567
  4. package/dist/observe/affects-marks.js +86 -0
  5. package/dist/observe/affects.js +22 -73
  6. package/dist/observe/boundaries.js +191 -189
  7. package/dist/observe/core/async.js +175 -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 +3 -3
  12. package/dist/observe/core/core.js +503 -363
  13. package/dist/observe/core/dev.js +66 -38
  14. package/dist/observe/core/effect.js +48 -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 +57 -43
  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 +226 -259
  21. package/dist/observe/core/owner.js +62 -50
  22. package/dist/observe/core/scheduler.js +319 -171
  23. package/dist/observe/core/truth.js +45 -0
  24. package/dist/observe/core/verdict.js +88 -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 +41 -30
  30. package/dist/observe/store/affects.js +6 -6
  31. package/dist/observe/store/edges.js +39 -0
  32. package/dist/observe/store/optimistic.js +41 -36
  33. package/dist/observe/store/projection.js +68 -55
  34. package/dist/observe/store/store.js +157 -104
  35. package/dist/prod/affects-marks.js +86 -0
  36. package/dist/prod/affects.js +23 -72
  37. package/dist/prod/boundaries.js +155 -153
  38. package/dist/prod/core/async.js +171 -127
  39. package/dist/prod/core/constants.js +17 -3
  40. package/dist/prod/core/context.js +3 -3
  41. package/dist/prod/core/core.js +478 -342
  42. package/dist/prod/core/effect.js +68 -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 +57 -43
  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 +217 -250
  49. package/dist/prod/core/owner.js +62 -51
  50. package/dist/prod/core/scheduler.js +310 -162
  51. package/dist/prod/core/truth.js +45 -0
  52. package/dist/prod/core/verdict.js +87 -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 +41 -30
  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 +37 -33
  61. package/dist/prod/store/projection.js +25 -19
  62. package/dist/prod/store/store.js +142 -96
  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 +44 -2
  68. package/dist/types/core/dev.d.ts +43 -13
  69. package/dist/types/core/graph.d.ts +11 -0
  70. package/dist/types/core/identities.d.ts +17 -0
  71. package/dist/types/core/index.d.ts +2 -1
  72. package/dist/types/core/lanes.d.ts +2 -14
  73. package/dist/types/core/owner.d.ts +5 -3
  74. package/dist/types/core/scheduler.d.ts +47 -28
  75. package/dist/types/core/truth.d.ts +16 -0
  76. package/dist/types/core/types.d.ts +21 -0
  77. package/dist/types/core/verdict.d.ts +32 -0
  78. package/dist/types/deferred.d.ts +28 -0
  79. package/dist/types/index.d.ts +6 -0
  80. package/dist/types/map.d.ts +2 -2
  81. package/dist/types/signals.d.ts +28 -7
  82. package/dist/types/store/edges.d.ts +9 -0
  83. package/dist/types/store/index.d.ts +10 -3
  84. package/dist/types/store/optimistic.d.ts +3 -2
  85. package/dist/types/store/projection.d.ts +4 -3
  86. package/dist/types/store/store.d.ts +1 -1
  87. package/dist/types/store/target.d.ts +16 -8
  88. package/dist/types/store/types.d.ts +14 -0
  89. package/package.json +1 -1
package/dist/prod/map.js CHANGED
@@ -16,98 +16,98 @@ import { accessor } from "./signals.js";
16
16
 
17
17
  import { $TRACK } from "./store/types.js";
18
18
 
19
- function mapArray(t, s, e) {
20
- const i = typeof e?.keyed === "function" ? e.keyed : undefined;
21
- const r = s.length > 1;
22
- const n = s;
19
+ function mapArray(t, e, i) {
20
+ const s = typeof i?.keyed === "function" ? i.keyed : undefined;
21
+ const n = e.length > 1;
22
+ const r = e;
23
23
  const o = {
24
24
  L: createOwner(),
25
- ht: 0,
26
- dt: t,
27
- wt: [],
28
- Ot: n,
29
- yt: [],
30
- ln: [],
31
- ot: i,
32
- At: i || e?.keyed === false ? [] : undefined,
33
- Mt: r && e?.keyed !== false ? [] : undefined,
34
- gt: e?.keyed === false,
35
- v: e?.fallback
25
+ it: 0,
26
+ st: t,
27
+ ot: [],
28
+ ft: r,
29
+ ht: [],
30
+ tn: [],
31
+ ze: s,
32
+ ct: s || i?.keyed === false ? [] : undefined,
33
+ lt: n && i?.keyed !== false ? [] : undefined,
34
+ ut: i?.keyed === false,
35
+ H: i?.fallback
36
36
  };
37
- const h = computed(updateKeyedMap.bind(o), undefined);
38
- h.i &= ~CONFIG_AUTO_DISPOSE;
39
- return accessor(h);
37
+ const f = computed(updateKeyedMap.bind(o), undefined);
38
+ f.l &= ~CONFIG_AUTO_DISPOSE;
39
+ return accessor(f);
40
40
  }
41
41
 
42
42
  const pureOptions = {
43
43
  ownedWrite: true
44
44
  };
45
45
 
46
- function trySmallMove(t, s, e, i) {
47
- const r = t.wt;
48
- const n = t.ht - 1;
46
+ function trySmallMove(t, e, i, s) {
47
+ const n = t.ot;
48
+ const r = t.it - 1;
49
49
  const o = [];
50
- const h = [];
51
50
  const f = [];
51
+ const h = [];
52
52
  // flat triples: oldStart, newStart, length
53
53
  let c = 256;
54
- let a = i;
55
- let u = i;
56
- let l = false;
57
- while (a <= n && u <= e - 1) {
58
- const t = r[a];
59
- const i = s[u];
60
- if (t === i) {
61
- if (!l) {
62
- f.push(a, u, 0);
63
- l = true;
54
+ let a = s;
55
+ let l = s;
56
+ let u = false;
57
+ while (a <= r && l <= i - 1) {
58
+ const t = n[a];
59
+ const s = e[l];
60
+ if (t === s) {
61
+ if (!u) {
62
+ h.push(a, l, 0);
63
+ u = true;
64
64
  }
65
- f[f.length - 1]++;
65
+ h[h.length - 1]++;
66
66
  a++;
67
- u++;
67
+ l++;
68
68
  continue;
69
69
  }
70
- l = false;
70
+ u = false;
71
71
  // Bounded realignment lookahead, shorter distance wins.
72
- let p = -1;
73
- let d = Math.min(32 - o.length, n - a, c);
74
- for (let t = 1; t <= d; t++) {
75
- if (r[a + t] === i) {
76
- p = t;
72
+ let d = -1;
73
+ let w = Math.min(32 - o.length, r - a, c);
74
+ for (let t = 1; t <= w; t++) {
75
+ if (n[a + t] === s) {
76
+ d = t;
77
77
  break;
78
78
  }
79
79
  }
80
- c -= p === -1 ? d : p;
81
- let w = -1;
82
- d = Math.min(32 - h.length, e - 1 - u, c);
83
- for (let e = 1; e <= d; e++) {
84
- if (s[u + e] === t) {
85
- w = e;
80
+ c -= d === -1 ? w : d;
81
+ let p = -1;
82
+ w = Math.min(32 - f.length, i - 1 - l, c);
83
+ for (let i = 1; i <= w; i++) {
84
+ if (e[l + i] === t) {
85
+ p = i;
86
86
  break;
87
87
  }
88
88
  }
89
- c -= w === -1 ? d : w;
90
- if (p !== -1 && (w === -1 || p <= w)) {
91
- while (p-- > 0) o.push(a++);
89
+ c -= p === -1 ? w : p;
90
+ if (d !== -1 && (p === -1 || d <= p)) {
91
+ while (d-- > 0) o.push(a++);
92
92
  continue;
93
93
  }
94
- if (w !== -1) {
95
- while (w-- > 0) h.push(u++);
94
+ if (p !== -1) {
95
+ while (p-- > 0) f.push(l++);
96
96
  continue;
97
97
  }
98
- if (c <= 0 || o.length === 32 || h.length === 32) return false;
98
+ if (c <= 0 || o.length === 32 || f.length === 32) return false;
99
99
  o.push(a++);
100
- h.push(u++);
100
+ f.push(l++);
101
101
  }
102
- for (;a <= n; a++) {
102
+ for (;a <= r; a++) {
103
103
  if (o.length === 32) return false;
104
104
  o.push(a);
105
105
  }
106
- for (;u <= e - 1; u++) {
107
- if (h.length === 32) return false;
108
- h.push(u);
106
+ for (;l <= i - 1; l++) {
107
+ if (f.length === 32) return false;
108
+ f.push(l);
109
109
  }
110
- return commitSmallMove(t, s, e, o, h, f);
110
+ return commitSmallMove(t, e, i, o, f, h);
111
111
  }
112
112
 
113
113
  /** PHASE 2 of the small-move path, in its OWN function so that a pass which
@@ -116,24 +116,24 @@ function trySmallMove(t, s, e, i) {
116
116
  * per function, and cold `replace` measured +0.5 ms with both phases in one
117
117
  * body. Pairs displaced sources with destinations (an unmatched destination
118
118
  * is a replacement/insertion → general path), then commits: slice() the live
119
- * arrays, copy shifted runs, patch displaced pairs, dispose leftovers. */ function commitSmallMove(t, s, e, i, r, n) {
120
- const o = t.wt;
121
- let h;
119
+ * arrays, copy shifted runs, patch displaced pairs, dispose leftovers. */ function commitSmallMove(t, e, i, s, n, r) {
120
+ const o = t.ot;
122
121
  let f;
122
+ let h;
123
123
  let c;
124
- if (r.length !== 0) {
125
- c = new Array(i.length);
126
- for (f = 0; f < r.length; f++) {
124
+ if (n.length !== 0) {
125
+ c = new Array(s.length);
126
+ for (h = 0; h < n.length; h++) {
127
127
  let t = -1;
128
- for (h = 0; h < i.length; h++) {
129
- if (!c[h] && o[i[h]] === s[r[f]]) {
130
- t = h;
128
+ for (f = 0; f < s.length; f++) {
129
+ if (!c[f] && o[s[f]] === e[n[h]]) {
130
+ t = f;
131
131
  break;
132
132
  }
133
133
  }
134
134
  if (t === -1) return false;
135
135
  c[t] = true;
136
- r[f] = r[f] << 6 | t;
136
+ n[h] = n[h] << 6 | t;
137
137
  // pack pairing (found < 32)
138
138
  }
139
139
  }
@@ -143,46 +143,119 @@ function trySmallMove(t, s, e, i) {
143
143
  // and if a displaced identity also occurs inside a run the two algorithms
144
144
  // can hand different occurrences different owners (row-local state moves;
145
145
  // a shrink could dispose the wrong one). Decline that case — general path.
146
- if (i.length !== 0 || r.length !== 0) {
146
+ if (s.length !== 0 || n.length !== 0) {
147
147
  const t = new Set;
148
- for (h = 0; h < i.length; h++) t.add(o[i[h]]);
149
- for (f = 0; f < r.length; f++) t.add(s[r[f] >> 6]);
150
- for (let s = 0; s < n.length; s += 3) {
151
- const e = n[s];
152
- for (let i = 0, r = n[s + 2]; i < r; i++) if (t.has(o[e + i])) return false;
148
+ for (f = 0; f < s.length; f++) t.add(o[s[f]]);
149
+ for (h = 0; h < n.length; h++) t.add(e[n[h] >> 6]);
150
+ for (let e = 0; e < r.length; e += 3) {
151
+ const i = r[e];
152
+ for (let s = 0, n = r[e + 2]; s < n; s++) if (t.has(o[i + s])) return false;
153
153
  }
154
154
  }
155
- const a = t.yt;
156
- const u = t.ln;
157
- const l = a.slice(0, e);
158
- const p = u.slice(0, e);
159
- for (let t = 0; t < n.length; t += 3) {
160
- const s = n[t];
161
- const e = n[t + 1];
162
- if (s !== e) {
163
- for (let i = 0; i < n[t + 2]; i++) {
164
- l[e + i] = a[s + i];
165
- p[e + i] = u[s + i];
155
+ const a = t.ht;
156
+ const l = t.tn;
157
+ const u = a.slice(0, i);
158
+ const d = l.slice(0, i);
159
+ for (let t = 0; t < r.length; t += 3) {
160
+ const e = r[t];
161
+ const i = r[t + 1];
162
+ if (e !== i) {
163
+ for (let s = 0; s < r[t + 2]; s++) {
164
+ u[i + s] = a[e + s];
165
+ d[i + s] = l[e + s];
166
166
  }
167
167
  }
168
168
  }
169
- for (f = 0; f < r.length; f++) {
170
- const t = r[f] >> 6;
171
- const s = i[r[f] & 63];
172
- l[t] = a[s];
173
- p[t] = u[s];
169
+ for (h = 0; h < n.length; h++) {
170
+ const t = n[h] >> 6;
171
+ const e = s[n[h] & 63];
172
+ u[t] = a[e];
173
+ d[t] = l[e];
174
174
  }
175
- t.yt = l;
176
- t.ln = p;
177
- t.ht = e;
178
- t.wt = s.slice(0);
175
+ t.ht = u;
176
+ t.tn = d;
177
+ t.it = i;
178
+ t.ot = e.slice(0);
179
179
  // Dispose unmatched sources LAST (general-path ordering).
180
- for (h = 0; h < i.length; h++) {
181
- if (c === undefined || !c[h]) u[i[h]].dispose();
180
+ for (f = 0; f < s.length; f++) {
181
+ if (c === undefined || !c[f]) l[s[f]].dispose();
182
182
  }
183
183
  return true;
184
184
  }
185
185
 
186
+ /** Stage a PURE REMOVAL without the key map: when the new window is the old
187
+ * window with items dropped, in order (one-item removals, every-nth, the
188
+ * table filters), a two-pointer walk lands every survivor in the window
189
+ * stage with one key call per item and returns the leavers; a shape that is
190
+ * not an ordered subsequence returns null with the node stage cleared, and
191
+ * the map path below starts from scratch (it rewrites every window slot of
192
+ * `temp` / `rows` / `indexes`, so only `tempNodes`' `!== undefined` probe
193
+ * needs the reset). The stage arrays are scratch — nothing live is touched. */ function stageRemoval(t, e, i, s, n, r, o, f, h) {
194
+ const c = t.ze;
195
+ const a = t.ot;
196
+ const l = t.tn;
197
+ const u = [];
198
+ let d = i;
199
+ for (let w = i; w <= n; d++, w++) {
200
+ const n = c ? c(e[w]) : e[w];
201
+ while (d <= s && (c ? c(a[d]) : a[d]) !== n) u.push(l[d++]);
202
+ if (d > s) {
203
+ o.fill(undefined);
204
+ return null;
205
+ }
206
+ r[w] = t.ht[d];
207
+ o[w - i] = l[d];
208
+ if (f) f[w - i] = t.ct[d];
209
+ if (h) h[w - i] = t.lt[d];
210
+ }
211
+ for (;d <= s; d++) u.push(l[d]);
212
+ return u;
213
+ }
214
+
215
+ /** Land a staged window into the live bookkeeping arrays in place: shift the
216
+ * suffix by `dif` (a loop walked from the side that never overwrites an
217
+ * unread slot), take the window from the stage, write survivors' row and
218
+ * index signals. Its own function so the general path's body stays under
219
+ * the JIT's optimization budget — the inlined commit measured ~10% slower on
220
+ * full-window reorders (the small-move note has the same finding). */ function commitWindow(t, e, i, s, n, r, o, f, h) {
221
+ const c = t.tn;
222
+ const a = t.ct;
223
+ const l = t.lt;
224
+ const u = t.it;
225
+ const d = i - u;
226
+ let w, p;
227
+ if (d > 0) {
228
+ // Grow first: a store past the end from the top is a third slower than
229
+ // filling a pre-sized tail (measured on 1k refs, 2026-10-15).
230
+ c.length = i;
231
+ if (a) a.length = i;
232
+ if (l) l.length = i;
233
+ for (w = u - 1, p = i - 1; w > n; w--, p--) {
234
+ c[p] = c[w];
235
+ if (a) a[p] = a[w];
236
+ if (l) l[p] = l[w];
237
+ }
238
+ } else if (d < 0) {
239
+ for (w = n + 1, p = r + 1; w < u; w++, p++) {
240
+ c[p] = c[w];
241
+ if (a) a[p] = a[w];
242
+ if (l) l[p] = l[w];
243
+ }
244
+ c.length = i;
245
+ if (a) a.length = i;
246
+ if (l) l.length = i;
247
+ }
248
+ for (p = s; p <= r; p++) {
249
+ c[p] = o[p - s];
250
+ if (a) setSignal(a[p] = f[p - s], e[p]);
251
+ if (l) setSignal(l[p] = h[p - s], p);
252
+ }
253
+ for (p = r + 1; p < i; p++) {
254
+ if (a) setSignal(a[p], e[p]);
255
+ if (l && d !== 0) setSignal(l[p], p);
256
+ }
257
+ }
258
+
186
259
  function updateKeyedMap() {
187
260
  // Untracked reads inside the internal owner resolve via _parentComputed:
188
261
  // routed through the node, a store-proxy lookup sees pending writes (not a
@@ -190,73 +263,80 @@ function updateKeyedMap() {
190
263
  // the flight's reader (pending tracking + post-settle retry) instead of
191
264
  // vanishing. Set per pass: the first pass runs inside `computed()`, before
192
265
  // the node exists to the caller.
193
- this.L.et = getOwner();
194
- const t = this.dt() || [], s = t.length;
266
+ this.L.Ye = getOwner();
267
+ const t = this.st() || [], e = t.length;
195
268
  t[$TRACK];
196
269
  // top level tracking (a store array's container node)
197
270
  runWithOwner(this.L, () => {
198
- let e, i, r, n,
271
+ let i, s,
272
+ // The stage slot a mapper writes its row/index signal into: `j` on the
273
+ // create-all path, `j - start` on the general path, whose stage arrays
274
+ // are sized to the changed WINDOW, not the list.
275
+ n, r, o,
199
276
  // Mappers write freshly-created row/index signals into the STAGE
200
277
  // arrays (`rows`/`indexes`), never into `this._rows`/`this._indexes`.
201
- o = this.At ? this.gt ? () => {
202
- r[i] = signal(t[i], pureOptions);
203
- return this.Ot(accessor(r[i]), i);
278
+ f = this.ct ? this.ut ? () => {
279
+ r[n] = signal(t[s], pureOptions);
280
+ return this.ft(accessor(r[n]), s);
204
281
  } : () => {
205
- r[i] = signal(t[i], pureOptions);
206
- n && (n[i] = signal(i, pureOptions));
207
- return this.Ot(accessor(r[i]), n ? accessor(n[i]) : undefined);
208
- } : this.Mt ? () => {
209
- const s = t[i];
210
- n[i] = signal(i, pureOptions);
211
- return this.Ot(s, accessor(n[i]));
282
+ r[n] = signal(t[s], pureOptions);
283
+ o && (o[n] = signal(s, pureOptions));
284
+ return this.ft(accessor(r[n]), o ? accessor(o[n]) : undefined);
285
+ } : this.lt ? () => {
286
+ const e = t[s];
287
+ o[n] = signal(s, pureOptions);
288
+ return this.ft(e, accessor(o[n]));
212
289
  } : () => {
213
- const s = t[i];
214
- return this.Ot(s);
290
+ const e = t[s];
291
+ return this.ft(e);
215
292
  };
216
293
  // fast path for empty arrays
217
- if (s === 0) {
218
- if (this.ht !== 0) {
294
+ if (e === 0) {
295
+ if (this.it !== 0) {
219
296
  this.L.dispose(false);
220
- this.ln = [];
221
- this.wt = [];
222
- this.yt = [];
223
- this.ht = 0;
224
- this.At && (this.At = []);
225
- this.Mt && (this.Mt = []);
297
+ this.tn = [];
298
+ this.ot = [];
299
+ this.ht = [];
300
+ this.it = 0;
301
+ this.ct && (this.ct = []);
302
+ this.lt && (this.lt = []);
226
303
  }
227
- if (this.v && !this.yt[0]) {
304
+ if (this.H && !this.ht[0]) {
228
305
  // an aborted fallback attempt leaves an owner without a mapping;
229
306
  // dispose it before re-creating
230
- this.ln[0]?.dispose();
231
- this.yt[0] = runWithOwner(this.ln[0] = createOwner(), this.v);
307
+ this.tn[0]?.dispose();
308
+ this.ht[0] = runWithOwner(this.tn[0] = createOwner(), this.H);
232
309
  }
233
310
  }
234
311
  // fast path for new create
235
- else if (this.ht === 0) {
236
- const h = new Array(s);
237
- const f = new Array(s);
238
- r = this.At && new Array(s);
239
- n = this.Mt && new Array(s);
312
+ else if (this.it === 0) {
313
+ const h = new Array(e);
314
+ const c = new Array(e);
315
+ r = this.ct && new Array(e);
316
+ o = this.lt && new Array(e);
240
317
  try {
241
- for (i = 0; i < s; i++) h[i] = runWithOwner(f[i] = createOwner(), o);
318
+ for (s = 0; s < e; s++) {
319
+ n = s;
320
+ h[s] = runWithOwner(c[s] = createOwner(), f);
321
+ }
242
322
  } catch (t) {
243
- for (e = 0; e <= i; e++) f[e]?.dispose();
323
+ for (i = 0; i <= s; i++) c[i]?.dispose();
244
324
  throw t;
245
325
  }
246
326
  // commit
247
- if (this.ln[0]) this.ln[0].dispose();
327
+ if (this.tn[0]) this.tn[0].dispose();
248
328
  // previous fallback
249
- this.yt = h;
250
- this.ln = f;
251
- r && (this.At = r);
252
- n && (this.Mt = n);
253
- this.wt = t.slice(0);
254
- this.ht = s;
329
+ this.ht = h;
330
+ this.tn = c;
331
+ r && (this.ct = r);
332
+ o && (this.lt = o);
333
+ this.ot = t.slice(0);
334
+ this.it = e;
255
335
  } else {
256
- let h, f, c, a, u, l, p, d, w,
336
+ let h, c, a, l, u, d, w, p, m,
257
337
  // Dev (attribution engine installed): the items behind the exited and
258
338
  // entered rows, for the list-identity census.
259
- m, O;
339
+ O, _;
260
340
  // The pass's write to a per-slot signal (a row accessor in index mode,
261
341
  // an index accessor in keyed mode). The list's frame lives in these
262
342
  // writes as much as in the computed's result, so under a LANE pass they
@@ -278,20 +358,20 @@ function updateKeyedMap() {
278
358
  // takes `setSignal` directly. The engine is installed whenever a lane
279
359
  // is active.
280
360
  // CARVE 2: the lane-slot arm (`_landOnOverride`, F1) went with the engine.
281
- const _ = setSignal;
361
+ const y = setSignal;
282
362
  // skip common prefix
283
- for (h = 0, f = Math.min(this.ht, s); h < f && (this.wt[h] === t[h] || this.At && compare(this.ot, this.wt[h], t[h])); h++) {
284
- if (this.At) _(this.At[h], t[h]);
363
+ for (h = 0, c = Math.min(this.it, e); h < c && (this.ot[h] === t[h] || this.ct && compare(this.ze, this.ot[h], t[h])); h++) {
364
+ if (this.ct) y(this.ct[h], t[h]);
285
365
  }
286
366
  // skip common suffix — counted only; retained entries land in one pass
287
367
  // at commit instead of being staged and copied twice
288
- for (f = this.ht - 1, c = s - 1; f >= h && c >= h && (this.wt[f] === t[c] || this.At && compare(this.ot, this.wt[f], t[c])); f--,
289
- c--) ;
368
+ for (c = this.it - 1, a = e - 1; c >= h && a >= h && (this.ot[c] === t[a] || this.ct && compare(this.ze, this.ot[c], t[a])); c--,
369
+ a--) ;
290
370
  // no structural change (every position matched in place at equal
291
371
  // length — the common post-reconcile shape): keep the same mapped
292
372
  // array identity so downstream consumers don't re-run at all
293
- if (h === s && this.ht === s) {
294
- this.wt = t.slice(0);
373
+ if (h === e && this.it === e) {
374
+ this.ot = t.slice(0);
295
375
  return;
296
376
  }
297
377
  // SMALL-MOVE FAST PATH: extracted to its own function — inlining it
@@ -301,103 +381,108 @@ function updateKeyedMap() {
301
381
  // (plain removals, tail edits), the general path is window-
302
382
  // proportional and cheap — the fast path would only re-walk what the
303
383
  // trims proved.
304
- if (s <= this.ht && f - h > 64 && this.At === undefined && this.Mt === undefined) {
384
+ if (e <= this.it && c - h > 64 && this.ct === undefined && this.lt === undefined) {
305
385
  // PROBE before the scan: a small move keeps a mid-window item within
306
386
  // ±32 of its old position; a REPLACE (all fresh items — the shape
307
387
  // every page's first structural pass usually is) has it nowhere.
308
388
  // ~65 compares, no allocation, and the scan function is never
309
389
  // compiled for a replace (its cold first-call compile was the cost).
310
- const e = h + (c - h >> 1);
311
- const i = t[e];
312
- const r = Math.min(f, e + 32);
313
- let n = Math.max(h, e - 32);
314
- while (n <= r && this.wt[n] !== i) n++;
315
- if (n <= r && trySmallMove(this, t, s, h)) return;
316
- }
317
- const y = s - this.ht;
318
- const A = new Array(s);
319
- const M = new Array(s);
320
- r = this.At ? new Array(s) : undefined;
321
- n = this.Mt ? new Array(s) : undefined;
322
- // 0) prepare a map of all indices in the changed window of newItems,
323
- // scanning backwards so we encounter them in natural order
324
- l = new Map;
325
- p = new Array(c + 1);
326
- for (i = c; i >= h; i--) {
327
- a = t[i];
328
- u = this.ot ? this.ot(a) : a;
329
- e = l.get(u);
330
- p[i] = e === undefined ? -1 : e;
331
- l.set(u, i);
332
- }
333
- // 1) step through the old changed window and see if items can be found
334
- // in the new set; if so, stage them at their new positions; if not,
335
- // queue them for disposal at commit
336
- for (e = h; e <= f; e++) {
337
- a = this.wt[e];
338
- u = this.ot ? this.ot(a) : a;
339
- i = l.get(u);
340
- if (i !== undefined && i !== -1) {
341
- A[i] = this.yt[e];
342
- M[i] = this.ln[e];
343
- r && (r[i] = this.At[e]);
344
- n && (n[i] = this.Mt[e]);
345
- i = p[i];
346
- l.set(u, i);
347
- } else {
348
- (d ??= []).push(this.ln[e]);
349
- if (false && attrHooks !== null) ;
350
- }
390
+ const i = h + (a - h >> 1);
391
+ const s = t[i];
392
+ const n = Math.min(c, i + 32);
393
+ let r = Math.max(h, i - 32);
394
+ while (r <= n && this.ot[r] !== s) r++;
395
+ if (r <= n && trySmallMove(this, t, e, h)) return;
351
396
  }
352
- // 2) create new rows into the temp arrays; an abort disposes only these
353
- try {
354
- for (i = h; i <= c; i++) {
355
- if (M[i] !== undefined) continue;
356
- (w ??= []).push(M[i] = createOwner());
357
- if (false && attrHooks !== null) ;
358
- A[i] = runWithOwner(M[i], o);
397
+ const g = e - this.it;
398
+ // WINDOW-SIZED STAGING, IN-PLACE COMMIT (2026-10-15). The strong-abort
399
+ // contract (#2903) only needs the pass to touch nothing live before
400
+ // every mapper has succeeded; it never needed four fresh newLen-sized
401
+ // arrays. Only the changed window [start, newEnd] is staged — the
402
+ // prefix and suffix are, by construction, the rows that did not move
403
+ // relative to each other — and at commit the bookkeeping arrays
404
+ // (`_nodes`, `_rows`, `_indexes`) take the window in place with the
405
+ // suffix shifted by a plain loop (`copyWithin` is a generic
406
+ // per-element builtin in V8 — 30× slower than the loop on 1k refs,
407
+ // measured 2026-10-15). `_mappings` alone is
408
+ // rebuilt: its fresh identity is the change signal downstream relies
409
+ // on. A one-row edit on a 500-row list used to allocate ~20 KB of
410
+ // staging plus a position-sized chain array per commit (uibench's
411
+ // one-item tree ops at 4–6× Octane, jfb-reorder `removefirst` at
412
+ // 2.5×); now it stages one slot.
413
+ const A = a - h + 1;
414
+ const S = new Array(e);
415
+ const M = new Array(A);
416
+ r = this.ct ? new Array(A) : undefined;
417
+ o = this.lt ? new Array(A) : undefined;
418
+ // A shrinking pass is tried as a pure removal first (stageRemoval: no
419
+ // key map, one key call per item); anything else takes the map path.
420
+ const k = g < 0 ? stageRemoval(this, t, h, c, a, S, M, r, o) : null;
421
+ if (k !== null) {
422
+ // (The list-churn census below wants removals AND creations in one
423
+ // pass; a pure removal has none, so no item ledger is kept here.)
424
+ if (k.length !== 0) p = k;
425
+ } else {
426
+ // 0) prepare a map of all indices in the changed window of newItems,
427
+ // scanning backwards so we encounter them in natural order
428
+ d = new Map;
429
+ w = new Array(A);
430
+ for (s = a; s >= h; s--) {
431
+ l = t[s];
432
+ u = this.ze ? this.ze(l) : l;
433
+ i = d.get(u);
434
+ w[s - h] = i === undefined ? -1 : i;
435
+ d.set(u, s);
359
436
  }
360
- } catch (t) {
361
- if (w) for (e = 0; e < w.length; e++) w[e].dispose();
362
- throw t;
363
- }
364
- // 3) commit: land the retained prefix and suffix plus the staged window
365
- // into the fresh arrays, swap them in (new identity for downstream
366
- // change propagation), then dispose exited rows
367
- for (e = 0; e < h; e++) {
368
- A[e] = this.yt[e];
369
- M[e] = this.ln[e];
370
- r && (r[e] = this.At[e]);
371
- n && (n[e] = this.Mt[e]);
372
- }
373
- for (i = h; i <= c; i++) {
374
- if (r) _(r[i], t[i]);
375
- if (n) _(n[i], i);
376
- }
377
- for (i = c + 1; i < s; i++) {
378
- A[i] = this.yt[i - y];
379
- M[i] = this.ln[i - y];
380
- if (r) {
381
- r[i] = this.At[i - y];
382
- _(r[i], t[i]);
437
+ // 1) step through the old changed window and see if items can be found
438
+ // in the new set; if so, stage them at their new positions; if not,
439
+ // queue them for disposal at commit
440
+ for (i = h; i <= c; i++) {
441
+ l = this.ot[i];
442
+ u = this.ze ? this.ze(l) : l;
443
+ s = d.get(u);
444
+ if (s !== undefined && s !== -1) {
445
+ S[s] = this.ht[i];
446
+ M[s - h] = this.tn[i];
447
+ r && (r[s - h] = this.ct[i]);
448
+ o && (o[s - h] = this.lt[i]);
449
+ s = w[s - h];
450
+ d.set(u, s);
451
+ } else {
452
+ (p ??= []).push(this.tn[i]);
453
+ if (false && attrHooks !== null) ;
454
+ }
383
455
  }
384
- if (n) {
385
- n[i] = this.Mt[i - y];
386
- if (y !== 0) _(n[i], i);
456
+ // 2) create new rows into the window stage; an abort disposes only these
457
+ try {
458
+ for (s = h; s <= a; s++) {
459
+ if (M[s - h] !== undefined) continue;
460
+ (m ??= []).push(M[s - h] = createOwner());
461
+ if (false && attrHooks !== null) ;
462
+ n = s - h;
463
+ S[s] = runWithOwner(M[s - h], f);
464
+ }
465
+ } catch (t) {
466
+ if (m) for (i = 0; i < m.length; i++) m[i].dispose();
467
+ throw t;
387
468
  }
388
469
  }
389
- this.yt = A;
390
- this.ln = M;
391
- r && (this.At = r);
392
- n && (this.Mt = n);
393
- this.ht = s;
470
+ // 3) commit: the retained prefix and suffix land in the fresh mapped
471
+ // array; the bookkeeping arrays take the staged window IN PLACE (own
472
+ // function: kept out of this body on purpose, see commitWindow); then
473
+ // the exited rows dispose
474
+ for (i = 0; i < h; i++) S[i] = this.ht[i];
475
+ for (s = a + 1; s < e; s++) S[s] = this.ht[s - g];
476
+ commitWindow(this, t, e, h, c, a, M, r, o);
477
+ this.ht = S;
478
+ this.it = e;
394
479
  // save a copy of the mapped items for the next update
395
- this.wt = t.slice(0);
396
- if (d) for (e = 0; e < d.length; e++) d[e].dispose();
397
- if (false && attrHooks !== null && m !== undefined && O !== undefined) ;
480
+ this.ot = t.slice(0);
481
+ if (p) for (i = 0; i < p.length; i++) p[i].dispose();
482
+ if (false && attrHooks !== null && O !== undefined && _ !== undefined) ;
398
483
  }
399
484
  });
400
- return this.yt;
485
+ return this.ht;
401
486
  }
402
487
 
403
488
  /**
@@ -413,23 +498,23 @@ function updateKeyedMap() {
413
498
  * const view = repeat(count, i => `Item ${i}`, { fallback: () => "empty" });
414
499
  * ```
415
500
  *
416
- * @description https://docs.solidjs.com/reference/reactive-utilities/repeat
417
- */ function repeat(t, s, e) {
418
- const i = s;
419
- const r = {
501
+ * @description https://v2.solidjs.com/reference/solid-js/advanced/jsx-component-primitives/repeat
502
+ */ function repeat(t, e, i) {
503
+ const s = e;
504
+ const n = {
420
505
  L: createOwner(),
421
- ht: 0,
422
- St: 0,
423
- kt: t,
424
- Ot: i,
425
- ln: [],
426
- yt: [],
427
- jt: e?.from,
428
- v: e?.fallback
506
+ it: 0,
507
+ dt: 0,
508
+ wt: t,
509
+ ft: s,
510
+ tn: [],
511
+ ht: [],
512
+ Ot: i?.from,
513
+ H: i?.fallback
429
514
  };
430
- const n = computed(updateRepeat.bind(r));
431
- n.i &= ~CONFIG_AUTO_DISPOSE;
432
- return accessor(n);
515
+ const r = computed(updateRepeat.bind(n));
516
+ r.l &= ~CONFIG_AUTO_DISPOSE;
517
+ return accessor(r);
433
518
  }
434
519
 
435
520
  // Same staged-commit discipline as `updateKeyedMap` (#2903): the retained
@@ -441,60 +526,60 @@ function updateKeyedMap() {
441
526
  // disjoint-window/front-clear/end-clear/shift special cases.
442
527
  function updateRepeat() {
443
528
  // As in updateKeyedMap: the node is the row callbacks' reader, per pass.
444
- this.L.et = getOwner();
445
- const t = this.kt();
446
- const s = this.jt?.() || 0;
529
+ this.L.Ye = getOwner();
530
+ const t = this.wt();
531
+ const e = this.Ot?.() || 0;
447
532
  runWithOwner(this.L, () => {
448
533
  if (t === 0) {
449
- if (this.ht !== 0) {
534
+ if (this.it !== 0) {
450
535
  this.L.dispose(false);
451
- this.ln = [];
452
- this.yt = [];
453
- this.ht = 0;
536
+ this.tn = [];
537
+ this.ht = [];
538
+ this.it = 0;
454
539
  // Reset offset to match the cleared data (#2767, repro 2).
455
- this.St = 0;
540
+ this.dt = 0;
456
541
  }
457
- if (this.v && !this.yt[0]) {
542
+ if (this.H && !this.ht[0]) {
458
543
  // an aborted fallback attempt leaves an owner without a mapping;
459
544
  // dispose it before re-creating
460
- this.ln[0]?.dispose();
461
- this.yt[0] = runWithOwner(this.ln[0] = createOwner(), this.v);
545
+ this.tn[0]?.dispose();
546
+ this.ht[0] = runWithOwner(this.tn[0] = createOwner(), this.H);
462
547
  }
463
548
  return;
464
549
  }
465
- const e = s + t;
466
- const i = this.St + this.ht;
550
+ const i = e + t;
551
+ const s = this.dt + this.it;
467
552
  // Retained overlap [keepStart, keepEnd) in global indexes; empty when the
468
553
  // windows are disjoint or when coming from empty/fallback.
469
- const r = Math.max(s, this.St);
470
- const n = Math.min(e, i);
554
+ const n = Math.max(e, this.dt);
555
+ const r = Math.min(i, s);
471
556
  const o = new Array(t);
472
- const h = new Array(t);
473
- for (let t = r; t < n; t++) {
474
- h[t - s] = this.ln[t - this.St];
475
- o[t - s] = this.yt[t - this.St];
557
+ const f = new Array(t);
558
+ for (let t = n; t < r; t++) {
559
+ f[t - e] = this.tn[t - this.dt];
560
+ o[t - e] = this.ht[t - this.dt];
476
561
  }
477
562
  try {
478
- for (let t = s; t < e; t++) {
479
- if (t >= r && t < n) continue;
480
- o[t - s] = runWithOwner(h[t - s] = createOwner(), () => this.Ot(t));
563
+ for (let t = e; t < i; t++) {
564
+ if (t >= n && t < r) continue;
565
+ o[t - e] = runWithOwner(f[t - e] = createOwner(), () => this.ft(t));
481
566
  }
482
567
  } catch (t) {
483
- for (let t = s; t < e; t++) if ((t < r || t >= n) && h[t - s]) h[t - s].dispose();
568
+ for (let t = e; t < i; t++) if ((t < n || t >= r) && f[t - e]) f[t - e].dispose();
484
569
  throw t;
485
570
  }
486
571
  // commit: dispose the previous fallback or the rows leaving the window
487
- if (this.ht === 0) this.ln[0]?.dispose(); else for (let t = this.St; t < i; t++) if (t < s || t >= e) this.ln[t - this.St].dispose();
488
- this.yt = o;
489
- this.ln = h;
490
- this.St = s;
491
- this.ht = t;
572
+ if (this.it === 0) this.tn[0]?.dispose(); else for (let t = this.dt; t < s; t++) if (t < e || t >= i) this.tn[t - this.dt].dispose();
573
+ this.ht = o;
574
+ this.tn = f;
575
+ this.dt = e;
576
+ this.it = t;
492
577
  });
493
- return this.yt;
578
+ return this.ht;
494
579
  }
495
580
 
496
- function compare(t, s, e) {
497
- return t ? t(s) === t(e) : true;
581
+ function compare(t, e, i) {
582
+ return t ? t(e) === t(i) : true;
498
583
  }
499
584
 
500
585
  export { mapArray, repeat };