@solidjs/signals 2.0.0-beta.25 → 2.0.0-beta.27
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.js +370 -73
- package/dist/node.cjs +1247 -946
- package/dist/prod/core/action.js +11 -6
- package/dist/prod/core/async.js +212 -108
- package/dist/prod/core/core.js +203 -189
- package/dist/prod/core/effect.js +28 -28
- package/dist/prod/core/external.js +3 -3
- package/dist/prod/core/graph.js +28 -28
- package/dist/prod/core/heap.js +30 -30
- package/dist/prod/core/lanes.js +15 -15
- package/dist/prod/core/optimistic.js +39 -39
- package/dist/prod/core/owner.js +44 -44
- package/dist/prod/core/scheduler.js +127 -106
- package/dist/prod/core/verdict.js +54 -54
- package/dist/prod/map.js +60 -60
- package/dist/prod/signals.js +1 -1
- package/dist/prod/store/optimistic.js +55 -36
- package/dist/prod/store/projection.js +38 -33
- package/dist/prod/store/reconcile.js +299 -243
- package/dist/prod/store/store.js +127 -50
- package/dist/types/core/async.d.ts +2 -0
- package/dist/types/core/scheduler.d.ts +1 -0
- package/dist/types/store/optimistic.d.ts +2 -2
- package/dist/types/store/projection.d.ts +7 -2
- package/dist/types/store/reconcile.d.ts +14 -0
- package/dist/types/store/store.d.ts +2 -2
- package/dist/types-cjs/core/async.d.cts +2 -0
- package/dist/types-cjs/core/scheduler.d.cts +1 -0
- package/dist/types-cjs/store/optimistic.d.cts +2 -2
- package/dist/types-cjs/store/projection.d.cts +7 -2
- package/dist/types-cjs/store/reconcile.d.cts +14 -0
- package/dist/types-cjs/store/store.d.cts +2 -2
- package/package.json +1 -1
package/dist/prod/core/action.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { globalQueue, activeTransition, currentTransition,
|
|
1
|
+
import { globalQueue, activeTransition, currentTransition, schedule, flush } from "./scheduler.js";
|
|
2
2
|
|
|
3
3
|
import { isThenable } from "./async.js";
|
|
4
4
|
|
|
@@ -79,13 +79,18 @@ function restoreTransition(e, r) {
|
|
|
79
79
|
globalQueue.initTransition();
|
|
80
80
|
let o = activeTransition;
|
|
81
81
|
o.ie.push(i);
|
|
82
|
-
const done = (e, r,
|
|
82
|
+
const done = (e, r, u = false) => {
|
|
83
83
|
o = currentTransition(o);
|
|
84
|
-
const
|
|
85
|
-
if (
|
|
86
|
-
|
|
84
|
+
const s = o.ie.indexOf(i);
|
|
85
|
+
if (s >= 0) o.ie.splice(s, 1);
|
|
86
|
+
// Re-adopt through initTransition like every other resumption site:
|
|
87
|
+
// a bare setActiveTransition leaves globalQueue._batch as a detached
|
|
88
|
+
// ambient batch, and anything registered before the scheduled flush
|
|
89
|
+
// (held writes on a merging transition, optimistic overrides,
|
|
90
|
+
// affects() marks) lands there with nothing to ever finalize it.
|
|
91
|
+
globalQueue.initTransition(o);
|
|
87
92
|
schedule();
|
|
88
|
-
|
|
93
|
+
u ? n(r) : t(e);
|
|
89
94
|
};
|
|
90
95
|
const step = (e, r) => {
|
|
91
96
|
let t;
|
package/dist/prod/core/async.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { STATUS_UNINITIALIZED, STATUS_ERROR, STATUS_PENDING, REACTIVE_DIRTY, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING
|
|
1
|
+
import { STATUS_UNINITIALIZED, STATUS_ERROR, STATUS_PENDING, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE, REACTIVE_DIRTY, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING } from "./constants.js";
|
|
2
2
|
|
|
3
3
|
import { untrack, context, setSignal } from "./core.js";
|
|
4
4
|
|
|
@@ -6,7 +6,7 @@ import "./invariants.js";
|
|
|
6
6
|
|
|
7
7
|
import { NotReadyError, StatusError } from "./error.js";
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { unobserved, trimStaleDeps } from "./graph.js";
|
|
10
10
|
|
|
11
11
|
import { enqueueSub } from "./heap.js";
|
|
12
12
|
|
|
@@ -14,7 +14,7 @@ import { resolveTransition, hasActiveOverride, assignOrMergeLane, resolveLane }
|
|
|
14
14
|
|
|
15
15
|
import { cleanup } from "./owner.js";
|
|
16
16
|
|
|
17
|
-
import { globalQueue, GlobalQueue, clock, schedule, flush, queuePendingNode, insertSubs } from "./scheduler.js";
|
|
17
|
+
import { globalQueue, GlobalQueue, clock, currentTransition, schedule, flush, queuePendingNode, insertSubs } from "./scheduler.js";
|
|
18
18
|
|
|
19
19
|
// The lazily-created Set is the ONE container for pending sources. Its
|
|
20
20
|
// predecessor — a singular slot promoted to a Set on the second source —
|
|
@@ -38,58 +38,125 @@ function clearPendingSources(e) {
|
|
|
38
38
|
e.oe = undefined;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
function setPendingError(e, n,
|
|
41
|
+
function setPendingError(e, n, t) {
|
|
42
42
|
if (!n) {
|
|
43
43
|
e._ = null;
|
|
44
44
|
return;
|
|
45
45
|
}
|
|
46
|
-
if (
|
|
47
|
-
e._ =
|
|
46
|
+
if (t instanceof NotReadyError && t.source === n) {
|
|
47
|
+
e._ = t;
|
|
48
48
|
return;
|
|
49
49
|
}
|
|
50
|
-
const
|
|
51
|
-
if (!(
|
|
50
|
+
const r = e._;
|
|
51
|
+
if (!(r instanceof NotReadyError) || r.source !== n) {
|
|
52
52
|
e._ = new NotReadyError(n);
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
function forEachDependent(e, n) {
|
|
57
|
-
for (let
|
|
57
|
+
for (let t = e.o; t !== null; t = t.ue) n(t.le, t);
|
|
58
58
|
// `?? null`: affects() marks route plain signals (no `_child` slot) through here.
|
|
59
|
-
for (let
|
|
60
|
-
for (let e =
|
|
59
|
+
for (let t = e.u ?? null; t !== null; t = t.fe) {
|
|
60
|
+
for (let e = t.o; e !== null; e = e.ue) n(e.le, e);
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
// Queue a node to re-run on the next flush (used both when a pending source
|
|
65
65
|
// settles and when an `isPending` observer must re-evaluate after a real error):
|
|
66
66
|
// shared scheduling helper in heap.ts (tracked effects bypass the heap).
|
|
67
|
+
// Settle-time counterpart of unlinkSubs' last-one-out check. A lazy node that
|
|
68
|
+
// loses its last subscriber while STATUS_PENDING is exempt from autodispose
|
|
69
|
+
// (the in-flight work is an observer), so whatever CLEARS that pending state
|
|
70
|
+
// must run the release — otherwise the node stays linked and recomputes
|
|
71
|
+
// forever with zero subscribers (#2934). The node's own promise/iterator
|
|
72
|
+
// callbacks handle their own release (settleAutodispose in handleAsync); this
|
|
73
|
+
// covers derivatively-pending dependents, which have no callbacks of their own.
|
|
74
|
+
function releaseIfSettledUnobserved(e) {
|
|
75
|
+
e.ae && e.T & CONFIG_AUTO_DISPOSE && !e.o && !(e.se & REACTIVE_ZOMBIE) && !(e.S & STATUS_PENDING) && unobserved(e);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Error-path sweep: notifyStatus(STATUS_ERROR) clears dependents' pending
|
|
79
|
+
// sources through its own recursion (no per-node settle callback), so after
|
|
80
|
+
// the propagation completes, walk the same graph for stranded lazy nodes.
|
|
81
|
+
// Collect-then-release so unobserved() never unlinks under the walk.
|
|
82
|
+
function releaseSettledDependents(e) {
|
|
83
|
+
let n;
|
|
84
|
+
const t = new Set;
|
|
85
|
+
const visit = e => {
|
|
86
|
+
if (t.has(e)) return;
|
|
87
|
+
t.add(e);
|
|
88
|
+
if (!e.o && e.T & CONFIG_AUTO_DISPOSE) (n ??= []).push(e);
|
|
89
|
+
forEachDependent(e, visit);
|
|
90
|
+
};
|
|
91
|
+
forEachDependent(e, visit);
|
|
92
|
+
if (n) for (const e of n) releaseIfSettledUnobserved(e);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Error-dimension twin of settlePendingSource's blocked re-enqueue (#2949):
|
|
96
|
+
// a node in STATUS_ERROR that recovers by recomputing to an UNCHANGED value
|
|
97
|
+
// fires no value notification — the recovery is completely silent. But a
|
|
98
|
+
// dependent that re-ran during the error window consumed its dirty flag and
|
|
99
|
+
// committed nothing (the fresh sibling values it read were absorbed into an
|
|
100
|
+
// errored run), so its committed value is stale. The propagated error is one
|
|
101
|
+
// object identity down the whole dependent tree, and holding it is exactly
|
|
102
|
+
// the "blocked on this error" marker — re-enqueue those holders so they
|
|
103
|
+
// re-run: fresh values commit and flow, and a dependent with another
|
|
104
|
+
// still-broken source simply re-errors. The async dimension needs no twin of
|
|
105
|
+
// its own: recovery there passes through a pending window whose re-runners
|
|
106
|
+
// set _blocked and ride settlePendingSource. Walks the full dependent graph
|
|
107
|
+
// (releaseSettledDependents shape): identity holders can sit below an
|
|
108
|
+
// intermediate whose own error state has since been scrubbed or replaced
|
|
109
|
+
// (e.g. an error boundary's tree node).
|
|
110
|
+
function settleErroredDependents(e, n) {
|
|
111
|
+
let t = false;
|
|
112
|
+
const r = new Set;
|
|
113
|
+
const visit = e => {
|
|
114
|
+
if (r.has(e)) return;
|
|
115
|
+
r.add(e);
|
|
116
|
+
if (e._ === n) {
|
|
117
|
+
enqueueSub(e);
|
|
118
|
+
t = true;
|
|
119
|
+
}
|
|
120
|
+
forEachDependent(e, visit);
|
|
121
|
+
};
|
|
122
|
+
forEachDependent(e, visit);
|
|
123
|
+
if (t) schedule();
|
|
124
|
+
}
|
|
125
|
+
|
|
67
126
|
function settlePendingSource(e) {
|
|
68
127
|
let n = false;
|
|
128
|
+
let t;
|
|
69
129
|
const r = new Set;
|
|
70
130
|
// Companion updates no-op without the verdict layer (null hook).
|
|
71
|
-
const
|
|
72
|
-
const settle =
|
|
73
|
-
if (r.has(
|
|
74
|
-
r.add(
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
if (
|
|
78
|
-
setPendingError(
|
|
79
|
-
|
|
131
|
+
const o = GlobalQueue.ce;
|
|
132
|
+
const settle = u => {
|
|
133
|
+
if (r.has(u) || !removePendingSource(u, e)) return;
|
|
134
|
+
r.add(u);
|
|
135
|
+
u.Se = clock;
|
|
136
|
+
const l = u.oe?.values().next().value;
|
|
137
|
+
if (l) {
|
|
138
|
+
setPendingError(u, l);
|
|
139
|
+
o !== null && o(u);
|
|
80
140
|
} else {
|
|
81
|
-
|
|
82
|
-
setPendingError(
|
|
83
|
-
|
|
84
|
-
if (
|
|
85
|
-
enqueueSub(
|
|
141
|
+
u.S &= ~STATUS_PENDING;
|
|
142
|
+
setPendingError(u);
|
|
143
|
+
o !== null && o(u);
|
|
144
|
+
if (u.de) {
|
|
145
|
+
enqueueSub(u);
|
|
86
146
|
n = true;
|
|
87
147
|
}
|
|
88
|
-
|
|
148
|
+
u.de = false;
|
|
149
|
+
// Fully settled with nobody watching: release candidate (#2934). Checked
|
|
150
|
+
// again at release time — deferred so unobserved() can't unlink subs
|
|
151
|
+
// lists this walk is still iterating.
|
|
152
|
+
if (!u.o && u.T & CONFIG_AUTO_DISPOSE) (t ??= []).push(u);
|
|
89
153
|
}
|
|
90
|
-
forEachDependent(
|
|
154
|
+
forEachDependent(u, settle);
|
|
91
155
|
};
|
|
92
156
|
forEachDependent(e, settle);
|
|
157
|
+
// Release before the flush schedule below: unobserved() pulls the node back
|
|
158
|
+
// out of the heap, so the enqueueSub above never recomputes a released node.
|
|
159
|
+
if (t) for (const e of t) releaseIfSettledUnobserved(e);
|
|
93
160
|
if (n) schedule();
|
|
94
161
|
}
|
|
95
162
|
|
|
@@ -98,44 +165,69 @@ function isThenable(e) {
|
|
|
98
165
|
return e != null && typeof e === "object" && typeof e.then === "function";
|
|
99
166
|
}
|
|
100
167
|
|
|
101
|
-
function handleAsync(e, n,
|
|
102
|
-
let
|
|
168
|
+
function handleAsync(e, n, t) {
|
|
169
|
+
let r = false;
|
|
103
170
|
let o = false;
|
|
104
171
|
if (typeof n === "object" && n !== null) {
|
|
105
172
|
untrack(() => {
|
|
106
|
-
|
|
107
|
-
o = !
|
|
173
|
+
r = n[Symbol.asyncIterator];
|
|
174
|
+
o = !r && isThenable(n);
|
|
108
175
|
});
|
|
109
176
|
}
|
|
110
|
-
if (!o && !
|
|
177
|
+
if (!o && !r) {
|
|
111
178
|
e.Te = null;
|
|
112
179
|
return n;
|
|
113
180
|
}
|
|
114
181
|
e.Te = n;
|
|
115
182
|
let u;
|
|
116
|
-
|
|
183
|
+
// Settle-time transition re-entry. The loading rail is invisible to
|
|
184
|
+
// transactions (#2933): a boundary-caught first load never registers as an
|
|
185
|
+
// async reporter, so its settle — the boundary's fallback -> content
|
|
186
|
+
// reveal — must flow ambiently. The node can still carry a `_transition`
|
|
187
|
+
// stamp (pending-node bookkeeping rides through the stamping sites), and
|
|
188
|
+
// blindly re-entering that stamped, still-incomplete transaction stashed
|
|
189
|
+
// the reveal with it — a deadlock when the transaction's completion
|
|
190
|
+
// depended on the reveal (#2937). An ESCAPED first load did register and
|
|
191
|
+
// keeps transition scheduling; initialized (value-holding) pending settles
|
|
192
|
+
// are the transaction's reveal machinery and always re-enter.
|
|
193
|
+
const settleTransition = () => {
|
|
194
|
+
const n = resolveTransition(e);
|
|
195
|
+
if (n && e.S & STATUS_UNINITIALIZED && !currentTransition(n).Ee.has(e)) {
|
|
196
|
+
// Drop the stale stamp too: the plain settle write (setSignal) and the
|
|
197
|
+
// stash-path restamp both re-enter the transaction through it.
|
|
198
|
+
e.Ie = null;
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
globalQueue.initTransition(n);
|
|
202
|
+
};
|
|
203
|
+
const handleError = t => {
|
|
117
204
|
if (e.Te !== n) return;
|
|
118
|
-
|
|
205
|
+
settleTransition();
|
|
119
206
|
// NotReadyError from rejected promises should be treated as pending, not error
|
|
120
|
-
|
|
121
|
-
e
|
|
207
|
+
const r = t instanceof NotReadyError;
|
|
208
|
+
notifyStatus(e, r ? STATUS_PENDING : STATUS_ERROR, t);
|
|
209
|
+
e.Se = clock;
|
|
210
|
+
// A real error settles derivatively-pending dependents (notifyStatus
|
|
211
|
+
// cleared their pending sources), so stranded lazy ones release here —
|
|
212
|
+
// the error twin of settlePendingSource's release (#2934).
|
|
213
|
+
if (!r) releaseSettledDependents(e);
|
|
122
214
|
};
|
|
123
|
-
const asyncWrite = (
|
|
215
|
+
const asyncWrite = (r, o) => {
|
|
124
216
|
if (e.Te !== n) return;
|
|
125
217
|
// If the node was dirtied by a newer write (optimistic override or regular),
|
|
126
218
|
// skip this stale async result — the upcoming flush will recompute the node
|
|
127
219
|
// with the new value, creating a fresh Promise that supersedes this one.
|
|
128
220
|
if (e.se & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY)) return;
|
|
129
|
-
|
|
221
|
+
settleTransition();
|
|
130
222
|
const u = !!(e.S & STATUS_UNINITIALIZED);
|
|
131
223
|
trimStaleDeps(e);
|
|
132
224
|
clearStatus(e);
|
|
133
225
|
const l = resolveLane(e);
|
|
134
|
-
if (l) l.
|
|
135
|
-
if (
|
|
136
|
-
r
|
|
226
|
+
if (l) l.Ne.delete(e);
|
|
227
|
+
if (t) {
|
|
228
|
+
t(r);
|
|
137
229
|
if (u) clearStatus(e, true);
|
|
138
|
-
} else if (e.
|
|
230
|
+
} else if (e.Ae !== undefined) {
|
|
139
231
|
// Optimistic node — resting OR covered by an active override — holds
|
|
140
232
|
// through the shared pending-node path, exactly like a plain async memo,
|
|
141
233
|
// so the commit clears STATUS_UNINITIALIZED (#2806) and elevation to
|
|
@@ -145,8 +237,8 @@ function handleAsync(e, n, r) {
|
|
|
145
237
|
// (A17 — every reader sees the override); the revert reveals whatever
|
|
146
238
|
// has committed by then, so corrections reveal atomically with their
|
|
147
239
|
// transition rather than escaping it.
|
|
148
|
-
if (e.
|
|
149
|
-
e.
|
|
240
|
+
if (e.De === NOT_PENDING) queuePendingNode(e);
|
|
241
|
+
e.De = r;
|
|
150
242
|
// The hold is a companion-visible write like any other (A13/A19): the
|
|
151
243
|
// clearStatus() above computed its verdict before the hold existed, so
|
|
152
244
|
// isPending must re-derive (the value is not final until commit — V1)
|
|
@@ -154,22 +246,22 @@ function handleAsync(e, n, r) {
|
|
|
154
246
|
// only notified when the hold is visible to them: under an active
|
|
155
247
|
// override every reader sees the override (A17), so waking subs would
|
|
156
248
|
// re-show an unchanged view — the revert is the notification point.
|
|
157
|
-
GlobalQueue.
|
|
249
|
+
GlobalQueue._e !== null && GlobalQueue._e(e, r);
|
|
158
250
|
if (!hasActiveOverride(e)) insertSubs(e);
|
|
159
|
-
e.
|
|
251
|
+
e.Se = clock;
|
|
160
252
|
} else if (l) {
|
|
161
253
|
// Route through lane's effect queue for independent flushing
|
|
162
|
-
const n = e.
|
|
163
|
-
const
|
|
164
|
-
const o = e.
|
|
254
|
+
const n = e.Pe;
|
|
255
|
+
const t = e.Ue;
|
|
256
|
+
const o = e.be;
|
|
165
257
|
try {
|
|
166
|
-
if (!n && u || !o || !o(
|
|
167
|
-
e.
|
|
168
|
-
e.
|
|
258
|
+
if (!n && u || !o || !o(r, t)) {
|
|
259
|
+
e.Ue = r;
|
|
260
|
+
e.Se = clock;
|
|
169
261
|
// The latest() shadow write gives latest() effects independent lanes; the
|
|
170
262
|
// _pendingSignal update is a no-op repeat of the clearStatus() call above
|
|
171
263
|
// (computePendingState doesn't read _value).
|
|
172
|
-
GlobalQueue.
|
|
264
|
+
GlobalQueue._e !== null && GlobalQueue._e(e, r);
|
|
173
265
|
insertSubs(e, true);
|
|
174
266
|
}
|
|
175
267
|
} catch (n) {
|
|
@@ -181,7 +273,7 @@ function handleAsync(e, n, r) {
|
|
|
181
273
|
}
|
|
182
274
|
} else {
|
|
183
275
|
try {
|
|
184
|
-
setSignal(e, () =>
|
|
276
|
+
setSignal(e, () => r);
|
|
185
277
|
} catch (n) {
|
|
186
278
|
// Same containment as above: setSignal's comparator throw is the only
|
|
187
279
|
// pre-commit failure here, and there is no user callsite to throw to.
|
|
@@ -198,17 +290,21 @@ function handleAsync(e, n, r) {
|
|
|
198
290
|
// work (a lazy async memo would otherwise tear down and re-execute — one
|
|
199
291
|
// fetch per suspended re-read). Settling is that observer's release, so
|
|
200
292
|
// it runs the same last-one-out check the other release sites run.
|
|
293
|
+
// Returns whether the node released, so the iterator branch can stop
|
|
294
|
+
// pulling values instead of pumping an unobserved stream forever (#2935).
|
|
201
295
|
const settleAutodispose = () => {
|
|
202
296
|
if (e.T & CONFIG_AUTO_DISPOSE && !e.o && !(e.S & STATUS_PENDING)) {
|
|
203
297
|
unobserved(e);
|
|
298
|
+
return true;
|
|
204
299
|
}
|
|
300
|
+
return false;
|
|
205
301
|
};
|
|
206
302
|
if (o) {
|
|
207
|
-
let
|
|
303
|
+
let t = false, r = false, o, l = true;
|
|
208
304
|
n.then(e => {
|
|
209
305
|
if (l) {
|
|
210
306
|
u = e;
|
|
211
|
-
|
|
307
|
+
t = true;
|
|
212
308
|
} else {
|
|
213
309
|
asyncWrite(e);
|
|
214
310
|
settleAutodispose();
|
|
@@ -216,87 +312,95 @@ function handleAsync(e, n, r) {
|
|
|
216
312
|
}, e => {
|
|
217
313
|
if (l) {
|
|
218
314
|
o = e;
|
|
219
|
-
|
|
315
|
+
r = true;
|
|
220
316
|
} else {
|
|
221
317
|
handleError(e);
|
|
222
318
|
settleAutodispose();
|
|
223
319
|
}
|
|
224
320
|
});
|
|
225
321
|
l = false;
|
|
226
|
-
if (
|
|
322
|
+
if (r) {
|
|
227
323
|
// Settle through the same status path an async rejection uses, then
|
|
228
324
|
// unwind the in-progress synchronous read so the errored node isn't
|
|
229
325
|
// momentarily read as `undefined`.
|
|
230
326
|
handleError(o);
|
|
231
327
|
throw o;
|
|
232
|
-
} else if (!
|
|
328
|
+
} else if (!t) {
|
|
233
329
|
globalQueue.initTransition(resolveTransition(e));
|
|
234
330
|
throw new NotReadyError(context);
|
|
235
331
|
}
|
|
236
332
|
}
|
|
237
|
-
if (
|
|
238
|
-
const
|
|
239
|
-
let
|
|
333
|
+
if (r) {
|
|
334
|
+
const t = n[Symbol.asyncIterator]();
|
|
335
|
+
let r = false;
|
|
240
336
|
let o = false;
|
|
241
337
|
let l = true;
|
|
242
338
|
cleanup(() => {
|
|
243
339
|
if (o) return;
|
|
244
340
|
o = true;
|
|
245
341
|
try {
|
|
246
|
-
const e =
|
|
342
|
+
const e = t.return?.();
|
|
247
343
|
if (isThenable(e)) e.then(undefined, () => {});
|
|
248
344
|
} catch {}
|
|
249
345
|
});
|
|
346
|
+
// Release check before each next pull: an unobserved lazy node must tear
|
|
347
|
+
// down (its cleanup above closes the iterator) instead of pumping the
|
|
348
|
+
// stream forever with zero subscribers (#2935).
|
|
349
|
+
const iterateOrRelease = () => {
|
|
350
|
+
if (!settleAutodispose()) iterate();
|
|
351
|
+
};
|
|
250
352
|
const iterate = () => {
|
|
251
|
-
let
|
|
252
|
-
|
|
353
|
+
let s, i, f = false, a = false, c = true;
|
|
354
|
+
t.next().then(t => {
|
|
253
355
|
if (c) {
|
|
254
|
-
|
|
356
|
+
s = t;
|
|
255
357
|
f = true;
|
|
256
|
-
if (
|
|
358
|
+
if (t.done) o = true;
|
|
257
359
|
} else if (e.Te !== n) {
|
|
258
360
|
return;
|
|
259
|
-
} else if (!
|
|
260
|
-
|
|
261
|
-
asyncWrite(
|
|
361
|
+
} else if (!t.done) {
|
|
362
|
+
r = true;
|
|
363
|
+
asyncWrite(t.value, iterateOrRelease);
|
|
262
364
|
} else {
|
|
263
365
|
o = true;
|
|
264
|
-
if (
|
|
366
|
+
if (r) {
|
|
265
367
|
schedule();
|
|
266
368
|
flush();
|
|
267
369
|
} else {
|
|
268
370
|
// Empty completion settles like the immediately-done sync path.
|
|
269
371
|
asyncWrite(undefined);
|
|
270
372
|
}
|
|
373
|
+
settleAutodispose();
|
|
271
374
|
}
|
|
272
|
-
},
|
|
375
|
+
}, t => {
|
|
273
376
|
if (c) {
|
|
274
|
-
|
|
377
|
+
i = t;
|
|
275
378
|
a = true;
|
|
276
379
|
} else if (e.Te === n) {
|
|
277
380
|
o = true;
|
|
278
|
-
handleError(
|
|
381
|
+
handleError(t);
|
|
382
|
+
settleAutodispose();
|
|
279
383
|
}
|
|
280
384
|
});
|
|
281
385
|
c = false;
|
|
282
386
|
if (a) {
|
|
283
387
|
// Match the promise branch, but only rethrow during the initial read.
|
|
284
388
|
o = true;
|
|
285
|
-
handleError(
|
|
286
|
-
if (l) throw
|
|
389
|
+
handleError(i);
|
|
390
|
+
if (l) throw i;
|
|
287
391
|
return true;
|
|
288
392
|
}
|
|
289
|
-
if (f && !
|
|
290
|
-
u =
|
|
291
|
-
|
|
393
|
+
if (f && !s.done) {
|
|
394
|
+
u = s.value;
|
|
395
|
+
r = true;
|
|
292
396
|
return iterate();
|
|
293
397
|
}
|
|
294
|
-
return f &&
|
|
398
|
+
return f && s.done;
|
|
295
399
|
};
|
|
296
|
-
const
|
|
400
|
+
const s = iterate();
|
|
297
401
|
// Later iterate() calls run from asyncWrite, where rethrowing would be unhandled.
|
|
298
402
|
l = false;
|
|
299
|
-
if (!
|
|
403
|
+
if (!r && !s) {
|
|
300
404
|
globalQueue.initTransition(resolveTransition(e));
|
|
301
405
|
throw new NotReadyError(context);
|
|
302
406
|
}
|
|
@@ -306,75 +410,75 @@ function handleAsync(e, n, r) {
|
|
|
306
410
|
|
|
307
411
|
function clearStatus(e, n = false) {
|
|
308
412
|
if (e.oe) clearPendingSources(e);
|
|
309
|
-
if (e.
|
|
413
|
+
if (e.de) e.de = false;
|
|
310
414
|
// The pending window is over; its quiet classification dies with it.
|
|
311
415
|
// (Unconditional: _reask is baked into the node literals, so this is a
|
|
312
416
|
// plain store to an existing slot — no shape change.)
|
|
313
|
-
e.
|
|
417
|
+
e.Re = false;
|
|
314
418
|
e.S = n ? 0 : e.S & STATUS_UNINITIALIZED;
|
|
315
419
|
if (e._) setPendingError(e);
|
|
316
420
|
// Update pending signal for isPending() reactivity (companions only exist
|
|
317
421
|
// once the verdict layer created them, which installs the hooks).
|
|
318
|
-
if (e.
|
|
319
|
-
if (e.u && GlobalQueue.
|
|
422
|
+
if (e.he || e.pe) GlobalQueue.ce(e);
|
|
423
|
+
if (e.u && GlobalQueue.Ge !== null) GlobalQueue.Ge(e);
|
|
320
424
|
if (e.i) e.i();
|
|
321
425
|
}
|
|
322
426
|
|
|
323
|
-
function notifyStatus(e, n,
|
|
427
|
+
function notifyStatus(e, n, t, r, o) {
|
|
324
428
|
// Wrap regular errors to track source node
|
|
325
|
-
if (n === STATUS_ERROR && !(
|
|
326
|
-
const u = n === STATUS_PENDING &&
|
|
429
|
+
if (n === STATUS_ERROR && !(t instanceof StatusError) && !(t instanceof NotReadyError)) t = new StatusError(e, t);
|
|
430
|
+
const u = n === STATUS_PENDING && t instanceof NotReadyError ? t.source : undefined;
|
|
327
431
|
const l = u === e;
|
|
328
|
-
const
|
|
329
|
-
const
|
|
330
|
-
if (!
|
|
432
|
+
const s = n === STATUS_PENDING && e.Ae !== undefined && !l;
|
|
433
|
+
const i = s && hasActiveOverride(e);
|
|
434
|
+
if (!r) {
|
|
331
435
|
if (n === STATUS_PENDING && u) {
|
|
332
436
|
addPendingSource(e, u);
|
|
333
437
|
e.S = STATUS_PENDING | e.S & STATUS_UNINITIALIZED;
|
|
334
438
|
// Preserve the current source on this propagation so render-effect notification
|
|
335
439
|
// can register every distinct pending source with the transition.
|
|
336
|
-
setPendingError(e, u,
|
|
440
|
+
setPendingError(e, u, t);
|
|
337
441
|
} else {
|
|
338
442
|
clearPendingSources(e);
|
|
339
443
|
e.S = n | (n !== STATUS_ERROR ? e.S & STATUS_UNINITIALIZED : 0);
|
|
340
|
-
e._ =
|
|
444
|
+
e._ = t;
|
|
341
445
|
}
|
|
342
|
-
GlobalQueue.
|
|
343
|
-
if (e.u && GlobalQueue.
|
|
446
|
+
GlobalQueue.ce !== null && GlobalQueue.ce(e);
|
|
447
|
+
if (e.u && GlobalQueue.Ge !== null) GlobalQueue.Ge(e);
|
|
344
448
|
}
|
|
345
|
-
if (o && !
|
|
449
|
+
if (o && !r) {
|
|
346
450
|
assignOrMergeLane(e, o);
|
|
347
451
|
}
|
|
348
|
-
const f =
|
|
349
|
-
const a =
|
|
452
|
+
const f = r || i;
|
|
453
|
+
const a = r || s ? undefined : o;
|
|
350
454
|
if (e.i) {
|
|
351
|
-
if (
|
|
455
|
+
if (r && n === STATUS_PENDING) {
|
|
352
456
|
return;
|
|
353
457
|
}
|
|
354
458
|
if (f) {
|
|
355
|
-
e.i(n,
|
|
459
|
+
e.i(n, t);
|
|
356
460
|
} else {
|
|
357
461
|
e.i();
|
|
358
462
|
}
|
|
359
463
|
return;
|
|
360
464
|
}
|
|
361
|
-
forEachDependent(e, (e,
|
|
362
|
-
e.
|
|
363
|
-
if (n === STATUS_PENDING && u && !e.oe?.has(u) || n !== STATUS_PENDING && (e._ !==
|
|
465
|
+
forEachDependent(e, (e, r) => {
|
|
466
|
+
e.Se = clock;
|
|
467
|
+
if (n === STATUS_PENDING && u && !e.oe?.has(u) || n !== STATUS_PENDING && (e._ !== t || e.oe)) {
|
|
364
468
|
// A pending-observer link is the subscription an `isPending` read created.
|
|
365
469
|
// It exists so the observer re-runs when the source settles, but it must
|
|
366
470
|
// not carry a real (non-NotReadyError) error — the synchronous `isPending`
|
|
367
471
|
// read swallows those, and the async path must match. Re-run the observer
|
|
368
472
|
// so `isPending` re-evaluates (to not-pending) instead of forwarding.
|
|
369
|
-
if (
|
|
473
|
+
if (r.Oe && n !== STATUS_PENDING && !(t instanceof NotReadyError)) {
|
|
370
474
|
enqueueSub(e);
|
|
371
475
|
schedule();
|
|
372
476
|
return;
|
|
373
477
|
}
|
|
374
|
-
if (!f && !e.
|
|
375
|
-
notifyStatus(e, n,
|
|
478
|
+
if (!f && !e.Ie) queuePendingNode(e);
|
|
479
|
+
notifyStatus(e, n, t, f, a);
|
|
376
480
|
}
|
|
377
481
|
});
|
|
378
482
|
}
|
|
379
483
|
|
|
380
|
-
export { addPendingSource, clearStatus, forEachDependent, handleAsync, isThenable, notifyStatus, setPendingError, settlePendingSource };
|
|
484
|
+
export { addPendingSource, clearStatus, forEachDependent, handleAsync, isThenable, notifyStatus, releaseSettledDependents, setPendingError, settleErroredDependents, settlePendingSource };
|