@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.
- package/dist/dev-shared.js +785 -367
- package/dist/dev.attribution.js +75 -52
- package/dist/dev.js +1120 -567
- package/dist/observe/affects-marks.js +86 -0
- package/dist/observe/affects.js +22 -73
- package/dist/observe/boundaries.js +191 -189
- package/dist/observe/core/async.js +175 -131
- package/dist/observe/core/attribution-queries.js +1 -1
- package/dist/observe/core/attribution.js +176 -159
- package/dist/observe/core/constants.js +17 -3
- package/dist/observe/core/context.js +3 -3
- package/dist/observe/core/core.js +503 -363
- package/dist/observe/core/dev.js +66 -38
- package/dist/observe/core/effect.js +48 -48
- package/dist/observe/core/error.js +5 -3
- package/dist/observe/core/external.js +5 -6
- package/dist/observe/core/graph.js +57 -43
- package/dist/observe/core/heap.js +33 -33
- package/dist/observe/core/identities.js +62 -0
- package/dist/observe/core/lanes.js +226 -259
- package/dist/observe/core/owner.js +62 -50
- package/dist/observe/core/scheduler.js +319 -171
- package/dist/observe/core/truth.js +45 -0
- package/dist/observe/core/verdict.js +88 -50
- package/dist/observe/deferred.js +184 -0
- package/dist/observe/index.js +7 -1
- package/dist/observe/map.js +345 -260
- package/dist/observe/reveal.js +76 -76
- package/dist/observe/signals.js +41 -30
- package/dist/observe/store/affects.js +6 -6
- package/dist/observe/store/edges.js +39 -0
- package/dist/observe/store/optimistic.js +41 -36
- package/dist/observe/store/projection.js +68 -55
- package/dist/observe/store/store.js +157 -104
- package/dist/prod/affects-marks.js +86 -0
- package/dist/prod/affects.js +23 -72
- package/dist/prod/boundaries.js +155 -153
- package/dist/prod/core/async.js +171 -127
- package/dist/prod/core/constants.js +17 -3
- package/dist/prod/core/context.js +3 -3
- package/dist/prod/core/core.js +478 -342
- package/dist/prod/core/effect.js +68 -68
- package/dist/prod/core/error.js +5 -3
- package/dist/prod/core/external.js +5 -6
- package/dist/prod/core/graph.js +57 -43
- package/dist/prod/core/heap.js +38 -38
- package/dist/prod/core/identities.js +62 -0
- package/dist/prod/core/lanes.js +217 -250
- package/dist/prod/core/owner.js +62 -51
- package/dist/prod/core/scheduler.js +310 -162
- package/dist/prod/core/truth.js +45 -0
- package/dist/prod/core/verdict.js +87 -49
- package/dist/prod/deferred.js +182 -0
- package/dist/prod/index.js +7 -1
- package/dist/prod/map.js +360 -275
- package/dist/prod/reveal.js +76 -76
- package/dist/prod/signals.js +41 -30
- package/dist/prod/store/affects.js +6 -6
- package/dist/prod/store/edges.js +39 -0
- package/dist/prod/store/optimistic.js +37 -33
- package/dist/prod/store/projection.js +25 -19
- package/dist/prod/store/store.js +142 -96
- package/dist/types/affects-marks.d.ts +14 -0
- package/dist/types/boundaries.d.ts +1 -1
- package/dist/types/core/async.d.ts +10 -0
- package/dist/types/core/constants.d.ts +13 -1
- package/dist/types/core/core.d.ts +44 -2
- package/dist/types/core/dev.d.ts +43 -13
- package/dist/types/core/graph.d.ts +11 -0
- package/dist/types/core/identities.d.ts +17 -0
- package/dist/types/core/index.d.ts +2 -1
- package/dist/types/core/lanes.d.ts +2 -14
- package/dist/types/core/owner.d.ts +5 -3
- package/dist/types/core/scheduler.d.ts +47 -28
- package/dist/types/core/truth.d.ts +16 -0
- package/dist/types/core/types.d.ts +21 -0
- package/dist/types/core/verdict.d.ts +32 -0
- package/dist/types/deferred.d.ts +28 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/map.d.ts +2 -2
- package/dist/types/signals.d.ts +28 -7
- package/dist/types/store/edges.d.ts +9 -0
- package/dist/types/store/index.d.ts +10 -3
- package/dist/types/store/optimistic.d.ts +3 -2
- package/dist/types/store/projection.d.ts +4 -3
- package/dist/types/store/store.d.ts +1 -1
- package/dist/types/store/target.d.ts +16 -8
- package/dist/types/store/types.d.ts +14 -0
- package/package.json +1 -1
package/dist/observe/map.js
CHANGED
|
@@ -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,
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
const
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
|
40
|
-
name:
|
|
39
|
+
const f = computed(updateKeyedMap.bind(o), i?.name ? {
|
|
40
|
+
name: i.name
|
|
41
41
|
} : undefined);
|
|
42
|
-
|
|
43
|
-
return accessor(
|
|
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,
|
|
51
|
-
const
|
|
52
|
-
const
|
|
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 =
|
|
59
|
-
let
|
|
60
|
-
let
|
|
61
|
-
while (a <=
|
|
62
|
-
const t =
|
|
63
|
-
const
|
|
64
|
-
if (t ===
|
|
65
|
-
if (!
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
69
|
+
h[h.length - 1]++;
|
|
70
70
|
a++;
|
|
71
|
-
|
|
71
|
+
l++;
|
|
72
72
|
continue;
|
|
73
73
|
}
|
|
74
|
-
|
|
74
|
+
u = false;
|
|
75
75
|
// Bounded realignment lookahead, shorter distance wins.
|
|
76
76
|
let d = -1;
|
|
77
|
-
let p = Math.min(32 - o.length,
|
|
77
|
+
let p = Math.min(32 - o.length, r - a, c);
|
|
78
78
|
for (let t = 1; t <= p; t++) {
|
|
79
|
-
if (
|
|
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 -
|
|
87
|
-
for (let
|
|
88
|
-
if (e[
|
|
89
|
-
w =
|
|
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)
|
|
99
|
+
while (w-- > 0) f.push(l++);
|
|
100
100
|
continue;
|
|
101
101
|
}
|
|
102
|
-
if (c <= 0 || o.length === 32 ||
|
|
102
|
+
if (c <= 0 || o.length === 32 || f.length === 32) return false;
|
|
103
103
|
o.push(a++);
|
|
104
|
-
|
|
104
|
+
f.push(l++);
|
|
105
105
|
}
|
|
106
|
-
for (;a <=
|
|
106
|
+
for (;a <= r; a++) {
|
|
107
107
|
if (o.length === 32) return false;
|
|
108
108
|
o.push(a);
|
|
109
109
|
}
|
|
110
|
-
for (;
|
|
111
|
-
if (
|
|
112
|
-
|
|
110
|
+
for (;l <= i - 1; l++) {
|
|
111
|
+
if (f.length === 32) return false;
|
|
112
|
+
f.push(l);
|
|
113
113
|
}
|
|
114
|
-
return commitSmallMove(t, e,
|
|
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,
|
|
124
|
-
const o = 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 (
|
|
129
|
-
c = new Array(
|
|
130
|
-
for (
|
|
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 (
|
|
133
|
-
if (!c[
|
|
134
|
-
t =
|
|
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
|
-
|
|
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 (
|
|
150
|
+
if (s.length !== 0 || n.length !== 0) {
|
|
151
151
|
const t = new Set;
|
|
152
|
-
for (
|
|
153
|
-
for (
|
|
154
|
-
for (let e = 0; e <
|
|
155
|
-
const
|
|
156
|
-
for (let
|
|
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.
|
|
160
|
-
const
|
|
161
|
-
const
|
|
162
|
-
const d =
|
|
163
|
-
for (let t = 0; t <
|
|
164
|
-
const e =
|
|
165
|
-
const
|
|
166
|
-
if (e !==
|
|
167
|
-
for (let
|
|
168
|
-
|
|
169
|
-
d[
|
|
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 (
|
|
174
|
-
const t =
|
|
175
|
-
const e =
|
|
176
|
-
|
|
177
|
-
d[t] =
|
|
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.
|
|
180
|
-
t.
|
|
181
|
-
t.wt =
|
|
182
|
-
t
|
|
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 (
|
|
185
|
-
if (c === undefined || !c[
|
|
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.
|
|
198
|
-
const t = this.
|
|
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
|
-
runWithOwner(this.
|
|
202
|
-
let
|
|
274
|
+
runWithOwner(this.H, () => {
|
|
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
|
-
|
|
206
|
-
r[
|
|
207
|
-
return this.
|
|
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[
|
|
210
|
-
|
|
211
|
-
return this.
|
|
212
|
-
} : this.
|
|
213
|
-
const e = t[
|
|
214
|
-
n
|
|
215
|
-
return this.
|
|
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[
|
|
218
|
-
return this.
|
|
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
|
-
this.
|
|
224
|
-
this.
|
|
225
|
-
this
|
|
226
|
-
this.
|
|
300
|
+
this.H.dispose(false);
|
|
301
|
+
this.je = [];
|
|
302
|
+
this.Mt = [];
|
|
303
|
+
this.vt = [];
|
|
227
304
|
this.wt = 0;
|
|
228
|
-
this.
|
|
229
|
-
this.
|
|
305
|
+
this.Kt && (this.Kt = []);
|
|
306
|
+
this.xt && (this.xt = []);
|
|
230
307
|
}
|
|
231
|
-
if (this.
|
|
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.
|
|
235
|
-
this.
|
|
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
|
|
242
|
-
r = this.
|
|
243
|
-
|
|
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 (
|
|
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 (
|
|
327
|
+
for (i = 0; i <= s; i++) c[i]?.dispose();
|
|
248
328
|
throw t;
|
|
249
329
|
}
|
|
250
330
|
// commit
|
|
251
|
-
if (this.
|
|
331
|
+
if (this.je[0]) this.je[0].dispose();
|
|
252
332
|
// previous fallback
|
|
253
|
-
this.
|
|
254
|
-
this.
|
|
255
|
-
r && (this.
|
|
256
|
-
|
|
257
|
-
this
|
|
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,
|
|
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
|
-
|
|
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
|
|
365
|
+
const y = setSignal;
|
|
286
366
|
// skip common prefix
|
|
287
|
-
for (h = 0,
|
|
288
|
-
if (this.
|
|
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 (
|
|
293
|
-
|
|
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
|
|
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 &&
|
|
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
|
|
315
|
-
const
|
|
316
|
-
const
|
|
317
|
-
let
|
|
318
|
-
while (
|
|
319
|
-
if (
|
|
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.Je[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.Je[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
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
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
|
-
|
|
365
|
-
if
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
A[i] = this.zt[i - y];
|
|
383
|
-
M[i] = this.Je[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
|
-
|
|
389
|
-
|
|
390
|
-
|
|
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
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
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
|
|
400
|
-
if (
|
|
401
|
-
if (true && attrHooks !== null &&
|
|
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.
|
|
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://
|
|
421
|
-
*/ function repeat(t, e,
|
|
422
|
-
const
|
|
423
|
-
const
|
|
424
|
-
|
|
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 = {
|
|
509
|
+
H: createOwner(),
|
|
425
510
|
wt: 0,
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
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
|
|
435
|
-
|
|
436
|
-
return accessor(
|
|
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.
|
|
449
|
-
const t = this.
|
|
450
|
-
const e = this.
|
|
451
|
-
runWithOwner(this.
|
|
533
|
+
this.H.Ht = getOwner();
|
|
534
|
+
const t = this.qt();
|
|
535
|
+
const e = this.zt?.() || 0;
|
|
536
|
+
runWithOwner(this.H, () => {
|
|
452
537
|
if (t === 0) {
|
|
453
538
|
if (this.wt !== 0) {
|
|
454
|
-
this.
|
|
455
|
-
this.
|
|
456
|
-
this.
|
|
539
|
+
this.H.dispose(false);
|
|
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
|
|
544
|
+
this.$t = 0;
|
|
460
545
|
}
|
|
461
|
-
if (this.
|
|
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.
|
|
465
|
-
this.
|
|
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
|
|
470
|
-
const
|
|
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
|
|
474
|
-
const
|
|
558
|
+
const n = Math.max(e, this.$t);
|
|
559
|
+
const r = Math.min(i, s);
|
|
475
560
|
const o = new Array(t);
|
|
476
|
-
const
|
|
477
|
-
for (let t =
|
|
478
|
-
|
|
479
|
-
o[t - e] = this.
|
|
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 <
|
|
483
|
-
if (t >=
|
|
484
|
-
o[t - e] = runWithOwner(
|
|
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 <
|
|
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.
|
|
492
|
-
this.
|
|
493
|
-
this.
|
|
494
|
-
this
|
|
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.
|
|
582
|
+
return this.vt;
|
|
498
583
|
}
|
|
499
584
|
|
|
500
|
-
function compare(t, e,
|
|
501
|
-
return t ? t(e) === t(
|
|
585
|
+
function compare(t, e, i) {
|
|
586
|
+
return t ? t(e) === t(i) : true;
|
|
502
587
|
}
|
|
503
588
|
|
|
504
589
|
export { mapArray, repeat };
|