@solidjs/signals 2.0.0-rc.2 → 2.0.0-rc.4

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 (57) hide show
  1. package/dist/dev.js +1454 -118
  2. package/dist/node.cjs +2709 -1396
  3. package/dist/prod/affects.js +13 -12
  4. package/dist/prod/boundaries.js +39 -34
  5. package/dist/prod/core/action.js +3 -3
  6. package/dist/prod/core/async.js +48 -46
  7. package/dist/prod/core/core.js +99 -67
  8. package/dist/prod/core/effect.js +25 -28
  9. package/dist/prod/core/external.js +2 -2
  10. package/dist/prod/core/graph.js +85 -49
  11. package/dist/prod/core/heap.js +10 -10
  12. package/dist/prod/core/lanes.js +19 -19
  13. package/dist/prod/core/optimistic.js +66 -41
  14. package/dist/prod/core/owner.js +13 -13
  15. package/dist/prod/core/scheduler.js +131 -85
  16. package/dist/prod/core/verdict.js +36 -15
  17. package/dist/prod/index.js +4 -0
  18. package/dist/prod/map.js +101 -101
  19. package/dist/prod/signals.js +1 -1
  20. package/dist/prod/store/index.js +2 -0
  21. package/dist/prod/store/next/optimistic.js +65 -11
  22. package/dist/prod/store/next/patch-hooks.js +13 -0
  23. package/dist/prod/store/next/patch.js +614 -0
  24. package/dist/prod/store/next/projection.js +107 -45
  25. package/dist/prod/store/next/reconcile.js +307 -120
  26. package/dist/prod/store/next/store.js +321 -92
  27. package/dist/prod/store/next/target.js +13 -4
  28. package/dist/prod/store/store.js +5 -5
  29. package/dist/types/core/core.d.ts +15 -1
  30. package/dist/types/core/dev.d.ts +8 -0
  31. package/dist/types/core/graph.d.ts +22 -0
  32. package/dist/types/core/invariants.d.ts +1 -1
  33. package/dist/types/core/scheduler.d.ts +12 -0
  34. package/dist/types/store/index.d.ts +2 -0
  35. package/dist/types/store/next/patch-hooks.d.ts +41 -0
  36. package/dist/types/store/next/patch.d.ts +91 -0
  37. package/dist/types/store/next/reconcile.d.ts +14 -0
  38. package/dist/types/store/next/store.d.ts +30 -2
  39. package/dist/types/store/next/target.d.ts +58 -8
  40. package/dist/types-cjs/core/core.d.cts +15 -1
  41. package/dist/types-cjs/core/dev.d.cts +8 -0
  42. package/dist/types-cjs/core/graph.d.cts +22 -0
  43. package/dist/types-cjs/core/invariants.d.cts +1 -1
  44. package/dist/types-cjs/core/scheduler.d.cts +12 -0
  45. package/dist/types-cjs/store/index.d.cts +2 -0
  46. package/dist/types-cjs/store/next/patch-hooks.d.cts +41 -0
  47. package/dist/types-cjs/store/next/patch.d.cts +91 -0
  48. package/dist/types-cjs/store/next/reconcile.d.cts +14 -0
  49. package/dist/types-cjs/store/next/store.d.cts +30 -2
  50. package/dist/types-cjs/store/next/target.d.cts +58 -8
  51. package/package.json +14 -14
  52. package/dist/types/store/optimistic.d.ts +0 -45
  53. package/dist/types/store/projection.d.ts +0 -70
  54. package/dist/types/store/reconcile.d.ts +0 -46
  55. package/dist/types-cjs/store/optimistic.d.cts +0 -45
  56. package/dist/types-cjs/store/projection.d.cts +0 -70
  57. package/dist/types-cjs/store/reconcile.d.cts +0 -46
package/dist/prod/map.js CHANGED
@@ -23,23 +23,23 @@ function mapArray(t, s, i) {
23
23
  const r = s.length > 1;
24
24
  const n = s;
25
25
  const h = {
26
- jt: createOwner(),
27
- Wt: 0,
28
- xt: t,
29
- Kt: [],
30
- $t: n,
31
- qt: [],
26
+ Wt: createOwner(),
27
+ xt: 0,
28
+ Kt: t,
29
+ $t: [],
30
+ qt: n,
32
31
  zt: [],
33
- Jt: e,
34
- Xt: e || i?.keyed === false ? [] : undefined,
35
- Yt: r && i?.keyed !== false ? [] : undefined,
36
- Zt: i?.keyed === false,
37
- ts: i?.fallback
32
+ Jt: [],
33
+ Xt: e,
34
+ Yt: e || i?.keyed === false ? [] : undefined,
35
+ Zt: r && i?.keyed !== false ? [] : undefined,
36
+ ts: i?.keyed === false,
37
+ ss: i?.fallback
38
38
  };
39
39
  const o = computed(updateKeyedMap.bind(h));
40
40
  // Untracked reads inside the internal owner resolve via _parentComputed; routing
41
41
  // them through node lets store-proxy lookups see pending writes (not stale _value).
42
- h.jt.Ot = o;
42
+ h.Wt.Ot = o;
43
43
  o.T &= ~CONFIG_AUTO_DISPOSE;
44
44
  return accessor(o);
45
45
  }
@@ -58,52 +58,52 @@ const pureOptions = {
58
58
  // strong-abort ordering: removed rows now dispose AFTER the pass's new rows
59
59
  // are created (you cannot destroy state before knowing the pass will land).
60
60
  function updateKeyedMap() {
61
- const t = this.xt() || [], s = t.length;
61
+ const t = this.Kt() || [], s = t.length;
62
62
  t[$TRACK];
63
63
  // top level tracking
64
- runWithOwner(this.jt, () => {
64
+ runWithOwner(this.Wt, () => {
65
65
  let i, e, r, n,
66
66
  // Mappers write freshly-created row/index signals into the STAGE
67
67
  // arrays (`rows`/`indexes`), never into `this._rows`/`this._indexes`.
68
- h = this.Xt ? this.Zt ? () => {
68
+ h = this.Yt ? this.ts ? () => {
69
69
  r[e] = signal(t[e], pureOptions);
70
- return this.$t(accessor(r[e]), e);
70
+ return this.qt(accessor(r[e]), e);
71
71
  } : () => {
72
72
  r[e] = signal(t[e], pureOptions);
73
73
  n && (n[e] = signal(e, pureOptions));
74
- return this.$t(accessor(r[e]), n ? accessor(n[e]) : undefined);
75
- } : this.Yt ? () => {
74
+ return this.qt(accessor(r[e]), n ? accessor(n[e]) : undefined);
75
+ } : this.Zt ? () => {
76
76
  const s = t[e];
77
77
  n[e] = signal(e, pureOptions);
78
- return this.$t(s, accessor(n[e]));
78
+ return this.qt(s, accessor(n[e]));
79
79
  } : () => {
80
80
  const s = t[e];
81
- return this.$t(s);
81
+ return this.qt(s);
82
82
  };
83
83
  // fast path for empty arrays
84
84
  if (s === 0) {
85
- if (this.Wt !== 0) {
86
- this.jt.dispose(false);
85
+ if (this.xt !== 0) {
86
+ this.Wt.dispose(false);
87
+ this.Jt = [];
88
+ this.$t = [];
87
89
  this.zt = [];
88
- this.Kt = [];
89
- this.qt = [];
90
- this.Wt = 0;
91
- this.Xt && (this.Xt = []);
90
+ this.xt = 0;
92
91
  this.Yt && (this.Yt = []);
92
+ this.Zt && (this.Zt = []);
93
93
  }
94
- if (this.ts && !this.qt[0]) {
94
+ if (this.ss && !this.zt[0]) {
95
95
  // an aborted fallback attempt leaves an owner without a mapping;
96
96
  // dispose it before re-creating
97
- this.zt[0]?.dispose();
98
- this.qt[0] = runWithOwner(this.zt[0] = createOwner(), this.ts);
97
+ this.Jt[0]?.dispose();
98
+ this.zt[0] = runWithOwner(this.Jt[0] = createOwner(), this.ss);
99
99
  }
100
100
  }
101
101
  // fast path for new create
102
- else if (this.Wt === 0) {
102
+ else if (this.xt === 0) {
103
103
  const o = new Array(s);
104
104
  const c = new Array(s);
105
- r = this.Xt && new Array(s);
106
- n = this.Yt && new Array(s);
105
+ r = this.Yt && new Array(s);
106
+ n = this.Zt && new Array(s);
107
107
  try {
108
108
  for (e = 0; e < s; e++) o[e] = runWithOwner(c[e] = createOwner(), h);
109
109
  } catch (t) {
@@ -111,43 +111,43 @@ function updateKeyedMap() {
111
111
  throw t;
112
112
  }
113
113
  // commit
114
- if (this.zt[0]) this.zt[0].dispose();
114
+ if (this.Jt[0]) this.Jt[0].dispose();
115
115
  // previous fallback
116
- this.qt = o;
117
- this.zt = c;
118
- r && (this.Xt = r);
119
- n && (this.Yt = n);
120
- this.Kt = t.slice(0);
121
- this.Wt = s;
116
+ this.zt = o;
117
+ this.Jt = c;
118
+ r && (this.Yt = r);
119
+ n && (this.Zt = n);
120
+ this.$t = t.slice(0);
121
+ this.xt = s;
122
122
  } else {
123
123
  let o, c, a, f, u, p, w, l, d;
124
124
  // skip common prefix
125
- for (o = 0, c = Math.min(this.Wt, s); o < c && (this.Kt[o] === t[o] || this.Xt && compare(this.Jt, this.Kt[o], t[o])); o++) {
126
- if (this.Xt) setSignal(this.Xt[o], t[o]);
125
+ for (o = 0, c = Math.min(this.xt, s); o < c && (this.$t[o] === t[o] || this.Yt && compare(this.Xt, this.$t[o], t[o])); o++) {
126
+ if (this.Yt) setSignal(this.Yt[o], t[o]);
127
127
  }
128
128
  // skip common suffix — counted only; retained entries land in one pass
129
129
  // at commit instead of being staged and copied twice
130
- for (c = this.Wt - 1, a = s - 1; c >= o && a >= o && (this.Kt[c] === t[a] || this.Xt && compare(this.Jt, this.Kt[c], t[a])); c--,
130
+ for (c = this.xt - 1, a = s - 1; c >= o && a >= o && (this.$t[c] === t[a] || this.Yt && compare(this.Xt, this.$t[c], t[a])); c--,
131
131
  a--) ;
132
132
  // no structural change (every position matched in place at equal
133
133
  // length — the common post-reconcile shape): keep the same mapped
134
134
  // array identity so downstream consumers don't re-run at all
135
- if (o === s && this.Wt === s) {
136
- this.Kt = t.slice(0);
135
+ if (o === s && this.xt === s) {
136
+ this.$t = t.slice(0);
137
137
  return;
138
138
  }
139
- const O = s - this.Wt;
139
+ const O = s - this.xt;
140
140
  const m = new Array(s);
141
141
  const _ = new Array(s);
142
- r = this.Xt ? new Array(s) : undefined;
143
- n = this.Yt ? new Array(s) : undefined;
142
+ r = this.Yt ? new Array(s) : undefined;
143
+ n = this.Zt ? new Array(s) : undefined;
144
144
  // 0) prepare a map of all indices in the changed window of newItems,
145
145
  // scanning backwards so we encounter them in natural order
146
146
  p = new Map;
147
147
  w = new Array(a + 1);
148
148
  for (e = a; e >= o; e--) {
149
149
  f = t[e];
150
- u = this.Jt ? this.Jt(f) : f;
150
+ u = this.Xt ? this.Xt(f) : f;
151
151
  i = p.get(u);
152
152
  w[e] = i === undefined ? -1 : i;
153
153
  p.set(u, e);
@@ -156,17 +156,17 @@ function updateKeyedMap() {
156
156
  // in the new set; if so, stage them at their new positions; if not,
157
157
  // queue them for disposal at commit
158
158
  for (i = o; i <= c; i++) {
159
- f = this.Kt[i];
160
- u = this.Jt ? this.Jt(f) : f;
159
+ f = this.$t[i];
160
+ u = this.Xt ? this.Xt(f) : f;
161
161
  e = p.get(u);
162
162
  if (e !== undefined && e !== -1) {
163
- m[e] = this.qt[i];
164
- _[e] = this.zt[i];
165
- r && (r[e] = this.Xt[i]);
166
- n && (n[e] = this.Yt[i]);
163
+ m[e] = this.zt[i];
164
+ _[e] = this.Jt[i];
165
+ r && (r[e] = this.Yt[i]);
166
+ n && (n[e] = this.Zt[i]);
167
167
  e = w[e];
168
168
  p.set(u, e);
169
- } else (l ??= []).push(this.zt[i]);
169
+ } else (l ??= []).push(this.Jt[i]);
170
170
  }
171
171
  // 2) create new rows into the temp arrays; an abort disposes only these
172
172
  try {
@@ -183,38 +183,38 @@ function updateKeyedMap() {
183
183
  // into the fresh arrays, swap them in (new identity for downstream
184
184
  // change propagation), then dispose exited rows
185
185
  for (i = 0; i < o; i++) {
186
- m[i] = this.qt[i];
187
- _[i] = this.zt[i];
188
- r && (r[i] = this.Xt[i]);
189
- n && (n[i] = this.Yt[i]);
186
+ m[i] = this.zt[i];
187
+ _[i] = this.Jt[i];
188
+ r && (r[i] = this.Yt[i]);
189
+ n && (n[i] = this.Zt[i]);
190
190
  }
191
191
  for (e = o; e <= a; e++) {
192
192
  if (r) setSignal(r[e], t[e]);
193
193
  if (n) setSignal(n[e], e);
194
194
  }
195
195
  for (e = a + 1; e < s; e++) {
196
- m[e] = this.qt[e - O];
197
- _[e] = this.zt[e - O];
196
+ m[e] = this.zt[e - O];
197
+ _[e] = this.Jt[e - O];
198
198
  if (r) {
199
- r[e] = this.Xt[e - O];
199
+ r[e] = this.Yt[e - O];
200
200
  setSignal(r[e], t[e]);
201
201
  }
202
202
  if (n) {
203
- n[e] = this.Yt[e - O];
203
+ n[e] = this.Zt[e - O];
204
204
  if (O !== 0) setSignal(n[e], e);
205
205
  }
206
206
  }
207
- this.qt = m;
208
- this.zt = _;
209
- r && (this.Xt = r);
210
- n && (this.Yt = n);
211
- this.Wt = s;
207
+ this.zt = m;
208
+ this.Jt = _;
209
+ r && (this.Yt = r);
210
+ n && (this.Zt = n);
211
+ this.xt = s;
212
212
  // save a copy of the mapped items for the next update
213
- this.Kt = t.slice(0);
213
+ this.$t = t.slice(0);
214
214
  if (l) for (i = 0; i < l.length; i++) l[i].dispose();
215
215
  }
216
216
  });
217
- return this.qt;
217
+ return this.zt;
218
218
  }
219
219
 
220
220
  /**
@@ -234,21 +234,21 @@ function updateKeyedMap() {
234
234
  */ function repeat(t, s, i) {
235
235
  const e = s;
236
236
  const r = {
237
- jt: createOwner(),
238
- Wt: 0,
239
- ss: 0,
240
- es: t,
241
- $t: e,
237
+ Wt: createOwner(),
238
+ xt: 0,
239
+ es: 0,
240
+ rs: t,
241
+ qt: e,
242
+ Jt: [],
242
243
  zt: [],
243
- qt: [],
244
- rs: i?.from,
245
- ts: i?.fallback
244
+ ns: i?.from,
245
+ ss: i?.fallback
246
246
  };
247
247
  const n = computed(updateRepeat.bind(r));
248
248
  // Same as mapArray: untracked reads inside the internal owner resolve via
249
249
  // _parentComputed, so async reads in row callbacks register with the node
250
250
  // (pending tracking + post-settle retry) instead of vanishing.
251
- r.jt.Ot = n;
251
+ r.Wt.Ot = n;
252
252
  n.T &= ~CONFIG_AUTO_DISPOSE;
253
253
  return accessor(n);
254
254
  }
@@ -261,55 +261,55 @@ function updateKeyedMap() {
261
261
  // for the post-settle retry. The overlap math also subsumes the previous
262
262
  // disjoint-window/front-clear/end-clear/shift special cases.
263
263
  function updateRepeat() {
264
- const t = this.es();
265
- const s = this.rs?.() || 0;
266
- runWithOwner(this.jt, () => {
264
+ const t = this.rs();
265
+ const s = this.ns?.() || 0;
266
+ runWithOwner(this.Wt, () => {
267
267
  if (t === 0) {
268
- if (this.Wt !== 0) {
269
- this.jt.dispose(false);
268
+ if (this.xt !== 0) {
269
+ this.Wt.dispose(false);
270
+ this.Jt = [];
270
271
  this.zt = [];
271
- this.qt = [];
272
- this.Wt = 0;
272
+ this.xt = 0;
273
273
  // Reset offset to match the cleared data (#2767, repro 2).
274
- this.ss = 0;
274
+ this.es = 0;
275
275
  }
276
- if (this.ts && !this.qt[0]) {
276
+ if (this.ss && !this.zt[0]) {
277
277
  // an aborted fallback attempt leaves an owner without a mapping;
278
278
  // dispose it before re-creating
279
- this.zt[0]?.dispose();
280
- this.qt[0] = runWithOwner(this.zt[0] = createOwner(), this.ts);
279
+ this.Jt[0]?.dispose();
280
+ this.zt[0] = runWithOwner(this.Jt[0] = createOwner(), this.ss);
281
281
  }
282
282
  return;
283
283
  }
284
284
  const i = s + t;
285
- const e = this.ss + this.Wt;
285
+ const e = this.es + this.xt;
286
286
  // Retained overlap [keepStart, keepEnd) in global indexes; empty when the
287
287
  // windows are disjoint or when coming from empty/fallback.
288
- const r = Math.max(s, this.ss);
288
+ const r = Math.max(s, this.es);
289
289
  const n = Math.min(i, e);
290
290
  const h = new Array(t);
291
291
  const o = new Array(t);
292
292
  for (let t = r; t < n; t++) {
293
- o[t - s] = this.zt[t - this.ss];
294
- h[t - s] = this.qt[t - this.ss];
293
+ o[t - s] = this.Jt[t - this.es];
294
+ h[t - s] = this.zt[t - this.es];
295
295
  }
296
296
  try {
297
297
  for (let t = s; t < i; t++) {
298
298
  if (t >= r && t < n) continue;
299
- h[t - s] = runWithOwner(o[t - s] = createOwner(), () => this.$t(t));
299
+ h[t - s] = runWithOwner(o[t - s] = createOwner(), () => this.qt(t));
300
300
  }
301
301
  } catch (t) {
302
302
  for (let t = s; t < i; t++) if ((t < r || t >= n) && o[t - s]) o[t - s].dispose();
303
303
  throw t;
304
304
  }
305
305
  // commit: dispose the previous fallback or the rows leaving the window
306
- if (this.Wt === 0) this.zt[0]?.dispose(); else for (let t = this.ss; t < e; t++) if (t < s || t >= i) this.zt[t - this.ss].dispose();
307
- this.qt = h;
308
- this.zt = o;
309
- this.ss = s;
310
- this.Wt = t;
306
+ if (this.xt === 0) this.Jt[0]?.dispose(); else for (let t = this.es; t < e; t++) if (t < s || t >= i) this.Jt[t - this.es].dispose();
307
+ this.zt = h;
308
+ this.Jt = o;
309
+ this.es = s;
310
+ this.xt = t;
311
311
  });
312
- return this.qt;
312
+ return this.zt;
313
313
  }
314
314
 
315
315
  function compare(t, s, i) {
@@ -251,7 +251,7 @@ function createEffect(e, t, n) {
251
251
  // route through the inherited queue.
252
252
  const r = getOwner();
253
253
  const o = new MicrotaskQueue;
254
- o.ve = r.C;
254
+ o.ke = r.C;
255
255
  // notify() forwards up the normal chain
256
256
  r.C = o;
257
257
  // A user effect rather than a bare computed: computeds are pull-based and
@@ -10,6 +10,8 @@ import "../core/effect.js";
10
10
 
11
11
  import { createStoreNext, deepNext, snapshotNext } from "./next/store.js";
12
12
 
13
+ export { storeHasFamily, storeHasOptimisticFamily, storeIsShallow } from "./next/store.js";
14
+
13
15
  import { reconcileNextState } from "./next/reconcile.js";
14
16
 
15
17
  import { createStoreDerivedNext } from "./next/projection.js";
@@ -18,6 +18,10 @@ import { runProjectionComputedNext } from "./projection.js";
18
18
 
19
19
  import { wrapNext, runAuthoritative, storeSetterNext, hasActiveOverride, unwrapValue, getNode, getHasNode, targetsEqual, getKeySetNode, bumpDeep } from "./store.js";
20
20
 
21
+ import { patchHooks, rowHooks } from "./patch-hooks.js";
22
+
23
+ import { buildIdentityRowOps, sameKey } from "./reconcile.js";
24
+
21
25
  import { setOptHooks } from "./target.js";
22
26
 
23
27
  /**
@@ -51,14 +55,31 @@ function installNextBlockedHalf() {
51
55
  // Scheduler flush tails call _clearOptimisticStores whenever tracked
52
56
  // stores exist; next has no layer to clear — reverts are engine-native —
53
57
  // so the hook only empties the batch set.
54
- if (!GlobalQueue.Vt) {
55
- GlobalQueue.Vt = e => {
58
+ if (!GlobalQueue.Bt) {
59
+ GlobalQueue.Bt = e => {
60
+ // Patch channel (revert site): engine-native reverts flip node values
61
+ // back to committed; patched records need a forced DOM re-apply from
62
+ // the post-revert view. Emission only — next keeps no layer to clear.
63
+ for (const t of e) {
64
+ const e = t?.[$TARGET];
65
+ const n = e?.fam?.overlaid;
66
+ if (n !== undefined) {
67
+ for (const e of n) {
68
+ if (e.pc !== null && e.pc.p !== null) patchHooks.emitPatchOptimistic(e, null, null);
69
+ // Row-ops resync (family increment 2): reverts flip node values
70
+ // back engine-natively; a driven list must rebuild retention by
71
+ // row identity against the post-revert view (resolved from the
72
+ // target at drain — overrides are gone by then).
73
+ if (e.pc !== null && e.pc.ro !== null) rowHooks.emitRowOpsOptimistic(e, null, null);
74
+ }
75
+ }
76
+ }
56
77
  e.clear();
57
78
  };
58
79
  }
59
- const e = GlobalQueue.dn;
60
- GlobalQueue.dn = t => {
61
- for (const e of t.cn) {
80
+ const e = GlobalQueue.In;
81
+ GlobalQueue.In = t => {
82
+ for (const e of t.Tn) {
62
83
  const t = e?.[$TARGET];
63
84
  const n = t?.fam?.node;
64
85
  // The hold exists to keep optimistic state alive until the store's own
@@ -148,6 +169,21 @@ function createOptimisticStoreNext(e, t, n) {
148
169
  const n = e.fam?.node;
149
170
  if (n?._e) globalQueue.initTransition(n._e);
150
171
  const i = e.v;
172
+ // Patch channel (override-application site): the draft IS the intended
173
+ // visible state; prev is the view before these overrides apply. Bypasses
174
+ // the transition stash — optimism is visible in flight.
175
+ if (e.pc !== null && e.pc.p !== null) patchHooks.emitPatchOptimistic(e, t, optimisticView(e, i));
176
+ // Row-ops channel (family increment 2): optimistic STRUCTURE on an array
177
+ // rides node overrides — it never enters the reconcile walk — so a driven
178
+ // list must get its structural ops here, lane-timed. Identity diff of the
179
+ // pre-write optimistic view against the draft; aligned writes emit nothing.
180
+ if (e.pc !== null && e.pc.ro !== null && Array.isArray(t)) {
181
+ const n = optimisticView(e, i);
182
+ if (Array.isArray(n)) {
183
+ const i = buildIdentityRowOps(n, t);
184
+ if (i !== null) rowHooks.emitRowOpsOptimistic(e, t, i);
185
+ }
186
+ }
151
187
  const visible = (t, n) => {
152
188
  const i = e.n?.[t];
153
189
  return i !== undefined && hasActiveOverride(i) ? unwrapOverride(i.o?.De) : n;
@@ -198,7 +234,7 @@ function createOptimisticStoreNext(e, t, n) {
198
234
  // and the target for landing consumption (RUL-2).
199
235
  e.pb = null;
200
236
  (e.fam.overlaid ??= new Set).add(e);
201
- GlobalQueue.Nn?.(e.fam.px ?? e.px);
237
+ GlobalQueue.An?.(e.fam.px ?? e.px);
202
238
  }
203
239
 
204
240
  /**
@@ -223,7 +259,7 @@ function createOptimisticStoreNext(e, t, n) {
223
259
  e.T |= CONFIG_OPTIMISTIC;
224
260
  const i = e.o;
225
261
  if (i) {
226
- i.Et = null;
262
+ i.Nt = null;
227
263
  i.Be = undefined;
228
264
  }
229
265
  e.Pe = NOT_PENDING;
@@ -264,12 +300,16 @@ function createOptimisticStoreNext(e, t, n) {
264
300
  e.k.T |= CONFIG_OPTIMISTIC;
265
301
  const t = e.k.o;
266
302
  if (t) {
267
- t.Et = null;
303
+ t.Nt = null;
268
304
  t.Be = undefined;
269
305
  }
270
306
  insertSubs(e.k, true);
271
307
  schedule();
272
308
  }
309
+ // Patch channel (override-consumption site): visible truth flipped to
310
+ // committed for the consumed keys — force a re-apply from the live
311
+ // view so the DOM leaves the override state.
312
+ if (e.pc !== null && e.pc.p !== null) patchHooks.emitPatchOptimistic(e, null, null);
273
313
  }
274
314
  t.clear();
275
315
  });
@@ -322,7 +362,9 @@ function applyTentative(e, t, n) {
322
362
  if (n) {
323
363
  const i = n(e);
324
364
  const o = n(t);
325
- if (i !== undefined && o !== undefined && i !== o) return null;
365
+ // SameValueZero (re-audit 3, P1-3): parity with the plain reconcile
366
+ // channel — NaN keys are self-equal.
367
+ if (i !== undefined && o !== undefined && !sameKey(i, o)) return null;
326
368
  }
327
369
  return r.get(unwrapValue(e)) ?? null;
328
370
  };
@@ -337,16 +379,28 @@ function applyTentative(e, t, n) {
337
379
  const t = n(r);
338
380
  if (t !== undefined) {
339
381
  if (i === null) {
382
+ // Occurrence-aware index queues (re-audit 3, P1-3): parity with
383
+ // the plain adoption window — duplicate keys match per
384
+ // occurrence, each view row consumed once.
340
385
  i = new Map;
341
386
  for (let t = 0; t < e.length; t++) {
342
387
  const o = unwrapValue(e[t]);
343
388
  if (isWrappable(o)) {
344
389
  const e = n(o);
345
- if (e !== undefined && !i.has(e)) i.set(e, o);
390
+ if (e === undefined) continue;
391
+ const r = i.get(e);
392
+ if (r === undefined) i.set(e, t); else if (Array.isArray(r)) r.push(t); else i.set(e, [ r, t ]);
346
393
  }
347
394
  }
348
395
  }
349
- s = i.get(t);
396
+ const o = i.get(t);
397
+ if (o === undefined) s = undefined; else if (Array.isArray(o)) {
398
+ s = unwrapValue(e[o.shift()]);
399
+ if (o.length === 1) i.set(t, o[0]);
400
+ } else {
401
+ s = unwrapValue(e[o]);
402
+ i.delete(t);
403
+ }
350
404
  } else s = unwrapValue(e[o]);
351
405
  } else s = unwrapValue(e[o]);
352
406
  const c = match(s, r);
@@ -0,0 +1,13 @@
1
+ let patchHooks = null;
2
+
3
+ let rowHooks = null;
4
+
5
+ function installPatchHooks(o) {
6
+ patchHooks = o;
7
+ }
8
+
9
+ function installRowHooks(o) {
10
+ rowHooks = o;
11
+ }
12
+
13
+ export { installPatchHooks, installRowHooks, patchHooks, rowHooks };