@solidjs/signals 2.0.0-rc.17 → 2.0.0-rc.19

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 (71) hide show
  1. package/dist/dev-shared.js +426 -241
  2. package/dist/dev.attribution.js +19 -16
  3. package/dist/dev.js +768 -409
  4. package/dist/observe/boundaries.js +88 -67
  5. package/dist/observe/core/action.js +8 -0
  6. package/dist/observe/core/async.js +183 -138
  7. package/dist/observe/core/attribution.js +30 -28
  8. package/dist/observe/core/constants.js +23 -1
  9. package/dist/observe/core/core.js +185 -157
  10. package/dist/observe/core/effect.js +34 -37
  11. package/dist/observe/core/graph.js +8 -8
  12. package/dist/observe/core/heap.js +5 -5
  13. package/dist/observe/core/lanes.js +104 -84
  14. package/dist/observe/core/owner.js +17 -16
  15. package/dist/observe/core/scheduler.js +61 -47
  16. package/dist/observe/core/truth.js +6 -6
  17. package/dist/observe/core/verdict.js +15 -15
  18. package/dist/observe/deferred.js +40 -38
  19. package/dist/observe/from.js +248 -0
  20. package/dist/observe/index.js +4 -2
  21. package/dist/observe/map.js +22 -22
  22. package/dist/observe/reveal.js +5 -5
  23. package/dist/observe/signals.js +57 -15
  24. package/dist/observe/store/affects.js +1 -1
  25. package/dist/observe/store/hooks.js +3 -0
  26. package/dist/observe/store/optimistic.js +11 -11
  27. package/dist/observe/store/projection.js +34 -21
  28. package/dist/observe/store/reconcile.js +138 -122
  29. package/dist/observe/store/store.js +628 -641
  30. package/dist/observe/store/target.js +1 -5
  31. package/dist/observe/store/utils.js +7 -2
  32. package/dist/prod/boundaries.js +153 -132
  33. package/dist/prod/core/action.js +8 -0
  34. package/dist/prod/core/async.js +242 -197
  35. package/dist/prod/core/constants.js +16 -1
  36. package/dist/prod/core/core.js +166 -138
  37. package/dist/prod/core/effect.js +31 -34
  38. package/dist/prod/core/graph.js +5 -5
  39. package/dist/prod/core/heap.js +12 -12
  40. package/dist/prod/core/lanes.js +98 -78
  41. package/dist/prod/core/owner.js +15 -14
  42. package/dist/prod/core/scheduler.js +64 -50
  43. package/dist/prod/core/truth.js +6 -6
  44. package/dist/prod/core/verdict.js +17 -17
  45. package/dist/prod/deferred.js +40 -38
  46. package/dist/prod/from.js +248 -0
  47. package/dist/prod/index.js +4 -2
  48. package/dist/prod/map.js +14 -14
  49. package/dist/prod/reveal.js +8 -8
  50. package/dist/prod/signals.js +46 -4
  51. package/dist/prod/store/affects.js +1 -1
  52. package/dist/prod/store/hooks.js +3 -0
  53. package/dist/prod/store/optimistic.js +16 -16
  54. package/dist/prod/store/projection.js +22 -9
  55. package/dist/prod/store/reconcile.js +138 -122
  56. package/dist/prod/store/store.js +308 -331
  57. package/dist/prod/store/target.js +13 -17
  58. package/dist/prod/store/utils.js +2 -0
  59. package/dist/types/core/action.d.ts +8 -0
  60. package/dist/types/core/async.d.ts +12 -2
  61. package/dist/types/core/constants.d.ts +25 -0
  62. package/dist/types/core/core.d.ts +33 -4
  63. package/dist/types/core/dev.d.ts +1 -1
  64. package/dist/types/core/index.d.ts +1 -1
  65. package/dist/types/core/lanes.d.ts +5 -1
  66. package/dist/types/core/scheduler.d.ts +17 -3
  67. package/dist/types/from.d.ts +47 -0
  68. package/dist/types/index.d.ts +4 -1
  69. package/dist/types/signals.d.ts +37 -0
  70. package/dist/types/store/target.d.ts +2 -0
  71. package/package.json +1 -1
@@ -0,0 +1,248 @@
1
+ import { isThenable, onSupersede } from "./core/async.js";
2
+
3
+ import { CONFIG_CHILDREN_FORBIDDEN } from "./core/constants.js";
4
+
5
+ import { cleanup, getOwner } from "./core/owner.js";
6
+
7
+ // `from` — a push source (callback subscription, Observable, AsyncIterable)
8
+ // as an AsyncIterable a computation can return or `for await` over:
9
+ // const value = createMemo(() => from(source));
10
+ // A subscription keeps the latest value: values that arrive between pulls
11
+ // coalesce. An AsyncIterable streams through unchanged. Either way the source
12
+ // opens when the result is iterated and closes on return(), on supersede (the
13
+ // owning computation re-runs) and on owner disposal, whichever comes first.
14
+ function toSubscribe(e) {
15
+ if (typeof e === "function") return e;
16
+ // TC39 / RxJS interop: `Symbol.observable` (or "@@observable") returns the observable.
17
+ // Read per call, not at module scope, so the module stays side-effect free.
18
+ const n = Symbol.observable;
19
+ const r = (n && e?.[n]) ?? e?.["@@observable"];
20
+ const t = typeof r === "function" ? r.call(e) : e;
21
+ if (t && typeof t.subscribe === "function") return (e, n, r) => t.subscribe({
22
+ next: e,
23
+ error: n,
24
+ complete: r
25
+ });
26
+ return null;
27
+ }
28
+
29
+ const DONE = {
30
+ value: undefined,
31
+ done: true
32
+ };
33
+
34
+ /**
35
+ * Ties an iterator's `finish` to the computation iterating it. Solid reaches
36
+ * an iterator's return() only when the compute returns the iterable directly.
37
+ * Inside an async generator the generator's own return() is queued behind its
38
+ * pending `await`, and owner cleanups wait for the replacing pass to commit,
39
+ * so close on supersede, with owner disposal as the backstop. No owner (an
40
+ * async compute iterating it after its first `await`, or plain iteration):
41
+ * no registration and no diagnostic, so only return() closes it. Scopes that
42
+ * forbid cleanups rely on supersede and return().
43
+ */ function bindToOwner(e) {
44
+ const n = getOwner();
45
+ if (!n) return;
46
+ onSupersede(e);
47
+ if (!(n.l & CONFIG_CHILDREN_FORBIDDEN)) cleanup(e);
48
+ }
49
+
50
+ /**
51
+ * An AsyncIterable as a stream bound to the iterating computation: every
52
+ * value passes through (no coalescing). `finish` calls the inner return()
53
+ * once and settles pending pulls as done, so a `for await` over it exits as
54
+ * soon as the computation re-runs — even when the inner iterator is an async
55
+ * generator whose own return() the language queues behind its pending
56
+ * `await`.
57
+ */ function wrapIterable(e) {
58
+ return {
59
+ [Symbol.asyncIterator]() {
60
+ const n = e[Symbol.asyncIterator]();
61
+ let r = false;
62
+ const t = new Set;
63
+ const finish = () => {
64
+ if (r) return DONE;
65
+ r = true;
66
+ for (const e of t) e(DONE);
67
+ t.clear();
68
+ try {
69
+ const e = n.return?.();
70
+ if (isThenable(e)) e.then(undefined, () => {});
71
+ } catch {}
72
+ return DONE;
73
+ };
74
+ bindToOwner(finish);
75
+ return {
76
+ next(...e) {
77
+ if (r) return Promise.resolve(DONE);
78
+ const o = n.next(...e);
79
+ // A bare result (the producer's buffered fast path) passes through.
80
+ if (!isThenable(o)) return o;
81
+ return new Promise((e, n) => {
82
+ t.add(e);
83
+ o.then(n => t.delete(e) && e(n), r => t.delete(e) && n(r));
84
+ });
85
+ },
86
+ return() {
87
+ return Promise.resolve(finish());
88
+ }
89
+ };
90
+ }
91
+ };
92
+ }
93
+
94
+ const toCleanup = e => typeof e === "function" ? e : e && typeof e.unsubscribe === "function" ? () => e.unsubscribe() : () => {};
95
+
96
+ /**
97
+ * Converts a callback subscription, an Observable or an AsyncIterable into an
98
+ * AsyncIterable for a Solid computation. A subscription keeps only the latest
99
+ * value, and a value the source emits during `subscribe` is served without a
100
+ * loading frame; an AsyncIterable streams every value through.
101
+ *
102
+ * The source closes on supersede — the owning computation re-running — and
103
+ * on owner disposal. Inside an async generator memo that holds for a
104
+ * `for await` that starts before the first `await`; one that starts after it
105
+ * has no owner to bind to, so its source closes only when the loop ends.
106
+ * Prefer returning `from(...)` from its own memo, with awaited inputs read
107
+ * from upstream memos and transforms done in a downstream memo.
108
+ *
109
+ * Iterate the result with `for await` or return it from a computation: its
110
+ * `next()` returns an already-buffered value as a bare result, not a promise.
111
+ *
112
+ * @example
113
+ * ```ts
114
+ * const position = createMemo(() =>
115
+ * from<Position>((next, error) => {
116
+ * const id = navigator.geolocation.watchPosition(next, error);
117
+ * return () => navigator.geolocation.clearWatch(id);
118
+ * })
119
+ * );
120
+ * const ticks = createMemo(() => from(interval$(rate())));
121
+ * ```
122
+ */ function from(e) {
123
+ const n = toSubscribe(e);
124
+ if (!n) {
125
+ if (e && typeof e[Symbol.asyncIterator] === "function") return wrapIterable(e);
126
+ throw new TypeError("from(): expected a subscribe function, an Observable or an AsyncIterable");
127
+ }
128
+ return {
129
+ [Symbol.asyncIterator]() {
130
+ let e;
131
+ let r = false;
132
+ let t = false;
133
+ let o = false;
134
+ let u;
135
+ let f = null;
136
+ let l = null;
137
+ let s = false;
138
+ const close = () => {
139
+ if (s) return;
140
+ s = true;
141
+ t = true;
142
+ const e = l;
143
+ l = null;
144
+ // A throwing unsubscribe must not escape: at supersede this runs
145
+ // inside flush(), ahead of the replacing pass (the returned-iterator
146
+ // path swallows it the same way).
147
+ try {
148
+ e?.();
149
+ } catch {}
150
+ };
151
+ const settle = () => {
152
+ if (!f) return;
153
+ const n = f;
154
+ if (r) {
155
+ f = null;
156
+ r = false;
157
+ n.resolve({
158
+ value: e,
159
+ done: false
160
+ });
161
+ } else if (o) {
162
+ f = null;
163
+ o = false;
164
+ n.reject(u);
165
+ } else if (t) {
166
+ f = null;
167
+ n.resolve({
168
+ value: undefined,
169
+ done: true
170
+ });
171
+ }
172
+ };
173
+ const i = n(n => {
174
+ if (t) return;
175
+ e = n;
176
+ r = true;
177
+ settle();
178
+ }, e => {
179
+ if (t) return;
180
+ o = true;
181
+ u = e;
182
+ t = true;
183
+ settle();
184
+ close();
185
+ }, () => {
186
+ if (t) return;
187
+ t = true;
188
+ settle();
189
+ close();
190
+ });
191
+ l = toCleanup(i);
192
+ // Completed or errored during subscribe: release what it just returned.
193
+ if (s) {
194
+ const e = l;
195
+ l = null;
196
+ try {
197
+ e();
198
+ } catch {}
199
+ }
200
+ // Idempotent: return(), supersede and disposal may all reach it.
201
+ const finish = () => {
202
+ close();
203
+ r = false;
204
+ if (f) {
205
+ const e = f;
206
+ f = null;
207
+ e.resolve(DONE);
208
+ }
209
+ return DONE;
210
+ };
211
+ bindToOwner(finish);
212
+ return {
213
+ next() {
214
+ // A buffered value is returned as a bare result, not a promise: the
215
+ // iterator consumer lands it in the same pass, so a source that
216
+ // emits during subscribe (BehaviorSubject-style) shows its value
217
+ // with no loading frame. `for await` accepts either form.
218
+ if (r) {
219
+ r = false;
220
+ return {
221
+ value: e,
222
+ done: false
223
+ };
224
+ }
225
+ if (o) {
226
+ o = false;
227
+ return Promise.reject(u);
228
+ }
229
+ if (t) return Promise.resolve({
230
+ value: undefined,
231
+ done: true
232
+ });
233
+ return new Promise((e, n) => {
234
+ f = {
235
+ resolve: e,
236
+ reject: n
237
+ };
238
+ });
239
+ },
240
+ return() {
241
+ return Promise.resolve(finish());
242
+ }
243
+ };
244
+ }
245
+ };
246
+ }
247
+
248
+ export { from };
@@ -18,7 +18,7 @@ export { $REFRESH, SUPPORTS_PROXY } from "./core/constants.js";
18
18
 
19
19
  export { setConsoleFooter } from "./core/dev.js";
20
20
 
21
- export { ROOT_ERROR_HOOK, enforceLoadingBoundary, flush, resetErrorHalt } from "./core/scheduler.js";
21
+ export { ROOT_ERROR_HOOK, clock, enforceLoadingBoundary, flush, resetErrorHalt } from "./core/scheduler.js";
22
22
 
23
23
  import "./core/effect.js";
24
24
 
@@ -26,7 +26,7 @@ export { configureClientErrors } from "./core/error-hooks.js";
26
26
 
27
27
  export { storeName } from "./store/edges.js";
28
28
 
29
- export { createEffect, createMemo, createOptimistic, createReaction, createRenderEffect, createSignal, createTrackedEffect, onCleanup, onSettled, refresh, resolve, until } from "./signals.js";
29
+ export { createEffect, createMemo, createOptimistic, createReaction, createRenderEffect, createSignal, createTrackedEffect, onCleanup, onFlush, onSettled, refresh, resolve, until } from "./signals.js";
30
30
 
31
31
  export { mapArray, repeat } from "./map.js";
32
32
 
@@ -36,6 +36,8 @@ export { affects } from "./affects.js";
36
36
 
37
37
  export { createDeferred } from "./deferred.js";
38
38
 
39
+ export { from } from "./from.js";
40
+
39
41
  export { flatten } from "./flatten.js";
40
42
 
41
43
  export { createErrorBoundary, createLoadingBoundary } from "./boundaries.js";
package/dist/prod/map.js CHANGED
@@ -21,7 +21,7 @@ function mapArray(t, e, i) {
21
21
  const n = e.length > 1;
22
22
  const r = e;
23
23
  const o = {
24
- L: createOwner(),
24
+ H: createOwner(),
25
25
  it: 0,
26
26
  st: t,
27
27
  ot: [],
@@ -32,7 +32,7 @@ function mapArray(t, e, i) {
32
32
  ct: s || i?.keyed === false ? [] : undefined,
33
33
  lt: n && i?.keyed !== false ? [] : undefined,
34
34
  ut: i?.keyed === false,
35
- H: i?.fallback
35
+ L: i?.fallback
36
36
  };
37
37
  const f = computed(updateKeyedMap.bind(o), undefined);
38
38
  f.l &= ~CONFIG_AUTO_DISPOSE;
@@ -263,11 +263,11 @@ function updateKeyedMap() {
263
263
  // the flight's reader (pending tracking + post-settle retry) instead of
264
264
  // vanishing. Set per pass: the first pass runs inside `computed()`, before
265
265
  // the node exists to the caller.
266
- this.L.Ye = getOwner();
266
+ this.H.Ye = getOwner();
267
267
  const t = this.st() || [], e = t.length;
268
268
  t[$TRACK];
269
269
  // top level tracking (a store array's container node)
270
- runWithOwner(this.L, () => {
270
+ runWithOwner(this.H, () => {
271
271
  let i, s,
272
272
  // The stage slot a mapper writes its row/index signal into: `j` on the
273
273
  // create-all path, `j - start` on the general path, whose stage arrays
@@ -293,7 +293,7 @@ function updateKeyedMap() {
293
293
  // fast path for empty arrays
294
294
  if (e === 0) {
295
295
  if (this.it !== 0) {
296
- this.L.dispose(false);
296
+ this.H.dispose(false);
297
297
  this.tn = [];
298
298
  this.ot = [];
299
299
  this.ht = [];
@@ -301,11 +301,11 @@ function updateKeyedMap() {
301
301
  this.ct && (this.ct = []);
302
302
  this.lt && (this.lt = []);
303
303
  }
304
- if (this.H && !this.ht.length) {
304
+ if (this.L && !this.ht.length) {
305
305
  // an aborted fallback attempt leaves an owner without a mapping;
306
306
  // dispose it before re-creating
307
307
  this.tn[0]?.dispose();
308
- this.ht[0] = runWithOwner(this.tn[0] = createOwner(), this.H);
308
+ this.ht[0] = runWithOwner(this.tn[0] = createOwner(), this.L);
309
309
  }
310
310
  }
311
311
  // fast path for new create
@@ -502,7 +502,7 @@ function updateKeyedMap() {
502
502
  */ function repeat(t, e, i) {
503
503
  const s = e;
504
504
  const n = {
505
- L: createOwner(),
505
+ H: createOwner(),
506
506
  it: 0,
507
507
  dt: 0,
508
508
  wt: t,
@@ -510,7 +510,7 @@ function updateKeyedMap() {
510
510
  tn: [],
511
511
  ht: [],
512
512
  Ot: i?.from,
513
- H: i?.fallback
513
+ L: i?.fallback
514
514
  };
515
515
  const r = computed(updateRepeat.bind(n));
516
516
  r.l &= ~CONFIG_AUTO_DISPOSE;
@@ -526,24 +526,24 @@ function updateKeyedMap() {
526
526
  // disjoint-window/front-clear/end-clear/shift special cases.
527
527
  function updateRepeat() {
528
528
  // As in updateKeyedMap: the node is the row callbacks' reader, per pass.
529
- this.L.Ye = getOwner();
529
+ this.H.Ye = getOwner();
530
530
  const t = this.wt();
531
531
  const e = this.Ot?.() || 0;
532
- runWithOwner(this.L, () => {
532
+ runWithOwner(this.H, () => {
533
533
  if (t === 0) {
534
534
  if (this.it !== 0) {
535
- this.L.dispose(false);
535
+ this.H.dispose(false);
536
536
  this.tn = [];
537
537
  this.ht = [];
538
538
  this.it = 0;
539
539
  // Reset offset to match the cleared data (#2767, repro 2).
540
540
  this.dt = 0;
541
541
  }
542
- if (this.H && !this.ht.length) {
542
+ if (this.L && !this.ht.length) {
543
543
  // an aborted fallback attempt leaves an owner without a mapping;
544
544
  // dispose it before re-creating
545
545
  this.tn[0]?.dispose();
546
- this.ht[0] = runWithOwner(this.tn[0] = createOwner(), this.H);
546
+ this.ht[0] = runWithOwner(this.tn[0] = createOwner(), this.L);
547
547
  }
548
548
  return;
549
549
  }
@@ -76,7 +76,7 @@ class RevealController {
76
76
  /** A slot that has left this controller's ownership: a boundary that has
77
77
  * shown content, a nested group that is ready. Shown is shown — it counts
78
78
  * as ready whatever happens to it later (a re-arm gates nobody else). */ gt(t) {
79
- return (isController(t) ? t._parent : t.V) !== this;
79
+ return (isController(t) ? t._parent : t.v) !== this;
80
80
  }
81
81
  /** The slots this controller still owns, in registration order — the
82
82
  * ones the order's state is forced on. */ Lt(t) {
@@ -106,7 +106,7 @@ class RevealController {
106
106
  }
107
107
  return t !== "natural";
108
108
  }
109
- oe(t) {
109
+ te(t) {
110
110
  if (this.At.includes(t)) return;
111
111
  this.At.push(t);
112
112
  // Gated from the start (collapsed in a collapsed sequential tail); the
@@ -118,13 +118,13 @@ class RevealController {
118
118
  t.ee = e;
119
119
  t._t();
120
120
  } else {
121
- t.V = this;
121
+ t.v = this;
122
122
  t.$ = true;
123
123
  t.ee = e;
124
124
  }
125
125
  untrack(() => this._t());
126
126
  }
127
- te(t) {
127
+ oe(t) {
128
128
  const e = this.At.indexOf(t);
129
129
  if (e >= 0) this.At.splice(e, 1);
130
130
  untrack(() => this._t());
@@ -194,8 +194,8 @@ class RevealController {
194
194
  });
195
195
  if (false) ;
196
196
  if (r) {
197
- r.oe(i);
198
- cleanup(() => r.te(i));
197
+ r.te(i);
198
+ cleanup(() => r.oe(i));
199
199
  }
200
200
  return e;
201
201
  });
@@ -204,9 +204,9 @@ class RevealController {
204
204
  // Installed at module evaluation — present exactly when something imports
205
205
  // `createRevealOrder`; a Loading boundary without one pays a null check.
206
206
  setRevealHooks({
207
- oe: (t, e) => t.oe(e),
208
207
  te: (t, e) => t.te(e),
209
- P: t => t._t()
208
+ oe: (t, e) => t.oe(e),
209
+ V: t => t._t()
210
210
  });
211
211
 
212
212
  export { createRevealOrder };
@@ -6,7 +6,7 @@ import { CONFIG_CHILDREN_FORBIDDEN, EFFECT_USER, $REFRESH, CONFIG_AUTO_DISPOSE,
6
6
 
7
7
  import { computed, signal, runWithOwner, setMemo, setSignal, markRefresh, read, untrack } from "./core/core.js";
8
8
 
9
- import { globalQueue, flushTransaction, dirtyQueue, GlobalQueue, resolveTx, passTx, staleReader, joinFuture } from "./core/scheduler.js";
9
+ import { schedule, globalQueue, flushTransaction, dirtyQueue, GlobalQueue, resolveTx, passTx, staleReader, joinFuture } from "./core/scheduler.js";
10
10
 
11
11
  import { cleanup, createRoot, getOwner, dispose } from "./core/owner.js";
12
12
 
@@ -463,7 +463,7 @@ function createMemo(e, t) {
463
463
  // guessed node's own re-ask is not answered by it (#3895): park
464
464
  // on that flight, or reject with its error.
465
465
  const e = read(t);
466
- if (t.l & CONFIG_GUESS && t.m & (STATUS_PENDING | STATUS_ERROR)) throw t.T?.F;
466
+ if (t.l & CONFIG_GUESS && t.m & (STATUS_PENDING | STATUS_ERROR)) throw t.T?.U;
467
467
  return e;
468
468
  }, t => {
469
469
  n(typeof e === "function" ? t : e);
@@ -657,7 +657,7 @@ function createMemo(e, t) {
657
657
  // still holding the optimistic value — reads do not pull (#3411). Fall
658
658
  // to the next pass while the heap has work; `run` swapped the queue,
659
659
  // so this lands there, and `enqueue` keeps the drain alive.
660
- if (dirtyQueue.et >= dirtyQueue.Ge) return globalQueue.enqueue(EFFECT_USER, fire);
660
+ if (dirtyQueue.et >= dirtyQueue.Fe) return globalQueue.enqueue(EFFECT_USER, fire);
661
661
  // Unowned, out-of-band fire (no owner, or a children-forbidden one this
662
662
  // one-shot must not bind to): a returned cleanup has no lifecycle to
663
663
  // attach to. Reject it in dev; in production the return is simply
@@ -666,4 +666,46 @@ function createMemo(e, t) {
666
666
  });
667
667
  }
668
668
 
669
- export { accessor, createEffect, createMemo, createOptimistic, createReaction, createRenderEffect, createSignal, createTrackedEffect, onCleanup, onSettled, refresh, resolve, until };
669
+ /**
670
+ * Schedules `callback` to run **once**, right after the pending flush
671
+ * finishes, in the same microtask checkpoint (before the browser paints).
672
+ * Unlike `onSettled`, it never waits on async work: if that flush is held by
673
+ * a pending transition, the callback still runs.
674
+ *
675
+ * Its main use is keeping a write out of another update's hold. Writes made
676
+ * in the same synchronous frame are applied together, so when one of them
677
+ * starts async work (a route change, a fetch), the others are held with it
678
+ * until it resolves. A write made in `onFlush` happens after that flush, so
679
+ * it is its own update: it commits on its own unless something it changes
680
+ * also derives from the held work (a memo or `createEffect` reading both),
681
+ * in which case it joins that hold as any later write would.
682
+ *
683
+ * Call it from event handlers and effect callbacks (including
684
+ * `createTrackedEffect` and `onSettled` bodies). In an owned scope such as a
685
+ * component body or a computation it warns in dev: use `onSettled` there.
686
+ *
687
+ * - A write in the callback lands after any write made inline in the same
688
+ * frame. Don't write the same signal both inside and outside it; use a
689
+ * functional setter if the order matters.
690
+ * - Several `onFlush` callbacks run in call order and batch into one flush.
691
+ * - `queueMicrotask` is not a substitute: called before the frame's first
692
+ * write, it runs before that flush and its write joins it.
693
+ *
694
+ * @example
695
+ * ```tsx
696
+ * // A menu closes now even when the consumer's onSelect starts async work.
697
+ * const select = (id: string) => {
698
+ * onFlush(() => setOpen(false));
699
+ * props.onSelect?.(id);
700
+ * };
701
+ * ```
702
+ *
703
+ * @param callback Function to run once after the pending flush
704
+ */ function onFlush(e) {
705
+ // The frame's flush is queued (or already draining) ahead of this
706
+ // microtask, so the callback's writes start a flush of their own.
707
+ schedule();
708
+ queueMicrotask(e);
709
+ }
710
+
711
+ export { accessor, createEffect, createMemo, createOptimistic, createReaction, createRenderEffect, createSignal, createTrackedEffect, onCleanup, onFlush, onSettled, refresh, resolve, until };
@@ -72,7 +72,7 @@ function scopeOf(e, o) {
72
72
  if (f !== undefined) {
73
73
  if (f.pb !== null) o.eo.add(f.v);
74
74
  // The writer's view: the tick's own unflushed guesses are in motion too.
75
- if (f.fam?.opt === true) r = optHooks.view(f, r, true);
75
+ if (optHooks && f.fam?.opt === true) r = optHooks.view(f, r, true);
76
76
  const e = f.n;
77
77
  if (e !== null) for (const o of Reflect.ownKeys(e))
78
78
  // Another mark's carrier is its own channel: counting it here would
@@ -1,3 +1,6 @@
1
+ // Set before any family is optimistic (`fam.opt`): a call site checks the
2
+ // hooks first, so a build that does not import optimistic.ts folds the arm
3
+ // away.
1
4
  let optHooks = null;
2
5
 
3
6
  function installOptHooks(o) {
@@ -83,7 +83,7 @@ function notifyOptimisticWrites(e, n) {
83
83
  // inner store's truth for its key before guessing over it.
84
84
  const o = e.ch ? e.v : null;
85
85
  const guess = (e, n, i, r) => {
86
- if (o !== null) e.Ie = i ? n in t : unwrapValue(t[n]);
86
+ if (o !== null) e.le = i ? n in t : unwrapValue(t[n]);
87
87
  // By value: a function leaf is a value, not an updater (#3017).
88
88
  optimisticWrite(e, () => r);
89
89
  };
@@ -274,7 +274,7 @@ function arrangementEqual(e, n, t) {
274
274
  if (!t) return fold(e, n);
275
275
  const i = e.O;
276
276
  if (i === undefined || e.ze !== $TRACK) return;
277
- const r = e.T.v;
277
+ const r = e.T.P;
278
278
  if (!(r instanceof LaneView)) return;
279
279
  if (!Array.isArray(n) || !Array.isArray(r.rows)) {
280
280
  r.base = n;
@@ -318,7 +318,7 @@ function arrangementEqual(e, n, t) {
318
318
  // key's new object is the same row, its target adopted it).
319
319
  if (n === undefined) {
320
320
  if (!sameRow(d[e], f[e], o)) laneSet(getNode(i, e), f[e], c);
321
- } else if (!sameRow(n.l & CONFIG_OVERRIDE ? n.T.v : d[e], f[e], o)) laneSet(n, f[e], c);
321
+ } else if (!sameRow(n.l & CONFIG_OVERRIDE ? n.T.P : d[e], f[e], o)) laneSet(n, f[e], c);
322
322
  }
323
323
  if (p !== null) {
324
324
  for (let e = f.length; ;e++) {
@@ -342,8 +342,8 @@ function arrangementEqual(e, n, t) {
342
342
  const i = txOf(e);
343
343
  // A guess the screen never showed is void at the correction (D-34); a
344
344
  // keyed arrangement guess's view carries its removed-keys set.
345
- if (!i.ke || !(t?.l & CONFIG_OVERRIDE) || !t.T.v?.removed) return false;
346
- const r = e.T.v;
345
+ if (!i.ge || !(t?.l & CONFIG_OVERRIDE) || !t.T.P?.removed) return false;
346
+ const r = e.T.P;
347
347
  if ((!e.xe || !e.xe(r, n)) && !GlobalQueue.nn(r, n)) {
348
348
  i.folded = foldedRead;
349
349
  insertSubs(e);
@@ -365,8 +365,8 @@ function arrangementEqual(e, n, t) {
365
365
  const t = txOf(n);
366
366
  joinPassAt(e, resolveTx(t._parent ?? t));
367
367
  }
368
- if (n.se !== NOT_PENDING) return n.se;
369
- return n.l & CONFIG_SLOT_NODE ? GlobalQueue.fn(n) : n.Ie;
368
+ if (n.ie !== NOT_PENDING) return n.ie;
369
+ return n.l & CONFIG_SLOT_NODE ? GlobalQueue.fn(n) : n.le;
370
370
  }
371
371
 
372
372
  function sameRow(e, n, t) {
@@ -383,8 +383,8 @@ function sameRow(e, n, t) {
383
383
  if (!(e.l & CONFIG_OVERRIDE)) {
384
384
  e.l |= CONFIG_OVERRIDE;
385
385
  list(e, t);
386
- } else if (i.v === n) return;
387
- i.v = n;
386
+ } else if (i.P === n) return;
387
+ i.P = n;
388
388
  if (e.o !== null) {
389
389
  insertSubs(e);
390
390
  laneDirty(e, t);
@@ -430,7 +430,7 @@ function sweepOverlaid() {
430
430
  // The container shows its backing again (a shown arrangement guess
431
431
  // that ended without a landing left its `LaneView` as the value).
432
432
  const t = e.k;
433
- if (t !== null && t.Ie instanceof LaneView) t.Ie = t.Ie.base;
433
+ if (t !== null && t.le instanceof LaneView) t.le = t.le.base;
434
434
  }
435
435
  if (n.size === 0) overlaidFamilies.delete(e);
436
436
  }
@@ -447,8 +447,8 @@ function sweepOverlaid() {
447
447
  if (i !== null) for (const e of Reflect.ownKeys(i)) {
448
448
  const r = i[e];
449
449
  const o = untrack(() => unwrapValue(n[e]));
450
- if (r.xe && r.xe(r.Ie, o)) continue;
451
- r.Ie = o;
450
+ if (r.xe && r.xe(r.le, o)) continue;
451
+ r.le = o;
452
452
  // (The lane's end notified it this epoch when the shown value and the
453
453
  // covered one differed — its readers re-derive already.)
454
454
  if (r.o !== null && r.Me !== notifyEpoch) {
@@ -460,8 +460,8 @@ function sweepOverlaid() {
460
460
  if (r !== null) for (const e of Reflect.ownKeys(r)) {
461
461
  const i = r[e];
462
462
  const o = untrack(() => e in n);
463
- if (i.Ie === o) continue;
464
- i.Ie = o;
463
+ if (i.le === o) continue;
464
+ i.le = o;
465
465
  if (i.o !== null && i.Me !== notifyEpoch) {
466
466
  insertSubs(i);
467
467
  t = true;
@@ -470,9 +470,9 @@ function sweepOverlaid() {
470
470
  // The arrangement the readers saw (the shown guess) against the inner's
471
471
  // now: a structural reader re-derives when they differ.
472
472
  const o = e.k;
473
- if (o !== null && o.o !== null && o.Ie instanceof LaneView && o.Me !== notifyEpoch) {
473
+ if (o !== null && o.o !== null && o.le instanceof LaneView && o.Me !== notifyEpoch) {
474
474
  const i = Array.isArray(n) ? untrack(() => n.map(unwrapValue)) : n;
475
- if (!arrangementEqual(o.Ie.rows, i, e.fam?.key ?? null)) {
475
+ if (!arrangementEqual(o.le.rows, i, e.fam?.key ?? null)) {
476
476
  insertSubs(o);
477
477
  t = true;
478
478
  }