@solidjs/signals 2.0.0-beta.24 → 2.0.0-beta.26

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.
@@ -8,7 +8,7 @@ import "../core/verdict.js";
8
8
 
9
9
  import "../core/effect.js";
10
10
 
11
- import { $TARGET, STORE_OVERRIDE, STORE_OPTIMISTIC_OVERRIDE, STORE_VALUE, STORE_NODE, storeLookup, STORE_LOOKUP, $PROXY, isWrappable, wrap, notifySelf, $TRACK, STORE_DESC, STORE_HAS, symbolKeyedRecords, getKeys, $DELETED, getStoreSymbols } from "./store.js";
11
+ import { $TARGET, STORE_SHALLOW, STORE_OVERRIDE, STORE_OPTIMISTIC_OVERRIDE, STORE_VALUE, STORE_LOOKUP, $PROXY, storeLookup, markRawIngest, STORE_NODE, $TRACK, STORE_HAS, notifySelf, isWrappable, rawValuesUsed, isRawValue, wrap, STORE_DESC, symbolKeyedRecords, $DELETED, getKeys, getStoreSymbols, STORE_WRAP, lookupTarget } from "./store.js";
12
12
 
13
13
  // Enumerate a node record's keys. Keeps the common string-key path on the
14
14
  // `Object.keys` fast path; only records currently holding a user symbol node
@@ -17,9 +17,9 @@ import { $TARGET, STORE_OVERRIDE, STORE_OPTIMISTIC_OVERRIDE, STORE_VALUE, STORE_
17
17
  function nodeKeys(e) {
18
18
  const t = Object.keys(e);
19
19
  if (symbolKeyedRecords.has(e)) {
20
- const r = Object.getOwnPropertySymbols(e);
21
- for (let e = 0, n = r.length; e < n; e++) {
22
- if (r[e] !== $TRACK) t.push(r[e]);
20
+ const n = Object.getOwnPropertySymbols(e);
21
+ for (let e = 0, a = n.length; e < a; e++) {
22
+ if (n[e] !== $TRACK) t.push(n[e]);
23
23
  }
24
24
  }
25
25
  return t;
@@ -32,56 +32,56 @@ function unwrap(e) {
32
32
  return e[$TARGET]?.[STORE_VALUE] ?? e;
33
33
  }
34
34
 
35
- function getOverrideValue(e, t, r, n) {
36
- if (n && r in n) return n[r];
37
- return t && r in t ? t[r] : e[r];
35
+ function getOverrideValue(e, t, n, a) {
36
+ if (a && n in a) return a[n];
37
+ return t && n in t ? t[n] : e[n];
38
38
  }
39
39
 
40
40
  // Append `o`'s *enumerable* own symbol keys from a pre-fetched symbol list.
41
- function addEnumSymbols(e, t, r) {
42
- for (let n = 0, a = t.length; n < a; n++) {
43
- if (Object.prototype.propertyIsEnumerable.call(e, t[n])) r.add(t[n]);
41
+ function addEnumSymbols(e, t, n) {
42
+ for (let a = 0, r = t.length; a < r; a++) {
43
+ if (Object.prototype.propertyIsEnumerable.call(e, t[a])) n.add(t[a]);
44
44
  }
45
45
  }
46
46
 
47
- function getAllKeys(e, t, r) {
47
+ function getAllKeys(e, t, n) {
48
48
  // Symbols are merged explicitly below; keep the common string-key path on
49
49
  // Object.keys() and avoid reflecting the base symbols twice.
50
- const n = getKeys(e, t);
51
- const a = Object.keys(r);
50
+ const a = getKeys(e, t);
51
+ const r = Object.keys(n);
52
52
  // `value` can be a wrapped store (store-in-store) whose ownKeys trap tracks;
53
53
  // mirror `getKeys` and enumerate its symbols untracked in that case.
54
54
  const i = e[$TARGET] ? untrack(() => Object.getOwnPropertySymbols(e)) : Object.getOwnPropertySymbols(e);
55
- const o = Object.getOwnPropertySymbols(r);
55
+ const s = Object.getOwnPropertySymbols(n);
56
56
  // Symbol-free diff (the overwhelmingly common case) stays on the exact
57
57
  // pre-#2851 path, including the identical-key-sets fast path from #2756.
58
- if (i.length === 0 && o.length === 0) {
59
- if (n.length === a.length) {
58
+ if (i.length === 0 && s.length === 0) {
59
+ if (a.length === r.length) {
60
60
  let e = true;
61
- for (let t = 0; t < n.length; t++) {
62
- if (n[t] !== a[t]) {
61
+ for (let t = 0; t < a.length; t++) {
62
+ if (a[t] !== r[t]) {
63
63
  e = false;
64
64
  break;
65
65
  }
66
66
  }
67
- if (e) return n;
67
+ if (e) return a;
68
68
  }
69
- const e = new Set(n);
70
- for (let t = 0; t < a.length; t++) e.add(a[t]);
69
+ const e = new Set(a);
70
+ for (let t = 0; t < r.length; t++) e.add(r[t]);
71
71
  return Array.from(e);
72
72
  }
73
73
  // Symbol-aware diff (#2851): base symbols join the set, then override
74
74
  // adds/deletes are re-applied so a `$DELETED` symbol stays deleted, then
75
75
  // `next`'s keys (a key present in next is never deleted by the diff).
76
- const l = new Set(n);
76
+ const l = new Set(a);
77
77
  addEnumSymbols(e, i, l);
78
78
  if (t) {
79
79
  for (const e of Reflect.ownKeys(t)) {
80
80
  t[e] === $DELETED ? l.delete(e) : l.add(e);
81
81
  }
82
82
  }
83
- for (let e = 0; e < a.length; e++) l.add(a[e]);
84
- addEnumSymbols(r, o, l);
83
+ for (let e = 0; e < r.length; e++) l.add(r[e]);
84
+ addEnumSymbols(n, s, l);
85
85
  return Array.from(l);
86
86
  }
87
87
 
@@ -96,8 +96,8 @@ function itemKey(e, t) {
96
96
  return isWrappable(e) ? t(e) : e;
97
97
  }
98
98
 
99
- function keyedMatch(e, t, r) {
100
- return e === t || isWrappable(e) && isWrappable(t) && r(e) === r(t);
99
+ function keyedMatch(e, t, n) {
100
+ return e === t || isWrappable(e) && isWrappable(t) && n(e) === n(t);
101
101
  }
102
102
 
103
103
  // Array reconciliation updates the slots it visits, then swaps STORE_VALUE.
@@ -105,32 +105,72 @@ function keyedMatch(e, t, r) {
105
105
  // and `in` dependencies should follow the new value's membership. Use
106
106
  // membership rather than length arithmetic so sparse arrays and named array
107
107
  // props behave like normal property reads.
108
+ // Inline key iteration on purpose: these loops run per array target per
109
+ // reconcile pass, and a shared helper means a closure + per-key call in
110
+ // instruction counts. String-only records (the common case) iterate in
111
+ // place; symbol-bearing records take the nodeKeys array path.
108
112
  function syncArrayNodeMembership(e, t) {
109
- let r = e[STORE_NODE];
110
- if (r) {
111
- const e = nodeKeys(r);
112
- for (let n = 0, a = e.length; n < a; n++) {
113
- const a = e[n];
114
- a in t || setSignal(r[a], undefined);
113
+ let n = e[STORE_NODE];
114
+ if (n) {
115
+ if (symbolKeyedRecords.has(n)) {
116
+ const e = nodeKeys(n);
117
+ for (let a = 0, r = e.length; a < r; a++) {
118
+ e[a] in t || setSignal(n[e[a]], undefined);
119
+ }
120
+ } else {
121
+ for (const e in n) {
122
+ e in t || setSignal(n[e], undefined);
123
+ }
115
124
  }
116
125
  }
117
- if (r = e[STORE_HAS]) {
118
- const e = nodeKeys(r);
119
- for (let n = 0, a = e.length; n < a; n++) {
120
- const a = e[n];
121
- setSignal(r[a], a in t);
126
+ if (n = e[STORE_HAS]) {
127
+ if (symbolKeyedRecords.has(n)) {
128
+ const e = nodeKeys(n);
129
+ for (let a = 0, r = e.length; a < r; a++) {
130
+ setSignal(n[e[a]], e[a] in t);
131
+ }
132
+ } else {
133
+ for (const e in n) {
134
+ setSignal(n[e], e in t);
135
+ }
122
136
  }
123
137
  }
124
138
  }
125
139
 
140
+ // Recurse into a matched wrappable child pair without manufacturing a proxy:
141
+ // resolve the child's target through the lookup and dispatch directly. A
142
+ // lookup miss means the child was never observed — no proxy, no nodes, no
143
+ // subscribers anywhere below — so the parent's swap making `next`
144
+ // authoritative IS the whole update; a later read wraps next's child on
145
+ // demand. This turns the diff from O(previous graph) into O(observed graph)
146
+ // and drops the wrap()/$PROXY/$TARGET round-trip per visited child.
147
+ // Wrap-family stores (projections/optimistic) own child proxy creation and
148
+ // keep the proxy-based recursion.
149
+ function applyStateChild(e, t, n, a) {
150
+ if (n[STORE_WRAP] !== undefined) {
151
+ applyState(e, wrap(t, n), a);
152
+ return;
153
+ }
154
+ const r = t[$TARGET] ?? storeLookup.get(t);
155
+ if (r === undefined) return;
156
+ e = unwrap(e);
157
+ if (r[STORE_SHALLOW]) {
158
+ applyStateShallow(e, r);
159
+ } else if (r[STORE_OVERRIDE] || r[STORE_OPTIMISTIC_OVERRIDE]) {
160
+ applyStateSlow(e, r, a);
161
+ } else {
162
+ applyStateFast(e, r, a);
163
+ }
164
+ }
165
+
126
166
  // Reconcile a single array slot: recurse into a wrappable pair, otherwise replace
127
167
  // the node's value outright (covers object→primitive and primitive→object).
128
- function applyArrayItem(e, t, r, n, a) {
129
- if (isWrappable(e) && isWrappable(t)) {
130
- const i = wrap(t, r);
131
- n && setSignal(n, i);
132
- applyState(e, i, a);
133
- } else n && setSignal(n, wrapValue(e, r));
168
+ function applyArrayItem(e, t, n, a, r) {
169
+ if (isWrappable(e) && isWrappable(t) && !(rawValuesUsed && (isRawValue(t) || isRawValue(e)))) {
170
+ const i = wrap(t, n);
171
+ a && setSignal(a, i);
172
+ applyState(e, i, r);
173
+ } else a && setSignal(a, wrapValue(e, n));
134
174
  }
135
175
 
136
176
  /**
@@ -144,20 +184,53 @@ function applyArrayItem(e, t, r, n, a) {
144
184
  * and stay pruned exactly as before; keys the main loop already visited
145
185
  * (node present) are skipped. Callers gate on the parent's own flag and on
146
186
  * `$TRACK` absence (an enumeration-tracked record already diffs every key).
147
- */ function applyDescendants(e, t, r, n, a, i, o) {
148
- const l = r[STORE_LOOKUP] || storeLookup;
149
- const s = (i ? getKeys(e, i) : Object.keys(e)).concat(getStoreSymbols(e, i));
150
- for (let p = 0, f = s.length; p < f; p++) {
151
- const f = s[p];
152
- if (n?.[f]) continue;
187
+ */ function applyDescendants(e, t, n, a, r, i, s) {
188
+ const l = n[STORE_LOOKUP] || storeLookup;
189
+ if (i) {
190
+ const n = getKeys(e, i).concat(getStoreSymbols(e, i));
191
+ for (let o = 0, f = n.length; o < f; o++) {
192
+ const f = n[o];
193
+ if (a?.[f]) continue;
153
194
  // main loop already diffed this slot
154
- const c = unwrap(i ? getOverrideValue(e, i, f, o) : e[f]);
155
- if (!isWrappable(c)) continue;
156
- const u = (l.get(c) ?? storeLookup.get(c))?.[$TARGET];
157
- if (!u?.[STORE_DESC]) continue;
158
- const y = unwrap(t[f]);
159
- if (c === y || !isWrappable(y) || Array.isArray(c) !== Array.isArray(y) || a(c) != null && a(c) !== a(y)) continue;
160
- applyState(y, wrap(c, r), a);
195
+ const p = unwrap(getOverrideValue(e, i, f, s));
196
+ if (!isWrappable(p)) continue;
197
+ descendInto(p, t[f], l, r);
198
+ }
199
+ return;
200
+ }
201
+ // No-override path (every applyStateFast call): iterate in place instead of
202
+ // building keys + symbols + concat arrays per object per pass. The cheap
203
+ // bails (noded key, primitive value) stay inline — only genuine descent
204
+ // candidates pay a call.
205
+ for (const n in e) {
206
+ if (a?.[n]) continue;
207
+ // main loop already diffed this slot
208
+ const i = unwrap(e[n]);
209
+ if (!isWrappable(i)) continue;
210
+ descendInto(i, t[n], l, r);
211
+ }
212
+ const o = Object.getOwnPropertySymbols(e);
213
+ for (let n = 0, i = o.length; n < i; n++) {
214
+ if (Object.prototype.propertyIsEnumerable.call(e, o[n])) {
215
+ if (a?.[o[n]]) continue;
216
+ const i = unwrap(e[o[n]]);
217
+ if (!isWrappable(i)) continue;
218
+ descendInto(i, t[o[n]], l, r);
219
+ }
220
+ }
221
+ }
222
+
223
+ function descendInto(e, t, n, a) {
224
+ const r = lookupTarget(e, n);
225
+ if (!r?.[STORE_DESC]) return;
226
+ const i = unwrap(t);
227
+ if (e === i || !isWrappable(i) || Array.isArray(e) !== Array.isArray(i) || a(e) != null && a(e) !== a(i)) return;
228
+ if (r[STORE_SHALLOW]) {
229
+ applyStateShallow(i, r);
230
+ } else if (r[STORE_OVERRIDE] || r[STORE_OPTIMISTIC_OVERRIDE]) {
231
+ applyStateSlow(i, r, a);
232
+ } else {
233
+ applyStateFast(i, r, a);
161
234
  }
162
235
  }
163
236
 
@@ -166,242 +239,363 @@ function applyArrayItem(e, t, r, n, a) {
166
239
  // appropriate body. The fast body never calls `getOverrideValue` and never
167
240
  // branches on a `fastPath` boolean, so V8 sees a tighter, more inlinable
168
241
  // shape for the overwhelmingly common case of plain stores.
169
- function applyState(e, t, r) {
242
+ function applyState(e, t, n) {
170
243
  // Array items and root calls can pass a store proxy as `next`; normalize to
171
244
  // its raw value or the swap would set a store's STORE_VALUE to its own proxy.
172
245
  e = unwrap(e);
173
- const n = t?.[$TARGET];
174
- if (!n) return;
175
- if (n[STORE_OVERRIDE] || n[STORE_OPTIMISTIC_OVERRIDE]) {
176
- applyStateSlow(e, n, r);
246
+ const a = t?.[$TARGET];
247
+ if (!a) return;
248
+ if (a[STORE_SHALLOW]) {
249
+ applyStateShallow(e, a);
250
+ } else if (a[STORE_OVERRIDE] || a[STORE_OPTIMISTIC_OVERRIDE]) {
251
+ applyStateSlow(e, a, n);
177
252
  } else {
178
- applyStateFast(e, n, r);
253
+ applyStateFast(e, a, n);
179
254
  }
180
255
  }
181
256
 
182
- function applyStateFast(e, t, r) {
183
- const n = t[STORE_VALUE];
184
- if (e === n) return;
185
- const a = t[STORE_NODE];
257
+ // Shallow boundary diff: the target's own keys are reactive, its values are
258
+ // raw records replaced by reference — no recursion, no wrapping. Arrays merge
259
+ // positionally (below a shallow boundary the VALUE is the identity; keyed
260
+ // row identity belongs to the consumer, e.g. <For keyed>). Incoming
261
+ // wrappables are sticky-marked raw so they present raw through every store.
262
+ // One pass over a shallow target's node record: replace changed slots with
263
+ // raw next values, null out slots absent from next. Returns whether anything
264
+ // differed.
265
+ function shallowDiffNodes(e, t, n, a) {
266
+ let r = false;
267
+ for (const i in e) {
268
+ if (a && i === "length") continue;
269
+ if (i in t) {
270
+ const a = t[i];
271
+ if (a !== n(i)) {
272
+ r = true;
273
+ setSignal(e[i], a);
274
+ }
275
+ } else {
276
+ r = true;
277
+ setSignal(e[i], undefined);
278
+ }
279
+ }
280
+ return r;
281
+ }
282
+
283
+ function applyStateShallow(e, t, n) {
284
+ const a = t[STORE_VALUE];
285
+ const r = t[STORE_OVERRIDE];
286
+ const i = t[STORE_OPTIMISTIC_OVERRIDE];
287
+ if (e === a && !r && !i) return;
288
+ // Setter-staged writes fold into the diff: previous values resolve through
289
+ // the override layers (so a replaced slot compares against what readers
290
+ // saw), and the regular override clears with the swap — reconcile makes
291
+ // `next` the authoritative base, same as the deep slow path.
292
+ const prevAt = e => {
293
+ const t = getOverrideValue(a, r, e, i);
294
+ return t === $DELETED ? undefined : t;
295
+ };
296
+ t[STORE_OVERRIDE] = undefined;
297
+ const s = t[STORE_LOOKUP];
298
+ s !== undefined ? s.set(e, t[$PROXY]) : storeLookup.set(e, t);
299
+ t[STORE_VALUE] = e;
300
+ markRawIngest(e);
301
+ const l = t[STORE_NODE];
302
+ const o = l && l[$TRACK];
303
+ let f = false;
304
+ if (Array.isArray(a)) {
305
+ const n = r?.length ?? i?.length ?? a.length;
306
+ if (l) {
307
+ f = shallowDiffNodes(l, e, prevAt, true);
308
+ if (l.length && n !== e.length) setSignal(l.length, e.length);
309
+ }
310
+ if (!f && (o || t[STORE_HAS])) {
311
+ // Slots without nodes still feed $TRACK enumerators / `in` probes.
312
+ if (n !== e.length) f = true; else {
313
+ for (let t = 0, n = e.length; t < n; t++) {
314
+ if (prevAt(t) !== e[t]) {
315
+ f = true;
316
+ break;
317
+ }
318
+ }
319
+ }
320
+ }
321
+ } else {
322
+ if (l) {
323
+ f = shallowDiffNodes(l, e, prevAt, false);
324
+ }
325
+ if (!f && (o || t[STORE_HAS])) f = true;
326
+ }
327
+ let p = t[STORE_HAS];
328
+ if (p) {
329
+ for (const t in p) {
330
+ setSignal(p[t], t in e);
331
+ }
332
+ }
333
+ f && notifySelf(t);
334
+ }
335
+
336
+ function applyStateFast(e, t, n) {
337
+ const a = t[STORE_VALUE];
338
+ if (e === a) return;
339
+ const r = t[STORE_NODE];
186
340
  // swap
187
- (t[STORE_LOOKUP] || storeLookup).set(e, t[$PROXY]);
341
+ {
342
+ const n = t[STORE_LOOKUP];
343
+ n !== undefined ? n.set(e, t[$PROXY]) : storeLookup.set(e, t);
344
+ }
188
345
  t[STORE_VALUE] = e;
189
346
  // merge
190
- if (Array.isArray(n)) {
347
+ if (Array.isArray(a)) {
191
348
  let i = false;
192
- const o = n.length;
193
- if (e.length && o && isWrappable(e[0]) && r(e[0]) != null) {
194
- let l, s, p, f, c, u, y, S;
195
- for (p = 0, f = Math.min(o, e.length); p < f && keyedMatch(u = n[p], e[p], r); p++) {
196
- isWrappable(u) && isWrappable(e[p]) && applyState(e[p], wrap(u, t), r);
349
+ const s = a.length;
350
+ if (e.length && s && isWrappable(e[0]) && n(e[0]) != null) {
351
+ let l, o, f, p, u, c, S, R;
352
+ for (f = 0, p = Math.min(s, e.length); f < p && keyedMatch(c = a[f], e[f], n); f++) {
353
+ // keyedMatch established both sides wrappable unless they're the
354
+ // SAME reference — and an identical slot is a guaranteed no-op
355
+ // (the child's STORE_VALUE tracks the previous graph), so skip
356
+ // the recursion dispatch entirely. Raw-marked values are leaves:
357
+ // replace the slot node instead of recursing.
358
+ if (c !== e[f]) {
359
+ if (rawValuesUsed && (isRawValue(c) || isRawValue(e[f]))) {
360
+ r?.[f] && setSignal(r[f], wrapValue(e[f], t));
361
+ } else applyStateChild(e[f], c, t, n);
362
+ }
197
363
  }
364
+ // Every position key-matched at equal length (the steady-state shape
365
+ // of a polling tick): membership, length, and order are unchanged —
366
+ // nothing below could do observable work, so skip the staging
367
+ // allocations and membership sync outright.
368
+ if (f === e.length && f === s) return;
198
369
  const E = new Array(e.length), O = new Map;
199
- for (f = o - 1, c = e.length - 1; f >= p && c >= p && keyedMatch(u = n[f], e[c], r); f--,
200
- c--) {
201
- E[c] = u;
370
+ for (p = s - 1, u = e.length - 1; p >= f && u >= f && keyedMatch(c = a[p], e[u], n); p--,
371
+ u--) {
372
+ E[u] = c;
202
373
  }
203
- if (p > c || p > f) {
204
- for (s = p; s <= c; s++) {
374
+ if (f > u || f > p) {
375
+ for (o = f; o <= u; o++) {
205
376
  i = true;
206
- a?.[s] && setSignal(a[s], wrapValue(e[s], t));
377
+ r?.[o] && setSignal(r[o], wrapValue(e[o], t));
207
378
  }
208
- for (;s < e.length; s++) {
379
+ for (;o < e.length; o++) {
209
380
  i = true;
210
- applyArrayItem(e[s], E[s], t, a?.[s], r);
381
+ applyArrayItem(e[o], E[o], t, r?.[o], n);
211
382
  }
212
383
  syncArrayNodeMembership(t, e);
213
- (i || o !== e.length) && notifySelf(t);
214
- o !== e.length && a?.length && setSignal(a.length, e.length);
384
+ (i || s !== e.length) && notifySelf(t);
385
+ s !== e.length && r?.length && setSignal(r.length, e.length);
215
386
  return;
216
387
  }
217
- y = new Array(c + 1);
218
- for (s = c; s >= p; s--) {
219
- u = e[s];
220
- S = itemKey(u, r);
221
- l = O.get(S);
222
- y[s] = l === undefined ? -1 : l;
223
- O.set(S, s);
388
+ S = new Array(u + 1);
389
+ for (o = u; o >= f; o--) {
390
+ c = e[o];
391
+ R = itemKey(c, n);
392
+ l = O.get(R);
393
+ S[o] = l === undefined ? -1 : l;
394
+ O.set(R, o);
224
395
  }
225
- for (l = p; l <= f; l++) {
226
- u = n[l];
227
- S = itemKey(u, r);
228
- s = O.get(S);
229
- if (s !== undefined && s !== -1) {
230
- E[s] = u;
231
- s = y[s];
232
- O.set(S, s);
396
+ for (l = f; l <= p; l++) {
397
+ c = a[l];
398
+ R = itemKey(c, n);
399
+ o = O.get(R);
400
+ if (o !== undefined && o !== -1) {
401
+ E[o] = c;
402
+ o = S[o];
403
+ O.set(R, o);
233
404
  }
234
405
  }
235
- for (s = p; s < e.length; s++) {
236
- if (s in E) {
237
- applyArrayItem(e[s], E[s], t, a?.[s], r);
238
- } else a?.[s] && setSignal(a[s], wrapValue(e[s], t));
406
+ for (o = f; o < e.length; o++) {
407
+ if (o in E) {
408
+ applyArrayItem(e[o], E[o], t, r?.[o], n);
409
+ } else r?.[o] && setSignal(r[o], wrapValue(e[o], t));
239
410
  }
240
- if (p < e.length) i = true;
411
+ if (f < e.length) i = true;
241
412
  } else if (e.length) {
242
- for (let o = 0, l = e.length; o < l; o++) {
243
- const l = n[o];
244
- if (isWrappable(l) && isWrappable(e[o])) applyState(e[o], wrap(l, t), r); else {
245
- if (l !== e[o]) i = true;
246
- a?.[o] && setSignal(a[o], wrapValue(e[o], t));
413
+ for (let s = 0, l = e.length; s < l; s++) {
414
+ const l = a[s];
415
+ if (isWrappable(l) && isWrappable(e[s]) && !(rawValuesUsed && (isRawValue(l) || isRawValue(e[s])))) {
416
+ if (l !== e[s]) applyStateChild(e[s], l, t, n);
417
+ } else {
418
+ if (l !== e[s]) i = true;
419
+ r?.[s] && setSignal(r[s], wrapValue(e[s], t));
247
420
  }
248
421
  }
249
422
  }
250
423
  syncArrayNodeMembership(t, e);
251
- if (o !== e.length) {
424
+ if (s !== e.length) {
252
425
  i = true;
253
- a?.length && setSignal(a.length, e.length);
426
+ r?.length && setSignal(r.length, e.length);
254
427
  }
255
428
  i && notifySelf(t);
256
429
  return;
257
430
  }
258
431
  // values
259
432
  let i = t[STORE_NODE];
260
- let o;
433
+ let s;
261
434
  if (i) {
262
- o = i[$TRACK];
263
- if (o || symbolKeyedRecords.has(i)) {
264
- const a = o ? getAllKeys(n, undefined, e) : nodeKeys(i);
265
- for (let l = 0, s = a.length; l < s; l++) {
266
- diffNodeKey(a[l], i, n, e, t, o, r);
435
+ s = i[$TRACK];
436
+ // The per-key body is duplicated across both loops on purpose: this is
437
+ // the hottest object-diff site and a shared helper costs a per-key call
438
+ // in instruction counts (CodSpeed regressed ~7% on deep-tree reconciles
439
+ // with the extracted form).
440
+ if (s || symbolKeyedRecords.has(i)) {
441
+ const r = s ? getAllKeys(a, undefined, e) : nodeKeys(i);
442
+ for (let l = 0, o = r.length; l < o; l++) {
443
+ const o = r[l];
444
+ const f = i[o];
445
+ const p = unwrap(a[o]);
446
+ const u = unwrap(e[o]);
447
+ if (p === u) continue;
448
+ if (!p || !isWrappable(p) || !isWrappable(u) ||
449
+ // Raw-marked values are leaves replaced by reference — a "wrappable
450
+ // pair" is only recursable when both sides are actual store children.
451
+ rawValuesUsed && (isRawValue(p) || isRawValue(u)) || Array.isArray(p) !== Array.isArray(u) || n(p) != null && n(p) !== n(u)) {
452
+ s && setSignal(s, void 0);
453
+ f && setSignal(f, isWrappable(u) ? wrap(u, t) : u);
454
+ } else applyStateChild(u, p, t, n);
267
455
  }
268
456
  } else {
269
457
  // Untracked, string-only node records (the overwhelmingly common case)
270
458
  // iterate in place — nodeKeys() allocated a fresh key array per object
271
459
  // per pass, which dominates allocation on large-graph reconciles.
272
- for (const a in i) {
273
- diffNodeKey(a, i, n, e, t, o, r);
460
+ for (const r in i) {
461
+ const l = i[r];
462
+ const o = unwrap(a[r]);
463
+ const f = unwrap(e[r]);
464
+ if (o === f) continue;
465
+ if (!o || !isWrappable(o) || !isWrappable(f) ||
466
+ // Raw-marked values are leaves replaced by reference — a "wrappable
467
+ // pair" is only recursable when both sides are actual store children.
468
+ rawValuesUsed && (isRawValue(o) || isRawValue(f)) || Array.isArray(o) !== Array.isArray(f) || n(o) != null && n(o) !== n(f)) {
469
+ s && setSignal(s, void 0);
470
+ l && setSignal(l, isWrappable(f) ? wrap(f, t) : f);
471
+ } else applyStateChild(f, o, t, n);
274
472
  }
275
473
  }
276
474
  }
277
- if (!o && t[STORE_DESC]) applyDescendants(n, e, t, i, r);
475
+ if (!s && t[STORE_DESC]) applyDescendants(a, e, t, i, n);
278
476
  // has
279
477
  if (i = t[STORE_HAS]) {
280
478
  const t = nodeKeys(i);
281
- for (let r = 0, n = t.length; r < n; r++) {
282
- const n = t[r];
283
- setSignal(i[n], n in e);
479
+ for (let n = 0, a = t.length; n < a; n++) {
480
+ const a = t[n];
481
+ setSignal(i[a], a in e);
284
482
  }
285
483
  }
286
484
  }
287
485
 
288
- // One node-key step of the fast object diff — shared by the array-iterating
289
- // (tracked / symbol-keyed) and for-in (plain) loops in applyStateFast.
290
- function diffNodeKey(e, t, r, n, a, i, o) {
291
- const l = t[e];
292
- const s = unwrap(r[e]);
293
- let p = unwrap(n[e]);
294
- if (s === p) return;
295
- if (!s || !isWrappable(s) || !isWrappable(p) || Array.isArray(s) !== Array.isArray(p) || o(s) != null && o(s) !== o(p)) {
296
- i && setSignal(i, void 0);
297
- l && setSignal(l, isWrappable(p) ? wrap(p, a) : p);
298
- } else applyState(p, wrap(s, a), o);
299
- }
300
-
301
- function applyStateSlow(e, t, r) {
302
- const n = t[STORE_VALUE];
303
- const a = t[STORE_OVERRIDE];
486
+ function applyStateSlow(e, t, n) {
487
+ const a = t[STORE_VALUE];
488
+ const r = t[STORE_OVERRIDE];
304
489
  const i = t[STORE_OPTIMISTIC_OVERRIDE];
305
- let o = t[STORE_NODE];
490
+ let s = t[STORE_NODE];
306
491
  // swap
307
- (t[STORE_LOOKUP] || storeLookup).set(e, t[$PROXY]);
492
+ {
493
+ const n = t[STORE_LOOKUP];
494
+ n !== undefined ? n.set(e, t[$PROXY]) : storeLookup.set(e, t);
495
+ }
308
496
  t[STORE_VALUE] = e;
309
497
  t[STORE_OVERRIDE] = undefined;
310
498
  // merge
311
- if (Array.isArray(n)) {
499
+ if (Array.isArray(a)) {
312
500
  let l = false;
313
- const s = getOverrideValue(n, a, "length", i);
314
- if (e.length && s && isWrappable(e[0]) && r(e[0]) != null) {
315
- let p, f, c, u, y, S, E, O;
316
- for (c = 0, u = Math.min(s, e.length); c < u && keyedMatch(S = getOverrideValue(n, a, c, i), e[c], r); c++) {
317
- isWrappable(S) && isWrappable(e[c]) && applyState(e[c], wrap(S, t), r);
501
+ const o = getOverrideValue(a, r, "length", i);
502
+ if (e.length && o && isWrappable(e[0]) && n(e[0]) != null) {
503
+ let f, p, u, c, S, R, E, O;
504
+ for (u = 0, c = Math.min(o, e.length); u < c && keyedMatch(R = getOverrideValue(a, r, u, i), e[u], n); u++) {
505
+ if (isWrappable(R) && isWrappable(e[u]) && R !== e[u]) {
506
+ if (rawValuesUsed && (isRawValue(R) || isRawValue(e[u]))) {
507
+ s?.[u] && setSignal(s[u], wrapValue(e[u], t));
508
+ } else applyState(e[u], wrap(R, t), n);
509
+ }
318
510
  }
319
- const d = new Array(e.length), R = new Map;
320
- for (u = s - 1, y = e.length - 1; u >= c && y >= c && keyedMatch(S = getOverrideValue(n, a, u, i), e[y], r); u--,
321
- y--) {
322
- d[y] = S;
511
+ const y = new Array(e.length), d = new Map;
512
+ for (c = o - 1, S = e.length - 1; c >= u && S >= u && keyedMatch(R = getOverrideValue(a, r, c, i), e[S], n); c--,
513
+ S--) {
514
+ y[S] = R;
323
515
  }
324
- if (c > y || c > u) {
325
- for (f = c; f <= y; f++) {
516
+ if (u > S || u > c) {
517
+ for (p = u; p <= S; p++) {
326
518
  l = true;
327
- o?.[f] && setSignal(o[f], wrapValue(e[f], t));
519
+ s?.[p] && setSignal(s[p], wrapValue(e[p], t));
328
520
  }
329
- for (;f < e.length; f++) {
521
+ for (;p < e.length; p++) {
330
522
  l = true;
331
- applyArrayItem(e[f], d[f], t, o?.[f], r);
523
+ applyArrayItem(e[p], y[p], t, s?.[p], n);
332
524
  }
333
- const n = e.length;
525
+ const a = e.length;
334
526
  syncArrayNodeMembership(t, e);
335
- (l || s !== n) && notifySelf(t);
336
- s !== n && o?.length && setSignal(o.length, n);
527
+ (l || o !== a) && notifySelf(t);
528
+ o !== a && s?.length && setSignal(s.length, a);
337
529
  return;
338
530
  }
339
- E = new Array(y + 1);
340
- for (f = y; f >= c; f--) {
341
- S = e[f];
342
- O = itemKey(S, r);
343
- p = R.get(O);
344
- E[f] = p === undefined ? -1 : p;
345
- R.set(O, f);
531
+ E = new Array(S + 1);
532
+ for (p = S; p >= u; p--) {
533
+ R = e[p];
534
+ O = itemKey(R, n);
535
+ f = d.get(O);
536
+ E[p] = f === undefined ? -1 : f;
537
+ d.set(O, p);
346
538
  }
347
- for (p = c; p <= u; p++) {
348
- S = getOverrideValue(n, a, p, i);
349
- O = itemKey(S, r);
350
- f = R.get(O);
351
- if (f !== undefined && f !== -1) {
352
- d[f] = S;
353
- f = E[f];
354
- R.set(O, f);
539
+ for (f = u; f <= c; f++) {
540
+ R = getOverrideValue(a, r, f, i);
541
+ O = itemKey(R, n);
542
+ p = d.get(O);
543
+ if (p !== undefined && p !== -1) {
544
+ y[p] = R;
545
+ p = E[p];
546
+ d.set(O, p);
355
547
  }
356
548
  }
357
- for (f = c; f < e.length; f++) {
358
- if (f in d) {
359
- applyArrayItem(e[f], d[f], t, o?.[f], r);
360
- } else o?.[f] && setSignal(o[f], wrapValue(e[f], t));
549
+ for (p = u; p < e.length; p++) {
550
+ if (p in y) {
551
+ applyArrayItem(e[p], y[p], t, s?.[p], n);
552
+ } else s?.[p] && setSignal(s[p], wrapValue(e[p], t));
361
553
  }
362
- if (c < e.length) l = true;
554
+ if (u < e.length) l = true;
363
555
  } else if (e.length) {
364
- for (let s = 0, p = e.length; s < p; s++) {
365
- const p = getOverrideValue(n, a, s, i);
366
- if (isWrappable(p) && isWrappable(e[s])) applyState(e[s], wrap(p, t), r); else {
367
- if (p !== e[s]) l = true;
368
- o?.[s] && setSignal(o[s], wrapValue(e[s], t));
556
+ for (let o = 0, f = e.length; o < f; o++) {
557
+ const f = getOverrideValue(a, r, o, i);
558
+ if (isWrappable(f) && isWrappable(e[o]) && !(rawValuesUsed && (isRawValue(f) || isRawValue(e[o])))) {
559
+ if (f !== e[o]) applyState(e[o], wrap(f, t), n);
560
+ } else {
561
+ if (f !== e[o]) l = true;
562
+ s?.[o] && setSignal(s[o], wrapValue(e[o], t));
369
563
  }
370
564
  }
371
565
  }
372
- const p = e.length;
566
+ const f = e.length;
373
567
  syncArrayNodeMembership(t, e);
374
- if (s !== p) {
568
+ if (o !== f) {
375
569
  l = true;
376
- o?.length && setSignal(o.length, p);
570
+ s?.length && setSignal(s.length, f);
377
571
  }
378
572
  l && notifySelf(t);
379
573
  return;
380
574
  }
381
575
  // values
382
576
  let l;
383
- if (o) {
384
- l = o[$TRACK];
385
- const s = l ? getAllKeys(n, a, e) : nodeKeys(o);
386
- for (let p = 0, f = s.length; p < f; p++) {
387
- const f = s[p];
388
- const c = o[f];
389
- const u = unwrap(getOverrideValue(n, a, f, i));
390
- let y = unwrap(e[f]);
391
- if (u === y) continue;
392
- if (!u || !isWrappable(u) || !isWrappable(y) || Array.isArray(u) !== Array.isArray(y) || r(u) != null && r(u) !== r(y)) {
577
+ if (s) {
578
+ l = s[$TRACK];
579
+ const o = l ? getAllKeys(a, r, e) : nodeKeys(s);
580
+ for (let f = 0, p = o.length; f < p; f++) {
581
+ const p = o[f];
582
+ const u = s[p];
583
+ const c = unwrap(getOverrideValue(a, r, p, i));
584
+ let S = unwrap(e[p]);
585
+ if (c === S) continue;
586
+ if (!c || !isWrappable(c) || !isWrappable(S) || rawValuesUsed && (isRawValue(c) || isRawValue(S)) || Array.isArray(c) !== Array.isArray(S) || n(c) != null && n(c) !== n(S)) {
393
587
  l && setSignal(l, void 0);
394
- c && setSignal(c, isWrappable(y) ? wrap(y, t) : y);
395
- } else applyState(y, wrap(u, t), r);
588
+ u && setSignal(u, isWrappable(S) ? wrap(S, t) : S);
589
+ } else applyState(S, wrap(c, t), n);
396
590
  }
397
591
  }
398
- if (!l && t[STORE_DESC]) applyDescendants(n, e, t, o, r, a, i);
592
+ if (!l && t[STORE_DESC]) applyDescendants(a, e, t, s, n, r, i);
399
593
  // has
400
- if (o = t[STORE_HAS]) {
401
- const t = nodeKeys(o);
402
- for (let r = 0, n = t.length; r < n; r++) {
403
- const n = t[r];
404
- setSignal(o[n], n in e);
594
+ if (s = t[STORE_HAS]) {
595
+ const t = nodeKeys(s);
596
+ for (let n = 0, a = t.length; n < a; n++) {
597
+ const a = t[n];
598
+ setSignal(s[a], a in e);
405
599
  }
406
600
  }
407
601
  }
@@ -441,16 +635,16 @@ const NOKEY = () => null;
441
635
  * setStats(reconcile(nextStats, null));
442
636
  * ```
443
637
  */ function reconcile(e, t = "id") {
444
- return r => {
445
- if (r == null) throw new Error("");
638
+ return n => {
639
+ if (n == null) throw new Error("");
446
640
  if (t === null) {
447
- applyState(e, r, NOKEY);
641
+ applyState(e, n, NOKEY);
448
642
  return;
449
643
  }
450
- const n = typeof t === "string" ? e => e[t] : t;
451
- const a = n(r);
452
- if (a !== undefined && n(e) !== a) throw new Error("");
453
- applyState(e, r, n);
644
+ const a = typeof t === "string" ? e => e[t] : t;
645
+ const r = a(n);
646
+ if (r !== undefined && a(e) !== r) throw new Error("");
647
+ applyState(e, n, a);
454
648
  };
455
649
  }
456
650