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

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 (66) hide show
  1. package/dist/dev-shared.js +248 -189
  2. package/dist/dev.attribution.js +124 -41
  3. package/dist/dev.js +553 -322
  4. package/dist/observe/affects.js +34 -31
  5. package/dist/observe/boundaries.js +166 -132
  6. package/dist/observe/core/async.js +90 -83
  7. package/dist/observe/core/attribution-queries.js +1 -1
  8. package/dist/observe/core/attribution.js +281 -197
  9. package/dist/observe/core/constants.js +7 -2
  10. package/dist/observe/core/context.js +3 -3
  11. package/dist/observe/core/core.js +277 -245
  12. package/dist/observe/core/dev.js +3 -3
  13. package/dist/observe/core/effect.js +21 -21
  14. package/dist/observe/core/external.js +2 -2
  15. package/dist/observe/core/graph.js +25 -25
  16. package/dist/observe/core/heap.js +9 -9
  17. package/dist/observe/core/lanes.js +206 -160
  18. package/dist/observe/core/owner.js +27 -27
  19. package/dist/observe/core/scheduler.js +92 -86
  20. package/dist/observe/core/verdict.js +66 -54
  21. package/dist/observe/map.js +35 -35
  22. package/dist/observe/reveal.js +24 -24
  23. package/dist/observe/signals.js +38 -28
  24. package/dist/observe/store/affects.js +75 -68
  25. package/dist/observe/store/hooks.js +13 -0
  26. package/dist/observe/store/index.js +0 -2
  27. package/dist/observe/store/optimistic.js +67 -34
  28. package/dist/observe/store/projection.js +37 -31
  29. package/dist/observe/store/reconcile.js +32 -30
  30. package/dist/observe/store/store.js +128 -85
  31. package/dist/prod/affects.js +34 -31
  32. package/dist/prod/boundaries.js +143 -109
  33. package/dist/prod/core/async.js +64 -67
  34. package/dist/prod/core/constants.js +7 -2
  35. package/dist/prod/core/context.js +3 -3
  36. package/dist/prod/core/core.js +302 -270
  37. package/dist/prod/core/effect.js +38 -38
  38. package/dist/prod/core/external.js +4 -4
  39. package/dist/prod/core/graph.js +27 -27
  40. package/dist/prod/core/heap.js +30 -30
  41. package/dist/prod/core/lanes.js +257 -211
  42. package/dist/prod/core/owner.js +39 -39
  43. package/dist/prod/core/scheduler.js +125 -119
  44. package/dist/prod/core/verdict.js +70 -58
  45. package/dist/prod/map.js +73 -73
  46. package/dist/prod/reveal.js +38 -38
  47. package/dist/prod/signals.js +30 -20
  48. package/dist/prod/store/affects.js +75 -68
  49. package/dist/prod/store/hooks.js +13 -0
  50. package/dist/prod/store/index.js +0 -2
  51. package/dist/prod/store/optimistic.js +114 -81
  52. package/dist/prod/store/projection.js +30 -24
  53. package/dist/prod/store/reconcile.js +32 -30
  54. package/dist/prod/store/store.js +136 -93
  55. package/dist/types/boundaries.d.ts +4 -0
  56. package/dist/types/core/attribution.d.ts +6 -2
  57. package/dist/types/core/constants.d.ts +5 -0
  58. package/dist/types/core/core.d.ts +33 -3
  59. package/dist/types/core/lanes.d.ts +10 -0
  60. package/dist/types/core/scheduler.d.ts +12 -6
  61. package/dist/types/core/types.d.ts +0 -6
  62. package/dist/types/store/affects.d.ts +11 -0
  63. package/dist/types/store/hooks.d.ts +48 -0
  64. package/dist/types/store/index.d.ts +0 -1
  65. package/dist/types/store/store.d.ts +3 -38
  66. package/package.json +1 -1
@@ -10,7 +10,9 @@ import { GlobalQueue, txOf, list, insertSubs, laneDirty, notifyEpoch, schedule }
10
10
 
11
11
  import { runProjectionComputed } from "./projection.js";
12
12
 
13
- import { wrap, storeSetter, LaneView, committed, getNode, unwrapValue, sameKey, installOptHooks, resolveChainedRaw, getHasNode, targetsEqual, getContainerNode, cloneRaw } from "./store.js";
13
+ import { wrap, storeSetter, LaneView, committed, getNode, unwrapValue, sameKey, resolveChainedRaw, getHasNode, targetsEqual, getContainerNode, cloneRaw } from "./store.js";
14
+
15
+ import { installOptHooks } from "./hooks.js";
14
16
 
15
17
  import { $OWNER } from "./target.js";
16
18
 
@@ -79,7 +81,7 @@ function notifyOptimisticWrites(e, n) {
79
81
  // inner store's truth for its key before guessing over it.
80
82
  const o = e.ch ? e.v : null;
81
83
  const guess = (e, n, i, r) => {
82
- if (o !== null) e.pe = i ? n in t : unwrapValue(t[n]);
84
+ if (o !== null) e.Ae = i ? n in t : unwrapValue(t[n]);
83
85
  // By value: a function leaf is a value, not an updater (#3017).
84
86
  optimisticWrite(e, () => r);
85
87
  };
@@ -89,17 +91,17 @@ function notifyOptimisticWrites(e, n) {
89
91
  // A chained draft holds the inner store's raws; a raw is the inner slot it
90
92
  // resolves to (as a read of it serves) — the row a held write staged is
91
93
  // the same row, not a guess over the slot (#3796).
92
- const f = n[t];
93
- const a = unwrapValue(o && f ? resolveChainedRaw(e, t, f) : f);
94
+ const a = n[t];
95
+ const f = unwrapValue(o && a ? resolveChainedRaw(e, t, a) : a);
94
96
  if (!(t in i)) {
95
- guess(getNode(e, t), t, false, a);
97
+ guess(getNode(e, t), t, false, f);
96
98
  guess(getHasNode(e, t), t, true, true);
97
99
  s = true;
98
100
  } else {
99
101
  const n = i[t];
100
- const f = unwrapValue(o && n ? resolveChainedRaw(e, t, n) : n);
101
- if (!isEqual(f, a) && !targetsEqual(f, a)) {
102
- guess(getNode(e, t), t, false, a);
102
+ const a = unwrapValue(o && n ? resolveChainedRaw(e, t, n) : n);
103
+ if (!isEqual(a, f) && !targetsEqual(a, f)) {
104
+ guess(getNode(e, t), t, false, f);
103
105
  if (r) s = true;
104
106
  }
105
107
  }
@@ -120,16 +122,16 @@ function notifyOptimisticWrites(e, n) {
120
122
  // The rows the setter removed from the view it saw, by key: a later
121
123
  // re-base over another action's truth must not bring them back.
122
124
  let s = null;
123
- const f = e.fam.key;
124
- if (r && f !== null && f !== undefined) {
125
+ const a = e.fam.key;
126
+ if (r && a !== null && a !== undefined) {
125
127
  const e = new Set;
126
128
  for (let n = 0; n < o.length; n++) {
127
- const t = isWrappable(o[n]) ? f(o[n]) : undefined;
129
+ const t = isWrappable(o[n]) ? a(o[n]) : undefined;
128
130
  if (t !== undefined) e.add(t);
129
131
  }
130
132
  for (let n = 0; n < i.length; n++) {
131
133
  const t = unwrapValue(i[n]);
132
- const r = isWrappable(t) ? f(t) : undefined;
134
+ const r = isWrappable(t) ? a(t) : undefined;
133
135
  if (r !== undefined && !e.has(r)) (s ??= new Set).add(r);
134
136
  }
135
137
  }
@@ -137,10 +139,10 @@ function notifyOptimisticWrites(e, n) {
137
139
  }
138
140
  if (e.dk !== null) optimisticWrite(e.dk, {});
139
141
  markOverlaid(e);
140
- const f = optStaged.get(e);
141
- if (f === undefined) return null;
142
+ const a = optStaged.get(e);
143
+ if (a === undefined) return null;
142
144
  optStaged.delete(e);
143
- return f;
145
+ return a;
144
146
  }
145
147
 
146
148
  /** The container comparator's lane arm (store.ts `containerEquals`): an
@@ -157,9 +159,40 @@ function rowSame(e, n, t) {
157
159
  if (t === null || !isWrappable(e) || !isWrappable(n)) return false;
158
160
  const i = t(unwrapValue(e));
159
161
  const r = t(unwrapValue(n));
162
+ // Keyless rows are identified by position — reconcile's fallback when the
163
+ // key is missing. The caller already paired these by index, so two keyless
164
+ // rows are the same row and the landing confirms the arrangement instead of
165
+ // dissolving it. Dissolving held an external echo under the action, and a
166
+ // mainline `until` then timed out on a source that already contained it
167
+ // (#3898). Keyed rows still match only on the key.
168
+ if (i === undefined && r === undefined) return true;
160
169
  return i !== undefined && sameKey(i, r);
161
170
  }
162
171
 
172
+ /** Whether reconcile would write anything into `a` from `b`: wrappable
173
+ * pairs descend, leaves compare as its writes do. A key mismatch is a
174
+ * replacement, which a content difference already says. Plain data — the
175
+ * server's echo of a guessed row is a new object with the same contents. */ function echoes(e, n) {
176
+ if (e === n) return true;
177
+ if (!isWrappable(e) || !isWrappable(n)) return false;
178
+ e = unwrapValue(e);
179
+ n = unwrapValue(n);
180
+ const t = Object.keys(e);
181
+ if (Array.isArray(e) !== Array.isArray(n) || t.length !== Object.keys(n).length) return false;
182
+ for (const i of t) if (!hasOwn.call(n, i) || !echoes(e[i], n[i])) return false;
183
+ return true;
184
+ }
185
+
186
+ // A cycle (a row that references itself) is not plain data: no match, so
187
+ // the landing stays a correction.
188
+ GlobalQueue.tn = (e, n) => {
189
+ try {
190
+ return echoes(e, n);
191
+ } catch {
192
+ return false;
193
+ }
194
+ };
195
+
163
196
  function arrangementEqual(e, n, t) {
164
197
  if (Array.isArray(e) !== Array.isArray(n)) return false;
165
198
  if (Array.isArray(e)) {
@@ -193,18 +226,18 @@ function arrangementEqual(e, n, t) {
193
226
  }
194
227
  }
195
228
  for (let s = 0; s < n.length; s++) {
196
- const f = n[s];
197
- let a;
198
- if (isWrappable(f)) {
229
+ const a = n[s];
230
+ let f;
231
+ if (isWrappable(a)) {
199
232
  if (o !== null) {
200
- const e = t(f);
201
- if (e !== undefined) a = o.get(e);
202
- } else if (s < i && isWrappable(unwrapValue(r[s]))) a = r[s];
233
+ const e = t(a);
234
+ if (e !== undefined) f = o.get(e);
235
+ } else if (s < i && isWrappable(unwrapValue(r[s]))) f = r[s];
203
236
  }
204
- if (a !== undefined) {
205
- if (unwrapValue(a) !== f) reconcileDraft(a, f, t);
206
- if (e[s] !== a) e[s] = a;
207
- } else if (unwrapValue(e[s]) !== f) e[s] = f;
237
+ if (f !== undefined) {
238
+ if (unwrapValue(f) !== a) reconcileDraft(f, a, t);
239
+ if (e[s] !== f) e[s] = f;
240
+ } else if (unwrapValue(e[s]) !== a) e[s] = a;
208
241
  }
209
242
  if (e.length !== n.length) e.length = n.length;
210
243
  return;
@@ -233,10 +266,10 @@ function arrangementEqual(e, n, t) {
233
266
  * node is born for it as the lane's derivation), on `length` only when the
234
267
  * committed length differs, and as the `LaneView`'s base for structural
235
268
  * reads. Unkeyed families compose
236
- * positionally — the guess stands as written (the documented residue). */ GlobalQueue.En = (e, n) => {
237
- const t = e.ot;
238
- if (t === undefined || e.rt !== $TRACK) return;
239
- const i = e.m.Z;
269
+ * positionally — the guess stands as written (the documented residue). */ GlobalQueue.an = (e, n) => {
270
+ const t = e.lt;
271
+ if (t === undefined || e.ot !== $TRACK) return;
272
+ const i = e.m.B;
240
273
  if (!(i instanceof LaneView)) return;
241
274
  if (!Array.isArray(n) || !Array.isArray(i.rows)) {
242
275
  i.base = n;
@@ -251,45 +284,45 @@ function arrangementEqual(e, n, t) {
251
284
  if (i !== undefined && !o.has(i)) o.set(i, t);
252
285
  }
253
286
  const s = new Set;
254
- const f = [];
255
- const a = i.rows;
256
- for (let e = 0; e < a.length; e++) {
257
- const n = a[e];
287
+ const a = [];
288
+ const f = i.rows;
289
+ for (let e = 0; e < f.length; e++) {
290
+ const n = f[e];
258
291
  const t = isWrappable(n) ? r(n) : undefined;
259
292
  const i = t !== undefined ? o.get(t) : undefined;
260
293
  if (i !== undefined) {
261
- f.push(i);
294
+ a.push(i);
262
295
  s.add(t);
263
- } else f.push(n);
296
+ } else a.push(n);
264
297
  }
265
- const l = i.removed;
298
+ const u = i.removed;
266
299
  for (let e = 0; e < n.length; e++) {
267
300
  const t = n[e];
268
301
  const i = isWrappable(t) ? r(t) : undefined;
269
- if (i !== undefined && !s.has(i) && (l === null || !l.has(i))) f.push(t);
302
+ if (i !== undefined && !s.has(i) && (u === null || !u.has(i))) a.push(t);
270
303
  }
271
- i.rows = f;
272
- i.base = f;
273
- const u = txOf(e);
304
+ i.rows = a;
305
+ i.base = a;
306
+ const l = txOf(e);
274
307
  const c = committed(t);
275
308
  const d = t.n;
276
- for (let e = 0; e < f.length; e++) {
309
+ for (let e = 0; e < a.length; e++) {
277
310
  const n = d?.[e];
278
311
  // A slot the lane does not own shows the committed row: it becomes the
279
312
  // lane's only where the composed row is another row (by key — the same
280
313
  // key's new object is the same row, its target adopted it).
281
314
  if (n === undefined) {
282
- if (!sameRow(c[e], f[e], r)) laneSet(getNode(t, e), f[e], u);
283
- } else if (!sameRow(n.u & CONFIG_OVERRIDE ? n.m.Z : c[e], f[e], r)) laneSet(n, f[e], u);
315
+ if (!sameRow(c[e], a[e], r)) laneSet(getNode(t, e), a[e], l);
316
+ } else if (!sameRow(n.i & CONFIG_OVERRIDE ? n.m.B : c[e], a[e], r)) laneSet(n, a[e], l);
284
317
  }
285
318
  if (d !== null) {
286
- for (let e = f.length; ;e++) {
319
+ for (let e = a.length; ;e++) {
287
320
  const n = d[e];
288
321
  if (n === undefined) break;
289
- laneSet(n, undefined, u);
322
+ laneSet(n, undefined, l);
290
323
  }
291
324
  const e = d.length;
292
- if (e !== undefined && (e.u & CONFIG_OVERRIDE || c.length !== f.length)) laneSet(e, f.length, u);
325
+ if (e !== undefined && (e.i & CONFIG_OVERRIDE || c.length !== a.length)) laneSet(e, a.length, l);
293
326
  }
294
327
  };
295
328
 
@@ -304,11 +337,11 @@ function sameRow(e, n, t) {
304
337
  * node that was not the lane's becomes its derivation (its committed value
305
338
  * untouched beneath; the lane's end commits the latest). */ function laneSet(e, n, t) {
306
339
  const i = ext(e);
307
- if (!(e.u & CONFIG_OVERRIDE)) {
308
- e.u |= CONFIG_OVERRIDE;
340
+ if (!(e.i & CONFIG_OVERRIDE)) {
341
+ e.i |= CONFIG_OVERRIDE;
309
342
  list(e, t);
310
- } else if (i.Z === n) return;
311
- i.Z = n;
343
+ } else if (i.B === n) return;
344
+ i.B = n;
312
345
  if (e.t !== null) {
313
346
  insertSubs(e);
314
347
  laneDirty(e, t);
@@ -354,7 +387,7 @@ function sweepOverlaid() {
354
387
  // The container shows its backing again (a shown arrangement guess
355
388
  // that ended without a landing left its `LaneView` as the value).
356
389
  const t = e.k;
357
- if (t !== null && t.pe instanceof LaneView) t.pe = t.pe.base;
390
+ if (t !== null && t.Ae instanceof LaneView) t.Ae = t.Ae.base;
358
391
  }
359
392
  if (n.size === 0) overlaidFamilies.delete(e);
360
393
  }
@@ -371,11 +404,11 @@ function sweepOverlaid() {
371
404
  if (i !== null) for (const e of Reflect.ownKeys(i)) {
372
405
  const r = i[e];
373
406
  const o = untrack(() => unwrapValue(n[e]));
374
- if (r.We && r.We(r.pe, o)) continue;
375
- r.pe = o;
407
+ if (r.ye && r.ye(r.Ae, o)) continue;
408
+ r.Ae = o;
376
409
  // (The lane's end notified it this epoch when the shown value and the
377
410
  // covered one differed — its readers re-derive already.)
378
- if (r.t !== null && r.ze !== notifyEpoch) {
411
+ if (r.t !== null && r.qe !== notifyEpoch) {
379
412
  insertSubs(r);
380
413
  t = true;
381
414
  }
@@ -384,9 +417,9 @@ function sweepOverlaid() {
384
417
  if (r !== null) for (const e of Reflect.ownKeys(r)) {
385
418
  const i = r[e];
386
419
  const o = untrack(() => e in n);
387
- if (i.pe === o) continue;
388
- i.pe = o;
389
- if (i.t !== null && i.ze !== notifyEpoch) {
420
+ if (i.Ae === o) continue;
421
+ i.Ae = o;
422
+ if (i.t !== null && i.qe !== notifyEpoch) {
390
423
  insertSubs(i);
391
424
  t = true;
392
425
  }
@@ -394,9 +427,9 @@ function sweepOverlaid() {
394
427
  // The arrangement the readers saw (the shown guess) against the inner's
395
428
  // now: a structural reader re-derives when they differ.
396
429
  const o = e.k;
397
- if (o !== null && o.t !== null && o.pe instanceof LaneView && o.ze !== notifyEpoch) {
430
+ if (o !== null && o.t !== null && o.Ae instanceof LaneView && o.qe !== notifyEpoch) {
398
431
  const i = Array.isArray(n) ? untrack(() => n.map(unwrapValue)) : n;
399
- if (!arrangementEqual(o.pe.rows, i, e.fam?.key ?? null)) {
432
+ if (!arrangementEqual(o.Ae.rows, i, e.fam?.key ?? null)) {
400
433
  insertSubs(o);
401
434
  t = true;
402
435
  }
@@ -405,12 +438,12 @@ function sweepOverlaid() {
405
438
  }
406
439
 
407
440
  function carriesLane(e) {
408
- if (e.k !== null && e.k.u & CONFIG_OVERRIDE) return true;
409
- if (e.dk !== null && e.dk.u & CONFIG_OVERRIDE) return true;
441
+ if (e.k !== null && e.k.i & CONFIG_OVERRIDE) return true;
442
+ if (e.dk !== null && e.dk.i & CONFIG_OVERRIDE) return true;
410
443
  const n = e.n;
411
- if (n !== null) for (const e of Reflect.ownKeys(n)) if (n[e].u & CONFIG_OVERRIDE) return true;
444
+ if (n !== null) for (const e of Reflect.ownKeys(n)) if (n[e].i & CONFIG_OVERRIDE) return true;
412
445
  const t = e.h;
413
- if (t !== null) for (const e of Reflect.ownKeys(t)) if (t[e].u & CONFIG_OVERRIDE) return true;
446
+ if (t !== null) for (const e of Reflect.ownKeys(t)) if (t[e].i & CONFIG_OVERRIDE) return true;
414
447
  return false;
415
448
  }
416
449
 
@@ -428,9 +461,9 @@ function overlaid(e) {
428
461
  * the backing answers. */ function optimisticHas(e, n) {
429
462
  if (!overlaid(e)) return undefined;
430
463
  const t = e.h?.[n];
431
- if (t !== undefined && t.u & CONFIG_OVERRIDE) return !!shown(t);
464
+ if (t !== undefined && t.i & CONFIG_OVERRIDE) return !!shown(t);
432
465
  const i = e.n?.[n];
433
- if (i !== undefined && i.u & CONFIG_OVERRIDE) return shown(i) !== undefined;
466
+ if (i !== undefined && i.i & CONFIG_OVERRIDE) return shown(i) !== undefined;
434
467
  return undefined;
435
468
  }
436
469
 
@@ -441,7 +474,7 @@ function overlaid(e) {
441
474
  if (t === undefined) return undefined;
442
475
  if (!t) return null;
443
476
  const i = e.n?.[n];
444
- const r = i !== undefined && i.u & CONFIG_OVERRIDE ? shown(i) : e.v[n];
477
+ const r = i !== undefined && i.i & CONFIG_OVERRIDE ? shown(i) : e.v[n];
445
478
  // An array's `length` is reported as the array has it (non-configurable).
446
479
  if (n === "length" && Array.isArray(e.v)) return {
447
480
  value: r,
@@ -474,7 +507,7 @@ i = false) {
474
507
  for (const e of Reflect.ownKeys(o)) {
475
508
  const r = o[e];
476
509
  let s;
477
- if (r.u & CONFIG_OVERRIDE) s = t(r); else if (!i || (s = pendingGuessOf(r)) === NOT_PENDING) continue;
510
+ if (r.i & CONFIG_OVERRIDE) s = t(r); else if (!i || (s = pendingGuessOf(r)) === NOT_PENDING) continue;
478
511
  if (e === "length" && Array.isArray(n)) {
479
512
  if (n.length !== s) ensure().length = s;
480
513
  } else if (!isEqual(n[e], s) || !hasOwn.call(n, e)) {
@@ -486,11 +519,11 @@ i = false) {
486
519
  if (s !== null) {
487
520
  for (const e of Reflect.ownKeys(s)) {
488
521
  const o = s[e];
489
- let f;
490
- if (o.u & CONFIG_OVERRIDE) f = t(o); else if (!i || (f = pendingGuessOf(o)) === NOT_PENDING) continue;
491
- const a = !!f;
492
- const l = r ?? n;
493
- if (!a && e in l) delete ensure()[e];
522
+ let a;
523
+ if (o.i & CONFIG_OVERRIDE) a = t(o); else if (!i || (a = pendingGuessOf(o)) === NOT_PENDING) continue;
524
+ const f = !!a;
525
+ const u = r ?? n;
526
+ if (!f && e in u) delete ensure()[e];
494
527
  }
495
528
  }
496
529
  return r ?? n;
@@ -501,7 +534,7 @@ function createOptimisticStore(e, n, t) {
501
534
  const r = i ? t : n;
502
535
  const o = i ? n : e;
503
536
  const s = r?.key === undefined ? "id" : r.key;
504
- const f = {
537
+ const a = {
505
538
  map: new WeakMap,
506
539
  node: null,
507
540
  live: new Set,
@@ -509,9 +542,9 @@ function createOptimisticStore(e, n, t) {
509
542
  opt: true,
510
543
  key: typeof s === "function" ? s : s === null ? null : e => isWrappable(e) ? e[s] : undefined
511
544
  };
512
- const a = wrap(o, null, null, f);
513
- if (f.shallow) {
514
- a[$TARGET].s = true;
545
+ const f = wrap(o, null, null, a);
546
+ if (a.shallow) {
547
+ f[$TARGET].s = true;
515
548
  markRawIngest(o);
516
549
  }
517
550
  if (i) {
@@ -521,13 +554,13 @@ function createOptimisticStore(e, n, t) {
521
554
  loadingValue: undefined
522
555
  };
523
556
  const i = computed(() => {
524
- if (!f.node) f.node = getOwner();
525
- runProjectionComputed(a, n, s);
557
+ if (!a.node) a.node = getOwner();
558
+ runProjectionComputed(f, n, s);
526
559
  }, t);
527
- i.u &= ~CONFIG_AUTO_DISPOSE;
528
- f.node = i;
560
+ i.i &= ~CONFIG_AUTO_DISPOSE;
561
+ a.node = i;
529
562
  }
530
- return [ a, e => storeSetter(a, e) ];
563
+ return [ f, e => storeSetter(f, e) ];
531
564
  }
532
565
 
533
566
  export { createOptimisticStore, notifyOptimisticWrites, optimisticHas, optimisticOwnDescriptor, optimisticView, sweepOverlaid };
@@ -18,7 +18,7 @@ import { wrap, storeSetter, runDirect } from "./store.js";
18
18
 
19
19
  import { setLookup } from "./target.js";
20
20
 
21
- import { $TARGET, markRawIngest, setWriteOverride } from "./types.js";
21
+ import { $TARGET, markRawIngest, setWriteOverride, isWrappable } from "./types.js";
22
22
 
23
23
  /**
24
24
  * Store — projections (plan sec. 31, S3; INTERNALS §7/§7b).
@@ -183,16 +183,16 @@ function forEachFamilyNode(e, t) {
183
183
  // for the whole first flight — the derive works a detached shadow of the
184
184
  // seed so draft writes cannot tear through to readers (#2988). Every
185
185
  // commit point reconciles the shadow through the normal commit path.
186
- const a = i._e ? cloneState(s.v, s.s) : null;
186
+ const a = i.De ? cloneState(s.v, s.s) : null;
187
187
  // The question a write of this run answers: inside a slice, the slice's
188
188
  // (the sync pass); in a continuation — the flight still registered — the
189
189
  // flight's (`handleAsync` stamped it with the action that asked).
190
- const asking = () => question || (i.m !== null && i.m.De !== null ? i.m.Oe : 0);
190
+ const asking = () => question || (i.m !== null && i.m.Te !== null ? i.m._e : 0);
191
191
  const f = wrapDraft(e, () => c.run === u && !isDisposed(i), o, s.s,
192
192
  // A write after the run returned (a continuation, a callback) arms the
193
193
  // drain itself when no landing will.
194
194
  () => {
195
- if (!(i.G & STATUS_PENDING) && !i._e) schedule();
195
+ if (!(i.S & STATUS_PENDING) && !i.De) schedule();
196
196
  }, asking);
197
197
  // The creation run commits directly (a memo's first value is its
198
198
  // `_value`); every later run — a re-derive in a flush, an async landing
@@ -219,7 +219,7 @@ function forEachFamilyNode(e, t) {
219
219
  }
220
220
  // A landing (not the sync pass's own commit): the readers held
221
221
  // pending on the derive settle.
222
- if (!d || !(i.G & STATUS_UNINITIALIZED)) settleFamily(c);
222
+ if (!d || !(i.S & STATUS_UNINITIALIZED)) settleFamily(c);
223
223
  };
224
224
  const u = handleAsync(i, l, commit, (e, t) => {
225
225
  // The flight rejected: the leaves' readers learn it from here —
@@ -228,10 +228,10 @@ function forEachFamilyNode(e, t) {
228
228
  // reader re-derives and meets the error at its pull (memo parity,
229
229
  // #2897) — and the flush that runs them advances the clock, so a
230
230
  // later tracked re-read may retry (core `read`).
231
- if (d && i.G & STATUS_UNINITIALIZED) return;
231
+ if (d && i.S & STATUS_UNINITIALIZED) return;
232
232
  if (t) wakeFamily(c, e); else errorFamily(c);
233
233
  });
234
- if (!i._e) commit(u);
234
+ if (!i.De) commit(u);
235
235
  }, false);
236
236
  try {
237
237
  d ? runDirect(pass) : pass();
@@ -239,42 +239,48 @@ function forEachFamilyNode(e, t) {
239
239
  // A flight went up (NotReady out of the pass — the derive's own, with
240
240
  // the derive as its source): the leaves' readers learn it from here —
241
241
  // they do not subscribe to the derive.
242
- if (e instanceof NotReadyError && !d) wakeFamily(c, e);
242
+ // A synchronous error the same way (#3956: a rerun that finds the
243
+ // entry failed throws): every live reader re-derives and meets it.
244
+ if (!d) e instanceof NotReadyError ? wakeFamily(c, e) : errorFamily(c);
243
245
  throw e;
244
246
  }
245
247
  // A pass that returned with a flight up (an iterator's sync first yield):
246
248
  // the wake, as for the throw. Not a sync pass that superseded one — the
247
249
  // status it reads is the dead flight's, cleared when this pass settles
248
250
  // (#3181: a reader the settle walk had just released, re-parked for good).
249
- if (i.G & STATUS_PENDING && i.m.De !== null && !i._e && !d) wakeFamily(c, i.m?.V);
251
+ if (i.S & STATUS_PENDING && i.m.Te !== null && !i.De && !d) wakeFamily(c, i.m?.F);
250
252
  return i;
251
253
  }
252
254
 
253
255
  function createProjectionInternal(e, t, r) {
254
- const n = {
256
+ const n = r?.key === undefined ? "id" : r.key;
257
+ const o = {
255
258
  map: new WeakMap,
256
259
  node: null,
257
260
  live: new Set,
258
- shallow: !!r?.shallow
261
+ shallow: !!r?.shallow,
262
+ // The row key, for a child paired after the adoption (store.ts
263
+ // `adoptChild`, #3712) as the adoption pairs them.
264
+ key: typeof n === "function" ? n : n === null ? null : e => isWrappable(e) ? e[n] : undefined
259
265
  };
260
- const o = wrap(t, null, null, n);
261
- if (n.shallow) {
262
- o[$TARGET].s = true;
266
+ const i = wrap(t, null, null, o);
267
+ if (o.shallow) {
268
+ i[$TARGET].s = true;
263
269
  markRawIngest(t);
264
270
  }
265
- let i;
266
- if (r?.seedLoadingValue) i = {
271
+ let s;
272
+ if (r?.seedLoadingValue) s = {
267
273
  loadingValue: undefined
268
274
  };
269
- const s = computed(() => {
270
- if (!n.node) n.node = getOwner();
271
- runProjectionComputed(o, e, r?.key === undefined ? "id" : r.key);
272
- }, i);
273
- s.u &= ~CONFIG_AUTO_DISPOSE;
274
- n.node = s;
275
+ const c = computed(() => {
276
+ if (!o.node) o.node = getOwner();
277
+ runProjectionComputed(i, e, n);
278
+ }, s);
279
+ c.i &= ~CONFIG_AUTO_DISPOSE;
280
+ o.node = c;
275
281
  return {
276
- store: o,
277
- node: s
282
+ store: i,
283
+ node: c
278
284
  };
279
285
  }
280
286
 
@@ -2,7 +2,9 @@ import { isEqual } from "../core/core.js";
2
2
 
3
3
  import { isOwned, $OWNER, storeLookup, lookupTarget } from "./target.js";
4
4
 
5
- import { userWriting, optHooks, unwrapValue, adoptPB, sameKey, materializePB, notifyFold, targetsEqual, bumpDeep, notifyKeyValue, notifyKeyDiff, notifyFoldTail, hasAccessorFlag } from "./store.js";
5
+ import { userWriting, unwrapValue, adoptPB, sameKey, materializePB, notifyFold, sameRow, targetsEqual, bumpDeep, notifyKeyValue, notifyKeyDiff, notifyFoldTail, hasAccessorFlag } from "./store.js";
6
+
7
+ import { optHooks } from "./hooks.js";
6
8
 
7
9
  import { $TARGET, markRawIngest, isWrappable, rawValuesUsed, isRawValue } from "./types.js";
8
10
 
@@ -73,33 +75,33 @@ import { $TARGET, markRawIngest, isWrappable, rawValuesUsed, isRawValue } from "
73
75
  // — key matching from the arrangement the guesses made (R28), so a row
74
76
  // the guess added keeps its proxy when the server returns it.
75
77
  const i = l?.opt === true ? optHooks.view(e, f) : f;
76
- const u = Array.isArray(n);
77
- const s = e.s === true;
78
+ const s = Array.isArray(n);
79
+ const u = e.s === true;
78
80
  const r = f;
79
81
  adoptPB(e, n, false);
80
- if (s) markRawIngest(n);
81
- if (Array.isArray(i) !== u) {
82
+ if (u) markRawIngest(n);
83
+ if (Array.isArray(i) !== s) {
82
84
  notifyFold(e, r, n);
83
85
  return;
84
86
  }
85
- if (u) {
87
+ if (s) {
86
88
  const f = i;
87
- const u = n;
89
+ const s = n;
88
90
  const a = e.n;
89
91
  let c = 0;
90
- if (t && !s) {
92
+ if (t && !u) {
91
93
  const i = f.length;
92
- const s = u.length;
94
+ const u = s.length;
93
95
  let d = false;
94
96
  let p = 0;
95
97
  // Aligned prefix: same row (by reference or key) at the same index.
96
- for (const y = Math.min(i, s); p < y; p++) {
97
- const i = u[p];
98
- const s = f[p];
99
- if (s !== i && !(s !== null && typeof s === "object" && i !== null && typeof i === "object" && sameKey(t(s), t(i)))) break;
98
+ for (const y = Math.min(i, u); p < y; p++) {
99
+ const i = s[p];
100
+ const u = f[p];
101
+ if (u !== i && !sameRow(u, i, t)) break;
100
102
  // misaligned: fall to the keyed remainder below
101
- if ((s !== i || i !== null && typeof i === "object" && isOwned(i)) && i !== null && typeof i === "object") descend(unwrapValue(s), i, t, l, o);
102
- if (e.dk !== null && !d && !(i !== null && typeof i === "object" ? targetsEqual(s, i) : isEqual(s, i))) {
103
+ if ((u !== i || i !== null && typeof i === "object" && isOwned(i)) && i !== null && typeof i === "object") descend(unwrapValue(u), i, t, l, o);
104
+ if (e.dk !== null && !d && !(i !== null && typeof i === "object" ? targetsEqual(u, i) : isEqual(u, i))) {
103
105
  bumpDeep(e);
104
106
  d = true;
105
107
  }
@@ -111,12 +113,12 @@ import { $TARGET, markRawIngest, isWrappable, rawValuesUsed, isRawValue } from "
111
113
  }
112
114
  }
113
115
  }
114
- if (e.dk !== null && !d && p < u.length) bumpDeep(e);
116
+ if (e.dk !== null && !d && p < s.length) bumpDeep(e);
115
117
  const y = p;
116
118
  // misalignment point (== nlen on aligned ticks)
117
119
  let w = null;
118
- for (;p < u.length; p++) {
119
- const e = u[p];
120
+ for (;p < s.length; p++) {
121
+ const e = s[p];
120
122
  if (e !== null && typeof e === "object") {
121
123
  const n = t(e);
122
124
  let i;
@@ -156,12 +158,12 @@ import { $TARGET, markRawIngest, isWrappable, rawValuesUsed, isRawValue } from "
156
158
  }
157
159
  }
158
160
  } else {
159
- const i = Math.min(f.length, u.length);
160
- const d = u.length;
161
+ const i = Math.min(f.length, s.length);
162
+ const d = s.length;
161
163
  let p = false;
162
164
  for (let y = 0; y < d; y++) {
163
- const d = u[y];
164
- if (!s && y < i && d !== null && typeof d === "object") descend(unwrapValue(f[y]), d, t, l, o);
165
+ const d = s[y];
166
+ if (!u && y < i && d !== null && typeof d === "object") descend(unwrapValue(f[y]), d, t, l, o);
165
167
  if (e.dk !== null && !p && !(d !== null && typeof d === "object" ? targetsEqual(f[y], d) : isEqual(f[y], d))) {
166
168
  bumpDeep(e);
167
169
  p = true;
@@ -179,7 +181,7 @@ import { $TARGET, markRawIngest, isWrappable, rawValuesUsed, isRawValue } from "
179
181
  if (a !== null && c < e.nc) {
180
182
  for (const e of Reflect.ownKeys(a)) {
181
183
  const t = typeof e === "string" ? +e : NaN;
182
- if (!(t >= 0 && t < u.length)) notifyKeyDiff(a[e], e, r, n, false);
184
+ if (!(t >= 0 && t < s.length)) notifyKeyDiff(a[e], e, r, n, false);
183
185
  }
184
186
  }
185
187
  notifyFoldTail(e, r, n);
@@ -189,15 +191,15 @@ import { $TARGET, markRawIngest, isWrappable, rawValuesUsed, isRawValue } from "
189
191
  let c = 0;
190
192
  let d = false;
191
193
  for (const f in n) {
192
- const u = n[f];
194
+ const s = n[f];
193
195
  const p = r[f];
194
- const y = u !== null && typeof u === "object";
195
- if (p === u && (!y || !isOwned(u)) && (a === null || a[f] === undefined || !hasAccessorFlag(a[f]))) {
196
+ const y = s !== null && typeof s === "object";
197
+ if (p === s && (!y || !isOwned(s)) && (a === null || a[f] === undefined || !hasAccessorFlag(a[f]))) {
196
198
  if (a !== null && a[f] !== undefined) c++;
197
199
  continue;
198
200
  }
199
- if (y && !s) descend(unwrapValue(i[f]), u, t, l, o);
200
- if (e.dk !== null && !d && !(y ? targetsEqual(p, u) : isEqual(p, u))) {
201
+ if (y && !u) descend(unwrapValue(i[f]), s, t, l, o);
202
+ if (e.dk !== null && !d && !(y ? targetsEqual(p, s) : isEqual(p, s))) {
201
203
  bumpDeep(e);
202
204
  d = true;
203
205
  }
@@ -205,7 +207,7 @@ import { $TARGET, markRawIngest, isWrappable, rawValuesUsed, isRawValue } from "
205
207
  const e = a[f];
206
208
  if (e !== undefined) {
207
209
  c++;
208
- notifyKeyValue(e, f, p, u, r, n);
210
+ notifyKeyValue(e, f, p, s, r, n);
209
211
  }
210
212
  }
211
213
  }
@@ -214,7 +216,7 @@ import { $TARGET, markRawIngest, isWrappable, rawValuesUsed, isRawValue } from "
214
216
  const f = p[e];
215
217
  if (f === $OWNER) continue;
216
218
  const i = n[f];
217
- if (!s && i !== null && typeof i === "object") descend(unwrapValue(r[f]), i, t, l, o);
219
+ if (!u && i !== null && typeof i === "object") descend(unwrapValue(r[f]), i, t, l, o);
218
220
  if (a !== null) {
219
221
  const e = a[f];
220
222
  if (e !== undefined) {