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