@solidjs/signals 2.0.0-rc.8 → 2.0.0-rc.9

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 (90) hide show
  1. package/dist/dev-shared.js +1399 -285
  2. package/dist/dev.attribution.js +459 -307
  3. package/dist/dev.js +1864 -435
  4. package/dist/observe/affects.js +3 -1
  5. package/dist/observe/attribution.js +7 -1
  6. package/dist/observe/boundaries.js +209 -154
  7. package/dist/observe/core/action.js +18 -8
  8. package/dist/observe/core/async.js +220 -110
  9. package/dist/observe/core/attribution-costs.js +66 -0
  10. package/dist/observe/core/attribution-feedback.js +282 -0
  11. package/dist/observe/core/attribution-hooks.js +23 -1
  12. package/dist/observe/core/attribution-queries.js +28 -0
  13. package/dist/observe/core/attribution.js +262 -485
  14. package/dist/observe/core/constants.js +34 -1
  15. package/dist/observe/core/context.js +3 -3
  16. package/dist/observe/core/core.js +867 -367
  17. package/dist/observe/core/dev.js +78 -17
  18. package/dist/observe/core/effect.js +67 -51
  19. package/dist/observe/core/error-hooks.js +71 -0
  20. package/dist/observe/core/external.js +4 -4
  21. package/dist/observe/core/graph.js +37 -37
  22. package/dist/observe/core/heap.js +45 -45
  23. package/dist/observe/core/invariants.js +2 -0
  24. package/dist/observe/core/lanes.js +86 -49
  25. package/dist/observe/core/optimistic.js +242 -95
  26. package/dist/observe/core/owner.js +98 -84
  27. package/dist/observe/core/scheduler.js +543 -305
  28. package/dist/observe/core/verdict.js +247 -129
  29. package/dist/observe/index.js +7 -3
  30. package/dist/observe/map.js +126 -124
  31. package/dist/observe/signals.js +33 -17
  32. package/dist/observe/store/index.js +2 -0
  33. package/dist/observe/store/next/optimistic.js +141 -132
  34. package/dist/observe/store/next/projection.js +34 -21
  35. package/dist/observe/store/next/reconcile.js +58 -56
  36. package/dist/observe/store/next/store.js +260 -146
  37. package/dist/observe/store/store.js +5 -3
  38. package/dist/observe/store/utils.js +948 -135
  39. package/dist/prod/attribution.js +26 -17
  40. package/dist/prod/boundaries.js +128 -76
  41. package/dist/prod/core/action.js +16 -8
  42. package/dist/prod/core/async.js +251 -143
  43. package/dist/prod/core/constants.js +34 -1
  44. package/dist/prod/core/context.js +3 -3
  45. package/dist/prod/core/core.js +843 -347
  46. package/dist/prod/core/dev.js +17 -1
  47. package/dist/prod/core/effect.js +48 -34
  48. package/dist/prod/core/error-hooks.js +71 -0
  49. package/dist/prod/core/external.js +4 -4
  50. package/dist/prod/core/graph.js +37 -37
  51. package/dist/prod/core/heap.js +45 -45
  52. package/dist/prod/core/lanes.js +90 -53
  53. package/dist/prod/core/optimistic.js +247 -100
  54. package/dist/prod/core/owner.js +57 -45
  55. package/dist/prod/core/scheduler.js +543 -305
  56. package/dist/prod/core/verdict.js +245 -127
  57. package/dist/prod/index.js +7 -3
  58. package/dist/prod/map.js +112 -112
  59. package/dist/prod/signals.js +28 -12
  60. package/dist/prod/store/next/optimistic.js +135 -128
  61. package/dist/prod/store/next/projection.js +31 -20
  62. package/dist/prod/store/next/store.js +325 -252
  63. package/dist/prod/store/store.js +2 -2
  64. package/dist/prod/store/utils.js +946 -135
  65. package/dist/types/attribution.d.ts +7 -2
  66. package/dist/types/attribution.prod.d.ts +10 -1
  67. package/dist/types/boundaries.d.ts +10 -1
  68. package/dist/types/core/action.d.ts +12 -5
  69. package/dist/types/core/attribution-costs.d.ts +35 -0
  70. package/dist/types/core/attribution-feedback.d.ts +133 -0
  71. package/dist/types/core/attribution-hooks.d.ts +28 -3
  72. package/dist/types/core/attribution-queries.d.ts +10 -0
  73. package/dist/types/core/attribution.d.ts +51 -172
  74. package/dist/types/core/constants.d.ts +33 -0
  75. package/dist/types/core/core.d.ts +186 -5
  76. package/dist/types/core/dev.d.ts +129 -9
  77. package/dist/types/core/error-hooks.d.ts +71 -0
  78. package/dist/types/core/index.d.ts +3 -1
  79. package/dist/types/core/invariants.d.ts +4 -0
  80. package/dist/types/core/lanes.d.ts +31 -4
  81. package/dist/types/core/scheduler.d.ts +95 -2
  82. package/dist/types/core/types.d.ts +3 -0
  83. package/dist/types/index.d.ts +2 -2
  84. package/dist/types/signals.d.ts +8 -0
  85. package/dist/types/store/index.d.ts +2 -1
  86. package/dist/types/store/next/optimistic.d.ts +1 -1
  87. package/dist/types/store/next/store.d.ts +6 -5
  88. package/dist/types/store/next/target.d.ts +1 -1
  89. package/dist/types/store/utils.d.ts +177 -6
  90. package/package.json +1 -1
@@ -14,7 +14,9 @@ import "./core/invariants.js";
14
14
 
15
15
  import { OBSERVE as OBSERVE$1 } from "./core/dev.js";
16
16
 
17
- export { enforceLoadingBoundary, flush, resetErrorHalt } from "./core/scheduler.js";
17
+ export { ownerPath } from "./core/dev.js";
18
+
19
+ export { ROOT_ERROR_HOOK, enforceLoadingBoundary, flush, resetErrorHalt } from "./core/scheduler.js";
18
20
 
19
21
  export { isPending, latest } from "./core/verdict.js";
20
22
 
@@ -22,6 +24,8 @@ import "./core/effect.js";
22
24
 
23
25
  export { action } from "./core/action.js";
24
26
 
27
+ export { configureClientErrors } from "./core/error-hooks.js";
28
+
25
29
  export { createEffect, createMemo, createOptimistic, createReaction, createRenderEffect, createSignal, createTrackedEffect, onCleanup, onSettled, refresh, resolve, until } from "./signals.js";
26
30
 
27
31
  export { affects } from "./affects.js";
@@ -34,12 +38,12 @@ export { createErrorBoundary, createLoadingBoundary, createRevealOrder, flatten
34
38
 
35
39
  export { $PROXY, $TARGET, $TRACK, isWrappable } from "./store/store.js";
36
40
 
41
+ export { MergeView, OmitView, SOURCE_MEMO, SOURCE_MERGE, SOURCE_OMIT, SOURCE_PLAIN, SOURCE_PROXY, hasStaticKeys, isStatic, merge, mergeSources, mergeView, omit, omitView, resolvedTable, sourceGet, sourceHas, sourceKeys, sourceOwners, viewOf } from "./store/utils.js";
42
+
37
43
  export { createOptimisticStoreNext as createOptimisticStore } from "./store/next/optimistic.js";
38
44
 
39
45
  export { createProjectionNext as createProjection } from "./store/next/projection.js";
40
46
 
41
- export { merge, mergeSources, omit } from "./store/utils.js";
42
-
43
47
  export { storeHasFamily, storeHasOptimisticFamily, storeIsShallow } from "./store/next/store.js";
44
48
 
45
49
  export { storePath } from "./store/storePath.js";
@@ -10,6 +10,8 @@ import { attrHooks } from "./core/attribution-hooks.js";
10
10
 
11
11
  import "./core/invariants.js";
12
12
 
13
+ import "./core/dev.js";
14
+
13
15
  import "./core/verdict.js";
14
16
 
15
17
  import "./core/effect.js";
@@ -20,31 +22,31 @@ import { $TRACK } from "./store/store.js";
20
22
 
21
23
  import "./store/next/store.js";
22
24
 
23
- function mapArray(t, s, i) {
24
- const e = typeof i?.keyed === "function" ? i.keyed : undefined;
25
+ function mapArray(t, s, e) {
26
+ const i = typeof e?.keyed === "function" ? e.keyed : undefined;
25
27
  const r = s.length > 1;
26
28
  const n = s;
27
29
  const o = {
28
- an: createOwner(),
30
+ nt: createOwner(),
29
31
  ts: 0,
30
32
  ss: t,
31
33
  es: [],
32
34
  rs: n,
33
35
  ns: [],
34
36
  hs: [],
35
- _n: e,
36
- fs: e || i?.keyed === false ? [] : undefined,
37
- cs: r && i?.keyed !== false ? [] : undefined,
38
- us: i?.keyed === false,
39
- ls: i?.fallback
37
+ En: i,
38
+ fs: i || e?.keyed === false ? [] : undefined,
39
+ cs: r && e?.keyed !== false ? [] : undefined,
40
+ us: e?.keyed === false,
41
+ ls: e?.fallback
40
42
  };
41
- const h = computed(updateKeyedMap.bind(o), i?.name ? {
42
- name: i.name
43
+ const h = computed(updateKeyedMap.bind(o), e?.name ? {
44
+ name: e.name
43
45
  } : undefined);
44
46
  // Untracked reads inside the internal owner resolve via _parentComputed; routing
45
47
  // them through node lets store-proxy lookups see pending writes (not stale _value).
46
- o.an.ln = h;
47
- h.T &= ~CONFIG_AUTO_DISPOSE;
48
+ o.nt.fn = h;
49
+ h.C &= ~CONFIG_AUTO_DISPOSE;
48
50
  return accessor(h);
49
51
  }
50
52
 
@@ -52,7 +54,7 @@ const pureOptions = {
52
54
  ownedWrite: true
53
55
  };
54
56
 
55
- function trySmallMove(t, s, i, e) {
57
+ function trySmallMove(t, s, e, i) {
56
58
  const r = t.es;
57
59
  const n = t.ts - 1;
58
60
  const o = [];
@@ -60,13 +62,13 @@ function trySmallMove(t, s, i, e) {
60
62
  const f = [];
61
63
  // flat triples: oldStart, newStart, length
62
64
  let c = 256;
63
- let a = e;
64
- let u = e;
65
+ let a = i;
66
+ let u = i;
65
67
  let l = false;
66
- while (a <= n && u <= i - 1) {
68
+ while (a <= n && u <= e - 1) {
67
69
  const t = r[a];
68
- const e = s[u];
69
- if (t === e) {
70
+ const i = s[u];
71
+ if (t === i) {
70
72
  if (!l) {
71
73
  f.push(a, u, 0);
72
74
  l = true;
@@ -81,17 +83,17 @@ function trySmallMove(t, s, i, e) {
81
83
  let d = -1;
82
84
  let p = Math.min(32 - o.length, n - a, c);
83
85
  for (let t = 1; t <= p; t++) {
84
- if (r[a + t] === e) {
86
+ if (r[a + t] === i) {
85
87
  d = t;
86
88
  break;
87
89
  }
88
90
  }
89
91
  c -= d === -1 ? p : d;
90
92
  let w = -1;
91
- p = Math.min(32 - h.length, i - 1 - u, c);
92
- for (let i = 1; i <= p; i++) {
93
- if (s[u + i] === t) {
94
- w = i;
93
+ p = Math.min(32 - h.length, e - 1 - u, c);
94
+ for (let e = 1; e <= p; e++) {
95
+ if (s[u + e] === t) {
96
+ w = e;
95
97
  break;
96
98
  }
97
99
  }
@@ -112,11 +114,11 @@ function trySmallMove(t, s, i, e) {
112
114
  if (o.length === 32) return false;
113
115
  o.push(a);
114
116
  }
115
- for (;u <= i - 1; u++) {
117
+ for (;u <= e - 1; u++) {
116
118
  if (h.length === 32) return false;
117
119
  h.push(u);
118
120
  }
119
- return commitSmallMove(t, s, i, o, h, f);
121
+ return commitSmallMove(t, s, e, o, h, f);
120
122
  }
121
123
 
122
124
  /** PHASE 2 of the small-move path, in its OWN function so that a pass which
@@ -125,17 +127,17 @@ function trySmallMove(t, s, i, e) {
125
127
  * per function, and cold `replace` measured +0.5 ms with both phases in one
126
128
  * body. Pairs displaced sources with destinations (an unmatched destination
127
129
  * is a replacement/insertion → general path), then commits: slice() the live
128
- * arrays, copy shifted runs, patch displaced pairs, dispose leftovers. */ function commitSmallMove(t, s, i, e, r, n) {
130
+ * arrays, copy shifted runs, patch displaced pairs, dispose leftovers. */ function commitSmallMove(t, s, e, i, r, n) {
129
131
  const o = t.es;
130
132
  let h;
131
133
  let f;
132
134
  let c;
133
135
  if (r.length !== 0) {
134
- c = new Array(e.length);
136
+ c = new Array(i.length);
135
137
  for (f = 0; f < r.length; f++) {
136
138
  let t = -1;
137
- for (h = 0; h < e.length; h++) {
138
- if (!c[h] && o[e[h]] === s[r[f]]) {
139
+ for (h = 0; h < i.length; h++) {
140
+ if (!c[h] && o[i[h]] === s[r[f]]) {
139
141
  t = h;
140
142
  break;
141
143
  }
@@ -152,42 +154,42 @@ function trySmallMove(t, s, i, e) {
152
154
  // and if a displaced identity also occurs inside a run the two algorithms
153
155
  // can hand different occurrences different owners (row-local state moves;
154
156
  // a shrink could dispose the wrong one). Decline that case — general path.
155
- if (e.length !== 0 || r.length !== 0) {
157
+ if (i.length !== 0 || r.length !== 0) {
156
158
  const t = new Set;
157
- for (h = 0; h < e.length; h++) t.add(o[e[h]]);
159
+ for (h = 0; h < i.length; h++) t.add(o[i[h]]);
158
160
  for (f = 0; f < r.length; f++) t.add(s[r[f] >> 6]);
159
161
  for (let s = 0; s < n.length; s += 3) {
160
- const i = n[s];
161
- for (let e = 0, r = n[s + 2]; e < r; e++) if (t.has(o[i + e])) return false;
162
+ const e = n[s];
163
+ for (let i = 0, r = n[s + 2]; i < r; i++) if (t.has(o[e + i])) return false;
162
164
  }
163
165
  }
164
166
  const a = t.ns;
165
167
  const u = t.hs;
166
- const l = a.slice(0, i);
167
- const d = u.slice(0, i);
168
+ const l = a.slice(0, e);
169
+ const d = u.slice(0, e);
168
170
  for (let t = 0; t < n.length; t += 3) {
169
171
  const s = n[t];
170
- const i = n[t + 1];
171
- if (s !== i) {
172
- for (let e = 0; e < n[t + 2]; e++) {
173
- l[i + e] = a[s + e];
174
- d[i + e] = u[s + e];
172
+ const e = n[t + 1];
173
+ if (s !== e) {
174
+ for (let i = 0; i < n[t + 2]; i++) {
175
+ l[e + i] = a[s + i];
176
+ d[e + i] = u[s + i];
175
177
  }
176
178
  }
177
179
  }
178
180
  for (f = 0; f < r.length; f++) {
179
181
  const t = r[f] >> 6;
180
- const s = e[r[f] & 63];
182
+ const s = i[r[f] & 63];
181
183
  l[t] = a[s];
182
184
  d[t] = u[s];
183
185
  }
184
186
  t.ns = l;
185
187
  t.hs = d;
186
- t.ts = i;
188
+ t.ts = e;
187
189
  t.es = s.slice(0);
188
190
  // Dispose unmatched sources LAST (general-path ordering).
189
- for (h = 0; h < e.length; h++) {
190
- if (c === undefined || !c[h]) u[e[h]].dispose();
191
+ for (h = 0; h < i.length; h++) {
192
+ if (c === undefined || !c[h]) u[i[h]].dispose();
191
193
  }
192
194
  return true;
193
195
  }
@@ -196,29 +198,29 @@ function updateKeyedMap() {
196
198
  const t = this.ss() || [], s = t.length;
197
199
  t[$TRACK];
198
200
  // top level tracking
199
- runWithOwner(this.an, () => {
200
- let i, e, r, n,
201
+ runWithOwner(this.nt, () => {
202
+ let e, i, r, n,
201
203
  // Mappers write freshly-created row/index signals into the STAGE
202
204
  // arrays (`rows`/`indexes`), never into `this._rows`/`this._indexes`.
203
205
  o = this.fs ? this.us ? () => {
204
- r[e] = signal(t[e], pureOptions);
205
- return this.rs(accessor(r[e]), e);
206
+ r[i] = signal(t[i], pureOptions);
207
+ return this.rs(accessor(r[i]), i);
206
208
  } : () => {
207
- r[e] = signal(t[e], pureOptions);
208
- n && (n[e] = signal(e, pureOptions));
209
- return this.rs(accessor(r[e]), n ? accessor(n[e]) : undefined);
209
+ r[i] = signal(t[i], pureOptions);
210
+ n && (n[i] = signal(i, pureOptions));
211
+ return this.rs(accessor(r[i]), n ? accessor(n[i]) : undefined);
210
212
  } : this.cs ? () => {
211
- const s = t[e];
212
- n[e] = signal(e, pureOptions);
213
- return this.rs(s, accessor(n[e]));
213
+ const s = t[i];
214
+ n[i] = signal(i, pureOptions);
215
+ return this.rs(s, accessor(n[i]));
214
216
  } : () => {
215
- const s = t[e];
217
+ const s = t[i];
216
218
  return this.rs(s);
217
219
  };
218
220
  // fast path for empty arrays
219
221
  if (s === 0) {
220
222
  if (this.ts !== 0) {
221
- this.an.dispose(false);
223
+ this.nt.dispose(false);
222
224
  this.hs = [];
223
225
  this.es = [];
224
226
  this.ns = [];
@@ -240,9 +242,9 @@ function updateKeyedMap() {
240
242
  r = this.fs && new Array(s);
241
243
  n = this.cs && new Array(s);
242
244
  try {
243
- for (e = 0; e < s; e++) h[e] = runWithOwner(f[e] = createOwner(), o);
245
+ for (i = 0; i < s; i++) h[i] = runWithOwner(f[i] = createOwner(), o);
244
246
  } catch (t) {
245
- for (i = 0; i <= e; i++) f[i]?.dispose();
247
+ for (e = 0; e <= i; e++) f[e]?.dispose();
246
248
  throw t;
247
249
  }
248
250
  // commit
@@ -260,12 +262,12 @@ function updateKeyedMap() {
260
262
  // entered rows, for the list-identity census.
261
263
  m, O;
262
264
  // skip common prefix
263
- for (h = 0, f = Math.min(this.ts, s); h < f && (this.es[h] === t[h] || this.fs && compare(this._n, this.es[h], t[h])); h++) {
265
+ for (h = 0, f = Math.min(this.ts, s); h < f && (this.es[h] === t[h] || this.fs && compare(this.En, this.es[h], t[h])); h++) {
264
266
  if (this.fs) setSignal(this.fs[h], t[h]);
265
267
  }
266
268
  // skip common suffix — counted only; retained entries land in one pass
267
269
  // at commit instead of being staged and copied twice
268
- for (f = this.ts - 1, c = s - 1; f >= h && c >= h && (this.es[f] === t[c] || this.fs && compare(this._n, this.es[f], t[c])); f--,
270
+ for (f = this.ts - 1, c = s - 1; f >= h && c >= h && (this.es[f] === t[c] || this.fs && compare(this.En, this.es[f], t[c])); f--,
269
271
  c--) ;
270
272
  // no structural change (every position matched in place at equal
271
273
  // length — the common post-reconcile shape): keep the same mapped
@@ -287,11 +289,11 @@ function updateKeyedMap() {
287
289
  // every page's first structural pass usually is) has it nowhere.
288
290
  // ~65 compares, no allocation, and the scan function is never
289
291
  // compiled for a replace (its cold first-call compile was the cost).
290
- const i = h + (c - h >> 1);
291
- const e = t[i];
292
- const r = Math.min(f, i + 32);
293
- let n = Math.max(h, i - 32);
294
- while (n <= r && this.es[n] !== e) n++;
292
+ const e = h + (c - h >> 1);
293
+ const i = t[e];
294
+ const r = Math.min(f, e + 32);
295
+ let n = Math.max(h, e - 32);
296
+ while (n <= r && this.es[n] !== i) n++;
295
297
  if (n <= r && trySmallMove(this, t, s, h)) return;
296
298
  }
297
299
  const _ = s - this.ts;
@@ -303,67 +305,67 @@ function updateKeyedMap() {
303
305
  // scanning backwards so we encounter them in natural order
304
306
  l = new Map;
305
307
  d = new Array(c + 1);
306
- for (e = c; e >= h; e--) {
307
- a = t[e];
308
- u = this._n ? this._n(a) : a;
309
- i = l.get(u);
310
- d[e] = i === undefined ? -1 : i;
311
- l.set(u, e);
308
+ for (i = c; i >= h; i--) {
309
+ a = t[i];
310
+ u = this.En ? this.En(a) : a;
311
+ e = l.get(u);
312
+ d[i] = e === undefined ? -1 : e;
313
+ l.set(u, i);
312
314
  }
313
315
  // 1) step through the old changed window and see if items can be found
314
316
  // in the new set; if so, stage them at their new positions; if not,
315
317
  // queue them for disposal at commit
316
- for (i = h; i <= f; i++) {
317
- a = this.es[i];
318
- u = this._n ? this._n(a) : a;
319
- e = l.get(u);
320
- if (e !== undefined && e !== -1) {
321
- y[e] = this.ns[i];
322
- A[e] = this.hs[i];
323
- r && (r[e] = this.fs[i]);
324
- n && (n[e] = this.cs[i]);
325
- e = d[e];
326
- l.set(u, e);
318
+ for (e = h; e <= f; e++) {
319
+ a = this.es[e];
320
+ u = this.En ? this.En(a) : a;
321
+ i = l.get(u);
322
+ if (i !== undefined && i !== -1) {
323
+ y[i] = this.ns[e];
324
+ A[i] = this.hs[e];
325
+ r && (r[i] = this.fs[e]);
326
+ n && (n[i] = this.cs[e]);
327
+ i = d[i];
328
+ l.set(u, i);
327
329
  } else {
328
- (p ??= []).push(this.hs[i]);
330
+ (p ??= []).push(this.hs[e]);
329
331
  if (true && attrHooks !== null) (m ??= []).push(a);
330
332
  }
331
333
  }
332
334
  // 2) create new rows into the temp arrays; an abort disposes only these
333
335
  try {
334
- for (e = h; e <= c; e++) {
335
- if (A[e] !== undefined) continue;
336
- (w ??= []).push(A[e] = createOwner());
337
- if (true && attrHooks !== null) (O ??= []).push(t[e]);
338
- y[e] = runWithOwner(A[e], o);
336
+ for (i = h; i <= c; i++) {
337
+ if (A[i] !== undefined) continue;
338
+ (w ??= []).push(A[i] = createOwner());
339
+ if (true && attrHooks !== null) (O ??= []).push(t[i]);
340
+ y[i] = runWithOwner(A[i], o);
339
341
  }
340
342
  } catch (t) {
341
- if (w) for (i = 0; i < w.length; i++) w[i].dispose();
343
+ if (w) for (e = 0; e < w.length; e++) w[e].dispose();
342
344
  throw t;
343
345
  }
344
346
  // 3) commit: land the retained prefix and suffix plus the staged window
345
347
  // into the fresh arrays, swap them in (new identity for downstream
346
348
  // change propagation), then dispose exited rows
347
- for (i = 0; i < h; i++) {
348
- y[i] = this.ns[i];
349
- A[i] = this.hs[i];
350
- r && (r[i] = this.fs[i]);
351
- n && (n[i] = this.cs[i]);
349
+ for (e = 0; e < h; e++) {
350
+ y[e] = this.ns[e];
351
+ A[e] = this.hs[e];
352
+ r && (r[e] = this.fs[e]);
353
+ n && (n[e] = this.cs[e]);
352
354
  }
353
- for (e = h; e <= c; e++) {
354
- if (r) setSignal(r[e], t[e]);
355
- if (n) setSignal(n[e], e);
355
+ for (i = h; i <= c; i++) {
356
+ if (r) setSignal(r[i], t[i]);
357
+ if (n) setSignal(n[i], i);
356
358
  }
357
- for (e = c + 1; e < s; e++) {
358
- y[e] = this.ns[e - _];
359
- A[e] = this.hs[e - _];
359
+ for (i = c + 1; i < s; i++) {
360
+ y[i] = this.ns[i - _];
361
+ A[i] = this.hs[i - _];
360
362
  if (r) {
361
- r[e] = this.fs[e - _];
362
- setSignal(r[e], t[e]);
363
+ r[i] = this.fs[i - _];
364
+ setSignal(r[i], t[i]);
363
365
  }
364
366
  if (n) {
365
- n[e] = this.cs[e - _];
366
- if (_ !== 0) setSignal(n[e], e);
367
+ n[i] = this.cs[i - _];
368
+ if (_ !== 0) setSignal(n[i], i);
367
369
  }
368
370
  }
369
371
  this.ns = y;
@@ -373,8 +375,8 @@ function updateKeyedMap() {
373
375
  this.ts = s;
374
376
  // save a copy of the mapped items for the next update
375
377
  this.es = t.slice(0);
376
- if (p) for (i = 0; i < p.length; i++) p[i].dispose();
377
- if (true && attrHooks !== null && m !== undefined && O !== undefined) attrHooks.listChurn(this.an.ln, m, O, s, this._n !== undefined);
378
+ if (p) for (e = 0; e < p.length; e++) p[e].dispose();
379
+ if (true && attrHooks !== null && m !== undefined && O !== undefined) attrHooks.listChurn(this.nt.fn, m, O, s, this.En !== undefined);
378
380
  }
379
381
  });
380
382
  return this.ns;
@@ -394,25 +396,25 @@ function updateKeyedMap() {
394
396
  * ```
395
397
  *
396
398
  * @description https://docs.solidjs.com/reference/reactive-utilities/repeat
397
- */ function repeat(t, s, i) {
398
- const e = s;
399
+ */ function repeat(t, s, e) {
400
+ const i = s;
399
401
  const r = {
400
- an: createOwner(),
402
+ nt: createOwner(),
401
403
  ts: 0,
402
404
  ds: 0,
403
405
  ps: t,
404
- rs: e,
406
+ rs: i,
405
407
  hs: [],
406
408
  ns: [],
407
- ws: i?.from,
408
- ls: i?.fallback
409
+ ws: e?.from,
410
+ ls: e?.fallback
409
411
  };
410
412
  const n = computed(updateRepeat.bind(r));
411
413
  // Same as mapArray: untracked reads inside the internal owner resolve via
412
414
  // _parentComputed, so async reads in row callbacks register with the node
413
415
  // (pending tracking + post-settle retry) instead of vanishing.
414
- r.an.ln = n;
415
- n.T &= ~CONFIG_AUTO_DISPOSE;
416
+ r.nt.fn = n;
417
+ n.C &= ~CONFIG_AUTO_DISPOSE;
416
418
  return accessor(n);
417
419
  }
418
420
 
@@ -426,10 +428,10 @@ function updateKeyedMap() {
426
428
  function updateRepeat() {
427
429
  const t = this.ps();
428
430
  const s = this.ws?.() || 0;
429
- runWithOwner(this.an, () => {
431
+ runWithOwner(this.nt, () => {
430
432
  if (t === 0) {
431
433
  if (this.ts !== 0) {
432
- this.an.dispose(false);
434
+ this.nt.dispose(false);
433
435
  this.hs = [];
434
436
  this.ns = [];
435
437
  this.ts = 0;
@@ -444,12 +446,12 @@ function updateRepeat() {
444
446
  }
445
447
  return;
446
448
  }
447
- const i = s + t;
448
- const e = this.ds + this.ts;
449
+ const e = s + t;
450
+ const i = this.ds + this.ts;
449
451
  // Retained overlap [keepStart, keepEnd) in global indexes; empty when the
450
452
  // windows are disjoint or when coming from empty/fallback.
451
453
  const r = Math.max(s, this.ds);
452
- const n = Math.min(i, e);
454
+ const n = Math.min(e, i);
453
455
  const o = new Array(t);
454
456
  const h = new Array(t);
455
457
  for (let t = r; t < n; t++) {
@@ -457,16 +459,16 @@ function updateRepeat() {
457
459
  o[t - s] = this.ns[t - this.ds];
458
460
  }
459
461
  try {
460
- for (let t = s; t < i; t++) {
462
+ for (let t = s; t < e; t++) {
461
463
  if (t >= r && t < n) continue;
462
464
  o[t - s] = runWithOwner(h[t - s] = createOwner(), () => this.rs(t));
463
465
  }
464
466
  } catch (t) {
465
- for (let t = s; t < i; t++) if ((t < r || t >= n) && h[t - s]) h[t - s].dispose();
467
+ for (let t = s; t < e; t++) if ((t < r || t >= n) && h[t - s]) h[t - s].dispose();
466
468
  throw t;
467
469
  }
468
470
  // commit: dispose the previous fallback or the rows leaving the window
469
- if (this.ts === 0) this.hs[0]?.dispose(); else for (let t = this.ds; t < e; t++) if (t < s || t >= i) this.hs[t - this.ds].dispose();
471
+ if (this.ts === 0) this.hs[0]?.dispose(); else for (let t = this.ds; t < i; t++) if (t < s || t >= e) this.hs[t - this.ds].dispose();
470
472
  this.ns = o;
471
473
  this.hs = h;
472
474
  this.ds = s;
@@ -475,8 +477,8 @@ function updateRepeat() {
475
477
  return this.ns;
476
478
  }
477
479
 
478
- function compare(t, s, i) {
479
- return t ? t(s) === t(i) : true;
480
+ function compare(t, s, e) {
481
+ return t ? t(s) === t(e) : true;
480
482
  }
481
483
 
482
484
  export { mapArray, repeat };
@@ -4,7 +4,7 @@ import { computed, optimisticComputed, setSignal, optimisticSignal, runWithOwner
4
4
 
5
5
  import { cleanup, createRoot, getOwner, dispose, getObserver } from "./core/owner.js";
6
6
 
7
- import { globalQueue, Queue, entangleConfirmingTransitions, activeTransition } from "./core/scheduler.js";
7
+ import { globalQueue, dirtyQueue, Queue, entangleConfirmingTransitions, activeTransition } from "./core/scheduler.js";
8
8
 
9
9
  import { CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, EFFECT_USER, $REFRESH, CONFIG_DIRECT_COMMIT, CONFIG_AUTHORITATIVE_READ, CONFIG_FRESH_READ } from "./core/constants.js";
10
10
 
@@ -69,7 +69,7 @@ function accessor(e) {
69
69
  function createSignal(e, t) {
70
70
  if (typeof e === "function") {
71
71
  const r = computed(e, t);
72
- r.T &= ~CONFIG_AUTO_DISPOSE;
72
+ r.C &= ~CONFIG_AUTO_DISPOSE;
73
73
  return [ accessor(r), setMemo.bind(null, r) ];
74
74
  }
75
75
  const r = signal(e, t);
@@ -330,9 +330,9 @@ function createMemo(e, t) {
330
330
  // route through the inherited queue.
331
331
  const i = getOwner();
332
332
  const o = new MicrotaskQueue;
333
- o.Nt = i.C;
333
+ o._parent = i.T;
334
334
  // notify() forwards up the normal chain
335
- i.C = o;
335
+ i.T = o;
336
336
  // A user effect rather than a bare computed: computeds are pull-based and
337
337
  // are only re-enqueued when a pending source *resolves* — a rejection just
338
338
  // marks them errored, so nothing would re-run and the promise would never
@@ -352,7 +352,7 @@ function createMemo(e, t) {
352
352
  // stale mainline value and resolves with old data.
353
353
  {
354
354
  user: true,
355
- Kt: CONFIG_DIRECT_COMMIT
355
+ Xt: CONFIG_DIRECT_COMMIT
356
356
  });
357
357
  });
358
358
  });
@@ -445,8 +445,8 @@ function createMemo(e, t) {
445
445
  if (i === null) {
446
446
  i = getOwner();
447
447
  const e = new MicrotaskQueue;
448
- e.Nt = i.C;
449
- i.C = e;
448
+ e._parent = i.T;
449
+ i.T = e;
450
450
  }
451
451
  return read(t);
452
452
  }, t => {
@@ -457,7 +457,7 @@ function createMemo(e, t) {
457
457
  dispose(i);
458
458
  }, {
459
459
  user: true,
460
- Kt: CONFIG_DIRECT_COMMIT | CONFIG_AUTHORITATIVE_READ | CONFIG_FRESH_READ
460
+ Xt: CONFIG_DIRECT_COMMIT | CONFIG_AUTHORITATIVE_READ | CONFIG_FRESH_READ
461
461
  });
462
462
  make();
463
463
  });
@@ -508,12 +508,20 @@ function createMemo(e, t) {
508
508
  *
509
509
  * Must be called *outside* a tracking scope.
510
510
  *
511
+ * Inside an action, call it from a step: after an `await`, put a bare `yield`
512
+ * before `yield until(...)`. The runtime cannot hook an async generator's
513
+ * `await` continuation, so the `until(...)` expression — which CREATES the
514
+ * predicate's reader — would otherwise run outside the transaction; created
515
+ * there it is born held (A29) and replays only at the commit its own promise
516
+ * holds open (#3482). See {@link action}.
517
+ *
511
518
  * @example
512
519
  * ```ts
513
520
  * const send = action(async function* (text: string) {
514
521
  * const clientId = crypto.randomUUID();
515
522
  * setMessages(m => { m.push({ clientId, text, pending: true }); }); // optimistic
516
523
  * await socket.send({ clientId, text }); // fire-and-forget transport
524
+ * yield; // re-enter the transaction after the await
517
525
  * // Hold until the live source echoes the write (authoritative view —
518
526
  * // the optimistic row above cannot satisfy this):
519
527
  * yield until(() => messages.some(m => m.clientId === clientId), { timeout: 10_000 });
@@ -540,10 +548,10 @@ function createMemo(e, t) {
540
548
  // microtask so the promise can settle while the transaction the caller
541
549
  // yielded it into is still open — that transaction being open is the
542
550
  // entire point of the hold.
543
- const s = getOwner();
544
- const u = new MicrotaskQueue;
545
- u.Nt = s.C;
546
- s.C = u;
551
+ const u = getOwner();
552
+ const s = new MicrotaskQueue;
553
+ s._parent = u.T;
554
+ u.T = s;
547
555
  let f;
548
556
  let a;
549
557
  const settle = e => {
@@ -572,7 +580,7 @@ function createMemo(e, t) {
572
580
  // the hold itself is keeping uncommitted.
573
581
  {
574
582
  user: true,
575
- Kt: CONFIG_AUTHORITATIVE_READ | CONFIG_DIRECT_COMMIT
583
+ Xt: CONFIG_AUTHORITATIVE_READ | CONFIG_DIRECT_COMMIT
576
584
  });
577
585
  if (t?.timeout !== undefined) f = setTimeout(() => settle(() => i(new TimeoutError)), t.timeout);
578
586
  if (o !== undefined) {
@@ -592,7 +600,7 @@ function createOptimistic(e, t) {
592
600
  installOptimisticEngine();
593
601
  if (typeof e === "function") {
594
602
  const r = optimisticComputed(e, t);
595
- r.T &= ~CONFIG_AUTO_DISPOSE;
603
+ r.C &= ~CONFIG_AUTO_DISPOSE;
596
604
  return [ accessor(r), setSignal.bind(null, r) ];
597
605
  }
598
606
  const r = optimisticSignal(e, t);
@@ -695,14 +703,22 @@ function createOptimistic(e, t) {
695
703
  * on owner disposal
696
704
  */ function onSettled(e) {
697
705
  const t = getOwner();
698
- t && !(t.T & CONFIG_CHILDREN_FORBIDDEN) ? trackedEffect(() => untrack(e), {
706
+ t && !(t.C & CONFIG_CHILDREN_FORBIDDEN) ? trackedEffect(() => untrack(e), {
699
707
  name: "onSettled"
700
- }) : globalQueue.enqueue(EFFECT_USER, () => {
708
+ }) : globalQueue.enqueue(EFFECT_USER, function fire() {
709
+ // Settled means derived. A settle that reverts optimism (or replays
710
+ // gated reads) only enqueues the affected subscribers; the pass after
711
+ // the commit re-derives them. Fired in the commit pass, the callback
712
+ // read the optimistic source already reverted beside a sync memo of it
713
+ // still holding the optimistic value — reads do not pull (#3411). Fall
714
+ // to the next pass while the heap has work; `run` swapped the queue,
715
+ // so this lands there, and `enqueue` keeps the drain alive.
716
+ if (dirtyQueue.EE >= dirtyQueue.Ct) return globalQueue.enqueue(EFFECT_USER, fire);
701
717
  // Unowned, out-of-band fire (no owner, or a children-forbidden one this
702
718
  // one-shot must not bind to): a returned cleanup has no lifecycle to
703
719
  // attach to. Reject it in dev; in production the return is simply
704
720
  // dropped — never bound to an unrelated owner or run eagerly.
705
- e();
721
+ e();
706
722
  });
707
723
  }
708
724