@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.
- package/dist/dev-shared.js +426 -241
- package/dist/dev.attribution.js +19 -16
- package/dist/dev.js +768 -409
- package/dist/observe/boundaries.js +88 -67
- package/dist/observe/core/action.js +8 -0
- package/dist/observe/core/async.js +183 -138
- package/dist/observe/core/attribution.js +30 -28
- package/dist/observe/core/constants.js +23 -1
- package/dist/observe/core/core.js +185 -157
- package/dist/observe/core/effect.js +34 -37
- package/dist/observe/core/graph.js +8 -8
- package/dist/observe/core/heap.js +5 -5
- package/dist/observe/core/lanes.js +104 -84
- package/dist/observe/core/owner.js +17 -16
- package/dist/observe/core/scheduler.js +61 -47
- package/dist/observe/core/truth.js +6 -6
- package/dist/observe/core/verdict.js +15 -15
- package/dist/observe/deferred.js +40 -38
- package/dist/observe/from.js +248 -0
- package/dist/observe/index.js +4 -2
- package/dist/observe/map.js +22 -22
- package/dist/observe/reveal.js +5 -5
- package/dist/observe/signals.js +57 -15
- package/dist/observe/store/affects.js +1 -1
- package/dist/observe/store/hooks.js +3 -0
- package/dist/observe/store/optimistic.js +11 -11
- package/dist/observe/store/projection.js +34 -21
- package/dist/observe/store/reconcile.js +138 -122
- package/dist/observe/store/store.js +628 -641
- package/dist/observe/store/target.js +1 -5
- package/dist/observe/store/utils.js +7 -2
- package/dist/prod/boundaries.js +153 -132
- package/dist/prod/core/action.js +8 -0
- package/dist/prod/core/async.js +242 -197
- package/dist/prod/core/constants.js +16 -1
- package/dist/prod/core/core.js +166 -138
- package/dist/prod/core/effect.js +31 -34
- package/dist/prod/core/graph.js +5 -5
- package/dist/prod/core/heap.js +12 -12
- package/dist/prod/core/lanes.js +98 -78
- package/dist/prod/core/owner.js +15 -14
- package/dist/prod/core/scheduler.js +64 -50
- package/dist/prod/core/truth.js +6 -6
- package/dist/prod/core/verdict.js +17 -17
- package/dist/prod/deferred.js +40 -38
- package/dist/prod/from.js +248 -0
- package/dist/prod/index.js +4 -2
- package/dist/prod/map.js +14 -14
- package/dist/prod/reveal.js +8 -8
- package/dist/prod/signals.js +46 -4
- package/dist/prod/store/affects.js +1 -1
- package/dist/prod/store/hooks.js +3 -0
- package/dist/prod/store/optimistic.js +16 -16
- package/dist/prod/store/projection.js +22 -9
- package/dist/prod/store/reconcile.js +138 -122
- package/dist/prod/store/store.js +308 -331
- package/dist/prod/store/target.js +13 -17
- package/dist/prod/store/utils.js +2 -0
- package/dist/types/core/action.d.ts +8 -0
- package/dist/types/core/async.d.ts +12 -2
- package/dist/types/core/constants.d.ts +25 -0
- package/dist/types/core/core.d.ts +33 -4
- package/dist/types/core/dev.d.ts +1 -1
- package/dist/types/core/index.d.ts +1 -1
- package/dist/types/core/lanes.d.ts +5 -1
- package/dist/types/core/scheduler.d.ts +17 -3
- package/dist/types/from.d.ts +47 -0
- package/dist/types/index.d.ts +4 -1
- package/dist/types/signals.d.ts +37 -0
- package/dist/types/store/target.d.ts +2 -0
- 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 };
|
package/dist/observe/index.js
CHANGED
|
@@ -20,7 +20,7 @@ import { OBSERVE as OBSERVE$1 } from "./core/dev.js";
|
|
|
20
20
|
|
|
21
21
|
export { setConsoleFooter } from "./core/dev.js";
|
|
22
22
|
|
|
23
|
-
export { ROOT_ERROR_HOOK, enforceLoadingBoundary, flush, resetErrorHalt } from "./core/scheduler.js";
|
|
23
|
+
export { ROOT_ERROR_HOOK, clock, enforceLoadingBoundary, flush, resetErrorHalt } from "./core/scheduler.js";
|
|
24
24
|
|
|
25
25
|
import "./core/effect.js";
|
|
26
26
|
|
|
@@ -28,7 +28,7 @@ export { configureClientErrors } from "./core/error-hooks.js";
|
|
|
28
28
|
|
|
29
29
|
export { storeName } from "./store/edges.js";
|
|
30
30
|
|
|
31
|
-
export { createEffect, createMemo, createOptimistic, createReaction, createRenderEffect, createSignal, createTrackedEffect, onCleanup, onSettled, refresh, resolve, until } from "./signals.js";
|
|
31
|
+
export { createEffect, createMemo, createOptimistic, createReaction, createRenderEffect, createSignal, createTrackedEffect, onCleanup, onFlush, onSettled, refresh, resolve, until } from "./signals.js";
|
|
32
32
|
|
|
33
33
|
export { mapArray, repeat } from "./map.js";
|
|
34
34
|
|
|
@@ -38,6 +38,8 @@ export { affects } from "./affects.js";
|
|
|
38
38
|
|
|
39
39
|
export { createDeferred } from "./deferred.js";
|
|
40
40
|
|
|
41
|
+
export { from } from "./from.js";
|
|
42
|
+
|
|
41
43
|
export { flatten } from "./flatten.js";
|
|
42
44
|
|
|
43
45
|
export { createErrorBoundary, createLoadingBoundary } from "./boundaries.js";
|
package/dist/observe/map.js
CHANGED
|
@@ -29,7 +29,7 @@ function mapArray(t, e, i) {
|
|
|
29
29
|
Mt: [],
|
|
30
30
|
Wt: r,
|
|
31
31
|
vt: [],
|
|
32
|
-
|
|
32
|
+
Ye: [],
|
|
33
33
|
Vt: s,
|
|
34
34
|
Kt: s || i?.keyed === false ? [] : undefined,
|
|
35
35
|
xt: n && i?.keyed !== false ? [] : undefined,
|
|
@@ -157,7 +157,7 @@ function trySmallMove(t, e, i, s) {
|
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
159
|
const a = t.vt;
|
|
160
|
-
const l = t.
|
|
160
|
+
const l = t.Ye;
|
|
161
161
|
const u = a.slice(0, i);
|
|
162
162
|
const d = l.slice(0, i);
|
|
163
163
|
for (let t = 0; t < r.length; t += 3) {
|
|
@@ -177,7 +177,7 @@ function trySmallMove(t, e, i, s) {
|
|
|
177
177
|
d[t] = l[e];
|
|
178
178
|
}
|
|
179
179
|
t.vt = u;
|
|
180
|
-
t.
|
|
180
|
+
t.Ye = d;
|
|
181
181
|
t.wt = i;
|
|
182
182
|
t.Mt = e.slice(0);
|
|
183
183
|
// Dispose unmatched sources LAST (general-path ordering).
|
|
@@ -197,7 +197,7 @@ function trySmallMove(t, e, i, s) {
|
|
|
197
197
|
* needs the reset). The stage arrays are scratch — nothing live is touched. */ function stageRemoval(t, e, i, s, n, r, o, f, h) {
|
|
198
198
|
const c = t.Vt;
|
|
199
199
|
const a = t.Mt;
|
|
200
|
-
const l = t.
|
|
200
|
+
const l = t.Ye;
|
|
201
201
|
const u = [];
|
|
202
202
|
let d = i;
|
|
203
203
|
for (let p = i; p <= n; d++, p++) {
|
|
@@ -222,7 +222,7 @@ function trySmallMove(t, e, i, s) {
|
|
|
222
222
|
* index signals. Its own function so the general path's body stays under
|
|
223
223
|
* the JIT's optimization budget — the inlined commit measured ~10% slower on
|
|
224
224
|
* full-window reorders (the small-move note has the same finding). */ function commitWindow(t, e, i, s, n, r, o, f, h) {
|
|
225
|
-
const c = t.
|
|
225
|
+
const c = t.Ye;
|
|
226
226
|
const a = t.Kt;
|
|
227
227
|
const l = t.xt;
|
|
228
228
|
const u = t.wt;
|
|
@@ -267,7 +267,7 @@ function updateKeyedMap() {
|
|
|
267
267
|
// the flight's reader (pending tracking + post-settle retry) instead of
|
|
268
268
|
// vanishing. Set per pass: the first pass runs inside `computed()`, before
|
|
269
269
|
// the node exists to the caller.
|
|
270
|
-
this.H.
|
|
270
|
+
this.H.ht = getOwner();
|
|
271
271
|
const t = this.yt() || [], e = t.length;
|
|
272
272
|
t[$TRACK];
|
|
273
273
|
// top level tracking (a store array's container node)
|
|
@@ -298,7 +298,7 @@ function updateKeyedMap() {
|
|
|
298
298
|
if (e === 0) {
|
|
299
299
|
if (this.wt !== 0) {
|
|
300
300
|
this.H.dispose(false);
|
|
301
|
-
this.
|
|
301
|
+
this.Ye = [];
|
|
302
302
|
this.Mt = [];
|
|
303
303
|
this.vt = [];
|
|
304
304
|
this.wt = 0;
|
|
@@ -308,8 +308,8 @@ function updateKeyedMap() {
|
|
|
308
308
|
if (this.L && !this.vt.length) {
|
|
309
309
|
// an aborted fallback attempt leaves an owner without a mapping;
|
|
310
310
|
// dispose it before re-creating
|
|
311
|
-
this.
|
|
312
|
-
this.vt[0] = runWithOwner(this.
|
|
311
|
+
this.Ye[0]?.dispose();
|
|
312
|
+
this.vt[0] = runWithOwner(this.Ye[0] = createOwner(), this.L);
|
|
313
313
|
}
|
|
314
314
|
}
|
|
315
315
|
// fast path for new create
|
|
@@ -328,10 +328,10 @@ function updateKeyedMap() {
|
|
|
328
328
|
throw t;
|
|
329
329
|
}
|
|
330
330
|
// commit
|
|
331
|
-
if (this.
|
|
331
|
+
if (this.Ye[0]) this.Ye[0].dispose();
|
|
332
332
|
// previous fallback
|
|
333
333
|
this.vt = h;
|
|
334
|
-
this.
|
|
334
|
+
this.Ye = c;
|
|
335
335
|
r && (this.Kt = r);
|
|
336
336
|
o && (this.xt = o);
|
|
337
337
|
this.Mt = t.slice(0);
|
|
@@ -447,13 +447,13 @@ function updateKeyedMap() {
|
|
|
447
447
|
s = d.get(u);
|
|
448
448
|
if (s !== undefined && s !== -1) {
|
|
449
449
|
S[s] = this.vt[i];
|
|
450
|
-
M[s - h] = this.
|
|
450
|
+
M[s - h] = this.Ye[i];
|
|
451
451
|
r && (r[s - h] = this.Kt[i]);
|
|
452
452
|
o && (o[s - h] = this.xt[i]);
|
|
453
453
|
s = p[s - h];
|
|
454
454
|
d.set(u, s);
|
|
455
455
|
} else {
|
|
456
|
-
(w ??= []).push(this.
|
|
456
|
+
(w ??= []).push(this.Ye[i]);
|
|
457
457
|
if (true && attrHooks !== null) (O ??= []).push(l);
|
|
458
458
|
}
|
|
459
459
|
}
|
|
@@ -483,7 +483,7 @@ function updateKeyedMap() {
|
|
|
483
483
|
// save a copy of the mapped items for the next update
|
|
484
484
|
this.Mt = t.slice(0);
|
|
485
485
|
if (w) for (i = 0; i < w.length; i++) w[i].dispose();
|
|
486
|
-
if (true && attrHooks !== null && O !== undefined && _ !== undefined) attrHooks.listChurn(this.H.
|
|
486
|
+
if (true && attrHooks !== null && O !== undefined && _ !== undefined) attrHooks.listChurn(this.H.ht, O, _, e, this.Vt !== undefined);
|
|
487
487
|
}
|
|
488
488
|
});
|
|
489
489
|
return this.vt;
|
|
@@ -511,7 +511,7 @@ function updateKeyedMap() {
|
|
|
511
511
|
$t: 0,
|
|
512
512
|
qt: t,
|
|
513
513
|
Wt: s,
|
|
514
|
-
|
|
514
|
+
Ye: [],
|
|
515
515
|
vt: [],
|
|
516
516
|
zt: i?.from,
|
|
517
517
|
L: i?.fallback
|
|
@@ -530,14 +530,14 @@ function updateKeyedMap() {
|
|
|
530
530
|
// disjoint-window/front-clear/end-clear/shift special cases.
|
|
531
531
|
function updateRepeat() {
|
|
532
532
|
// As in updateKeyedMap: the node is the row callbacks' reader, per pass.
|
|
533
|
-
this.H.
|
|
533
|
+
this.H.ht = getOwner();
|
|
534
534
|
const t = this.qt();
|
|
535
535
|
const e = this.zt?.() || 0;
|
|
536
536
|
runWithOwner(this.H, () => {
|
|
537
537
|
if (t === 0) {
|
|
538
538
|
if (this.wt !== 0) {
|
|
539
539
|
this.H.dispose(false);
|
|
540
|
-
this.
|
|
540
|
+
this.Ye = [];
|
|
541
541
|
this.vt = [];
|
|
542
542
|
this.wt = 0;
|
|
543
543
|
// Reset offset to match the cleared data (#2767, repro 2).
|
|
@@ -546,8 +546,8 @@ function updateRepeat() {
|
|
|
546
546
|
if (this.L && !this.vt.length) {
|
|
547
547
|
// an aborted fallback attempt leaves an owner without a mapping;
|
|
548
548
|
// dispose it before re-creating
|
|
549
|
-
this.
|
|
550
|
-
this.vt[0] = runWithOwner(this.
|
|
549
|
+
this.Ye[0]?.dispose();
|
|
550
|
+
this.vt[0] = runWithOwner(this.Ye[0] = createOwner(), this.L);
|
|
551
551
|
}
|
|
552
552
|
return;
|
|
553
553
|
}
|
|
@@ -560,7 +560,7 @@ function updateRepeat() {
|
|
|
560
560
|
const o = new Array(t);
|
|
561
561
|
const f = new Array(t);
|
|
562
562
|
for (let t = n; t < r; t++) {
|
|
563
|
-
f[t - e] = this.
|
|
563
|
+
f[t - e] = this.Ye[t - this.$t];
|
|
564
564
|
o[t - e] = this.vt[t - this.$t];
|
|
565
565
|
}
|
|
566
566
|
try {
|
|
@@ -573,9 +573,9 @@ function updateRepeat() {
|
|
|
573
573
|
throw t;
|
|
574
574
|
}
|
|
575
575
|
// commit: dispose the previous fallback or the rows leaving the window
|
|
576
|
-
if (this.wt === 0) this.
|
|
576
|
+
if (this.wt === 0) this.Ye[0]?.dispose(); else for (let t = this.$t; t < s; t++) if (t < e || t >= i) this.Ye[t - this.$t].dispose();
|
|
577
577
|
this.vt = o;
|
|
578
|
-
this.
|
|
578
|
+
this.Ye = f;
|
|
579
579
|
this.$t = e;
|
|
580
580
|
this.wt = t;
|
|
581
581
|
});
|
package/dist/observe/reveal.js
CHANGED
|
@@ -106,7 +106,7 @@ class RevealController {
|
|
|
106
106
|
}
|
|
107
107
|
return t !== "natural";
|
|
108
108
|
}
|
|
109
|
-
|
|
109
|
+
te(t) {
|
|
110
110
|
if (this.Zt.includes(t)) return;
|
|
111
111
|
this.Zt.push(t);
|
|
112
112
|
// Gated from the start (collapsed in a collapsed sequential tail); the
|
|
@@ -124,7 +124,7 @@ class RevealController {
|
|
|
124
124
|
}
|
|
125
125
|
untrack(() => this.Bt());
|
|
126
126
|
}
|
|
127
|
-
|
|
127
|
+
oe(t) {
|
|
128
128
|
const e = this.Zt.indexOf(t);
|
|
129
129
|
if (e >= 0) this.Zt.splice(e, 1);
|
|
130
130
|
untrack(() => this.Bt());
|
|
@@ -194,8 +194,8 @@ class RevealController {
|
|
|
194
194
|
});
|
|
195
195
|
if (true) s._name = "reveal order";
|
|
196
196
|
if (r) {
|
|
197
|
-
r.
|
|
198
|
-
cleanup(() => r.
|
|
197
|
+
r.te(i);
|
|
198
|
+
cleanup(() => r.oe(i));
|
|
199
199
|
}
|
|
200
200
|
return e;
|
|
201
201
|
});
|
|
@@ -204,8 +204,8 @@ 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),
|
|
208
|
+
oe: (t, e) => t.oe(e),
|
|
209
209
|
P: t => t.Bt()
|
|
210
210
|
});
|
|
211
211
|
|
package/dist/observe/signals.js
CHANGED
|
@@ -8,7 +8,7 @@ import { computed, signal, runWithOwner, setMemo, setSignal, markRefresh, read,
|
|
|
8
8
|
|
|
9
9
|
import { registerGraph } from "./core/dev.js";
|
|
10
10
|
|
|
11
|
-
import { globalQueue, flushTransaction, dirtyQueue, GlobalQueue, resolveTx, passTx, staleReader, joinFuture } from "./core/scheduler.js";
|
|
11
|
+
import { schedule, globalQueue, flushTransaction, dirtyQueue, GlobalQueue, resolveTx, passTx, staleReader, joinFuture } from "./core/scheduler.js";
|
|
12
12
|
|
|
13
13
|
import { cleanup, createRoot, getOwner, dispose } from "./core/owner.js";
|
|
14
14
|
|
|
@@ -343,14 +343,14 @@ function createMemo(e, t) {
|
|
|
343
343
|
// or an already-displayed guess would satisfy it in a later slice. (REDERIVE
|
|
344
344
|
// after: the first pass's own error is the promise's rejection.)
|
|
345
345
|
computed(() => {
|
|
346
|
-
let
|
|
346
|
+
let u, i = false;
|
|
347
347
|
try {
|
|
348
|
-
|
|
348
|
+
u = e();
|
|
349
349
|
} catch (e) {
|
|
350
350
|
if (e instanceof NotReadyError) throw e;
|
|
351
351
|
// An error is an answer (D-41): delivered by the same frame rule.
|
|
352
|
-
|
|
353
|
-
|
|
352
|
+
u = unwrapStatusError(e);
|
|
353
|
+
i = true;
|
|
354
354
|
}
|
|
355
355
|
const s = getOwner();
|
|
356
356
|
// A truth reader waits out a `createDeferred` flight its value derives
|
|
@@ -363,9 +363,9 @@ function createMemo(e, t) {
|
|
|
363
363
|
staleReader(s, resolveTx(a));
|
|
364
364
|
return;
|
|
365
365
|
}
|
|
366
|
-
if (
|
|
367
|
-
o?.(
|
|
368
|
-
queueMicrotask(() => t(
|
|
366
|
+
if (i) return void queueMicrotask(() => r(u));
|
|
367
|
+
o?.(u);
|
|
368
|
+
queueMicrotask(() => t(u));
|
|
369
369
|
}, {
|
|
370
370
|
St: n
|
|
371
371
|
}).l |= CONFIG_REDERIVE;
|
|
@@ -525,14 +525,14 @@ function createMemo(e, t) {
|
|
|
525
525
|
// (`saving=true` beside the confirmed `version=1` is a tear no timeline
|
|
526
526
|
// contains). Updates that do not flip the predicate reveal freely.
|
|
527
527
|
const c = flushTransaction;
|
|
528
|
-
createRoot(
|
|
529
|
-
let
|
|
528
|
+
createRoot(u => {
|
|
529
|
+
let i;
|
|
530
530
|
let s;
|
|
531
531
|
const settle = e => {
|
|
532
|
-
if (
|
|
532
|
+
if (i !== undefined) clearTimeout(i);
|
|
533
533
|
if (s !== undefined) o.removeEventListener("abort", s);
|
|
534
534
|
e();
|
|
535
|
-
|
|
535
|
+
u();
|
|
536
536
|
};
|
|
537
537
|
// Authoritative: the predicate sees the truth, never the action's own
|
|
538
538
|
// optimistic guess — a guess cannot satisfy the wait for its own
|
|
@@ -547,7 +547,7 @@ function createMemo(e, t) {
|
|
|
547
547
|
// revealed with the action.
|
|
548
548
|
if (e && c !== null && globalQueue.q && resolveTx(c).tt !== 0) joinFuture(c);
|
|
549
549
|
});
|
|
550
|
-
if (t?.timeout !== undefined)
|
|
550
|
+
if (t?.timeout !== undefined) i = setTimeout(() => settle(() => n(new TimeoutError)), t.timeout);
|
|
551
551
|
if (o !== undefined) {
|
|
552
552
|
s = () => settle(() => n(o.reason));
|
|
553
553
|
o.addEventListener("abort", s, {
|
|
@@ -663,7 +663,7 @@ function createMemo(e, t) {
|
|
|
663
663
|
// still holding the optimistic value — reads do not pull (#3411). Fall
|
|
664
664
|
// to the next pass while the heap has work; `run` swapped the queue,
|
|
665
665
|
// so this lands there, and `enqueue` keeps the drain alive.
|
|
666
|
-
if (dirtyQueue.jt >= dirtyQueue.
|
|
666
|
+
if (dirtyQueue.jt >= dirtyQueue.lt) return globalQueue.enqueue(EFFECT_USER, fire);
|
|
667
667
|
// Unowned, out-of-band fire (no owner, or a children-forbidden one this
|
|
668
668
|
// one-shot must not bind to): a returned cleanup has no lifecycle to
|
|
669
669
|
// attach to. Reject it in dev; in production the return is simply
|
|
@@ -672,4 +672,46 @@ function createMemo(e, t) {
|
|
|
672
672
|
});
|
|
673
673
|
}
|
|
674
674
|
|
|
675
|
-
|
|
675
|
+
/**
|
|
676
|
+
* Schedules `callback` to run **once**, right after the pending flush
|
|
677
|
+
* finishes, in the same microtask checkpoint (before the browser paints).
|
|
678
|
+
* Unlike `onSettled`, it never waits on async work: if that flush is held by
|
|
679
|
+
* a pending transition, the callback still runs.
|
|
680
|
+
*
|
|
681
|
+
* Its main use is keeping a write out of another update's hold. Writes made
|
|
682
|
+
* in the same synchronous frame are applied together, so when one of them
|
|
683
|
+
* starts async work (a route change, a fetch), the others are held with it
|
|
684
|
+
* until it resolves. A write made in `onFlush` happens after that flush, so
|
|
685
|
+
* it is its own update: it commits on its own unless something it changes
|
|
686
|
+
* also derives from the held work (a memo or `createEffect` reading both),
|
|
687
|
+
* in which case it joins that hold as any later write would.
|
|
688
|
+
*
|
|
689
|
+
* Call it from event handlers and effect callbacks (including
|
|
690
|
+
* `createTrackedEffect` and `onSettled` bodies). In an owned scope such as a
|
|
691
|
+
* component body or a computation it warns in dev: use `onSettled` there.
|
|
692
|
+
*
|
|
693
|
+
* - A write in the callback lands after any write made inline in the same
|
|
694
|
+
* frame. Don't write the same signal both inside and outside it; use a
|
|
695
|
+
* functional setter if the order matters.
|
|
696
|
+
* - Several `onFlush` callbacks run in call order and batch into one flush.
|
|
697
|
+
* - `queueMicrotask` is not a substitute: called before the frame's first
|
|
698
|
+
* write, it runs before that flush and its write joins it.
|
|
699
|
+
*
|
|
700
|
+
* @example
|
|
701
|
+
* ```tsx
|
|
702
|
+
* // A menu closes now even when the consumer's onSelect starts async work.
|
|
703
|
+
* const select = (id: string) => {
|
|
704
|
+
* onFlush(() => setOpen(false));
|
|
705
|
+
* props.onSelect?.(id);
|
|
706
|
+
* };
|
|
707
|
+
* ```
|
|
708
|
+
*
|
|
709
|
+
* @param callback Function to run once after the pending flush
|
|
710
|
+
*/ function onFlush(e) {
|
|
711
|
+
// The frame's flush is queued (or already draining) ahead of this
|
|
712
|
+
// microtask, so the callback's writes start a flush of their own.
|
|
713
|
+
schedule();
|
|
714
|
+
queueMicrotask(e);
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
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.oo.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
|