@solidjs/signals 2.0.0-rc.16 → 2.0.0-rc.18
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 +1972 -1751
- package/dist/dev.attribution.js +16 -16
- package/dist/dev.js +672 -319
- package/dist/observe/boundaries.js +2 -2
- package/dist/observe/core/async.js +151 -116
- package/dist/observe/core/constants.js +3 -1
- package/dist/observe/core/context.js +2 -0
- package/dist/observe/core/core.js +51 -35
- package/dist/observe/core/effect.js +29 -30
- package/dist/observe/core/graph.js +13 -11
- package/dist/observe/core/lanes.js +25 -14
- package/dist/observe/core/owner.js +77 -51
- package/dist/observe/core/scheduler.js +202 -175
- package/dist/observe/core/verdict.js +5 -2
- package/dist/observe/deferred.js +16 -13
- package/dist/observe/from.js +248 -0
- package/dist/observe/index.js +2 -0
- package/dist/observe/map.js +2 -2
- package/dist/observe/signals.js +6 -1
- package/dist/observe/store/affects.js +13 -13
- package/dist/observe/store/optimistic.js +112 -71
- package/dist/observe/store/reconcile.js +137 -121
- package/dist/observe/store/store.js +612 -626
- package/dist/observe/store/target.js +1 -5
- package/dist/prod/boundaries.js +2 -2
- package/dist/prod/core/async.js +211 -176
- package/dist/prod/core/constants.js +3 -1
- package/dist/prod/core/context.js +2 -0
- package/dist/prod/core/core.js +63 -47
- package/dist/prod/core/effect.js +24 -25
- package/dist/prod/core/graph.js +12 -10
- package/dist/prod/core/heap.js +1 -1
- package/dist/prod/core/lanes.js +39 -28
- package/dist/prod/core/owner.js +70 -44
- package/dist/prod/core/scheduler.js +78 -51
- package/dist/prod/core/truth.js +4 -4
- package/dist/prod/core/verdict.js +11 -8
- package/dist/prod/deferred.js +17 -14
- package/dist/prod/from.js +248 -0
- package/dist/prod/index.js +2 -0
- package/dist/prod/map.js +2 -2
- package/dist/prod/signals.js +18 -13
- package/dist/prod/store/optimistic.js +112 -71
- package/dist/prod/store/reconcile.js +137 -121
- package/dist/prod/store/store.js +109 -121
- package/dist/prod/store/target.js +13 -17
- package/dist/types/core/async.d.ts +12 -2
- package/dist/types/core/constants.d.ts +5 -0
- package/dist/types/core/core.d.ts +29 -6
- package/dist/types/core/dev.d.ts +9 -1
- package/dist/types/core/error.d.ts +9 -1
- package/dist/types/core/owner.d.ts +4 -0
- package/dist/types/core/scheduler.d.ts +38 -7
- package/dist/types/from.d.ts +47 -0
- package/dist/types/index.d.ts +1 -0
- package/package.json +1 -1
package/dist/prod/core/async.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { STATUS_ERROR, STATUS_PENDING, STATUS_UNINITIALIZED, CONFIG_GUESS, CONFIG_HELD, REACTIVE_STAGED_READ, CONFIG_STAGED, CONFIG_STAGED_ERROR, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE, REACTIVE_DIRTY, NOT_PENDING, REACTIVE_DISPOSED, CONFIG_REDERIVE, CONFIG_VERDICT, CONFIG_OVERRIDE } from "./constants.js";
|
|
1
|
+
import { STATUS_ERROR, STATUS_PENDING, STATUS_UNINITIALIZED, CONFIG_GUESS, REACTIVE_RECOMPUTING_DEPS, CONFIG_HELD, REACTIVE_STAGED_READ, CONFIG_STAGED, CONFIG_STAGED_ERROR, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE, NewSet, REACTIVE_DIRTY, NOT_PENDING, REACTIVE_DISPOSED, CONFIG_REDERIVE, CONFIG_VERDICT, CONFIG_OVERRIDE } from "./constants.js";
|
|
2
2
|
|
|
3
|
-
import { ext, statusNotifierOf,
|
|
3
|
+
import { ext, statusNotifierOf, context, untrack, setSignal } from "./core.js";
|
|
4
4
|
|
|
5
5
|
import { StatusError, NotReadyError } from "./error.js";
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ import { unobserved, trimStaleDeps } from "./graph.js";
|
|
|
8
8
|
|
|
9
9
|
import { enqueueSub } from "./heap.js";
|
|
10
10
|
|
|
11
|
-
import { cleanup } from "./owner.js";
|
|
11
|
+
import { disposeChildren, cleanup } from "./owner.js";
|
|
12
12
|
|
|
13
13
|
import { passLane, joinFuture, question, MAINLINE_QUESTION, globalQueue, schedule, clock, flush, freshHook, txOf, laneStageHook, queuePendingNode } from "./scheduler.js";
|
|
14
14
|
|
|
@@ -17,16 +17,16 @@ import { passLane, joinFuture, question, MAINLINE_QUESTION, globalQueue, schedul
|
|
|
17
17
|
// created dual state whose migration invariant was easy to break: a third
|
|
18
18
|
// overlapping source landed beside the Set and removePendingSource refused
|
|
19
19
|
// to clear it, stranding the Set members' pending forever (#2893).
|
|
20
|
-
function addPendingSource(e,
|
|
21
|
-
if (e.T?.fe?.has(
|
|
22
|
-
(ext(e).fe ??= new
|
|
20
|
+
function addPendingSource(e, n) {
|
|
21
|
+
if (e.T?.fe?.has(n)) return false;
|
|
22
|
+
(ext(e).fe ??= new NewSet).add(n);
|
|
23
23
|
return true;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
function removePendingSource(e,
|
|
27
|
-
const
|
|
28
|
-
if (!
|
|
29
|
-
if (!
|
|
26
|
+
function removePendingSource(e, n) {
|
|
27
|
+
const t = e.T?.fe;
|
|
28
|
+
if (!t?.delete(n)) return false;
|
|
29
|
+
if (!t.size) e.T.fe = undefined;
|
|
30
30
|
return true;
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -41,10 +41,10 @@ function clearPendingSources(e) {
|
|
|
41
41
|
// edge: a dep that IS the source, or one whose own pending chain carries it
|
|
42
42
|
// (pending sources propagate the origin node, so this covers any depth).
|
|
43
43
|
// Also guards branch-local recovery: another dependency may still need the source.
|
|
44
|
-
function retryReaches(e,
|
|
45
|
-
for (let
|
|
46
|
-
const e =
|
|
47
|
-
if (e ===
|
|
44
|
+
function retryReaches(e, n) {
|
|
45
|
+
for (let t = e.C; t; t = t.G) {
|
|
46
|
+
const e = t.S;
|
|
47
|
+
if (e === n || e.T?.fe?.has(n)) return true;
|
|
48
48
|
}
|
|
49
49
|
return false;
|
|
50
50
|
}
|
|
@@ -55,33 +55,33 @@ function retryReaches(e, t) {
|
|
|
55
55
|
* settlePendingSource walk runs off `_pendingSources` + `_blocked` alone —
|
|
56
56
|
* with NO read-visible pending status and no downstream propagation. Commit
|
|
57
57
|
* #0 keeps serving.
|
|
58
|
-
*/ function parkLoadingWindow(e,
|
|
59
|
-
ext(e).
|
|
60
|
-
if (
|
|
58
|
+
*/ function parkLoadingWindow(e, n) {
|
|
59
|
+
ext(e).se = true;
|
|
60
|
+
if (n.source) addPendingSource(e, n.source);
|
|
61
61
|
// A settled error is the node's answer ("the error stays the answer until
|
|
62
62
|
// this retry can actually run") — the park must not replace it: reads
|
|
63
63
|
// throw `_error` while STATUS_ERROR is set, and overwriting it here leaks
|
|
64
64
|
// a pending-class NotReadyError from a read-invisible park (#2989).
|
|
65
|
-
if (!(e.m & STATUS_ERROR)) setPendingError(e,
|
|
65
|
+
if (!(e.m & STATUS_ERROR)) setPendingError(e, n.source, n);
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
function setPendingError(e,
|
|
69
|
-
if (!
|
|
68
|
+
function setPendingError(e, n, t) {
|
|
69
|
+
if (!n) {
|
|
70
70
|
if (e.T !== null) e.T.F = null;
|
|
71
71
|
return;
|
|
72
72
|
}
|
|
73
|
-
if (
|
|
74
|
-
ext(e).F =
|
|
73
|
+
if (t instanceof NotReadyError && t.source === n) {
|
|
74
|
+
ext(e).F = t;
|
|
75
75
|
return;
|
|
76
76
|
}
|
|
77
77
|
const r = e.T?.F;
|
|
78
|
-
if (!(r instanceof NotReadyError) || r.source !==
|
|
79
|
-
ext(e).F = new NotReadyError(
|
|
78
|
+
if (!(r instanceof NotReadyError) || r.source !== n) {
|
|
79
|
+
ext(e).F = new NotReadyError(n);
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
function forEachDependent(e,
|
|
84
|
-
for (let
|
|
83
|
+
function forEachDependent(e, n) {
|
|
84
|
+
for (let t = e.o; t !== null; t = t.t) n(t.u, t);
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
// Queue a node to re-run on the next flush (used both when a pending source
|
|
@@ -103,16 +103,16 @@ function releaseIfSettledUnobserved(e) {
|
|
|
103
103
|
// the propagation completes, walk the same graph for stranded lazy nodes.
|
|
104
104
|
// Collect-then-release so unobserved() never unlinks under the walk.
|
|
105
105
|
function releaseSettledDependents(e) {
|
|
106
|
-
let
|
|
107
|
-
const
|
|
106
|
+
let n;
|
|
107
|
+
const t = new NewSet;
|
|
108
108
|
const visit = e => {
|
|
109
|
-
if (
|
|
110
|
-
|
|
111
|
-
if (!e.o && e.l & CONFIG_AUTO_DISPOSE) (
|
|
109
|
+
if (t.has(e)) return;
|
|
110
|
+
t.add(e);
|
|
111
|
+
if (!e.o && e.l & CONFIG_AUTO_DISPOSE) (n ??= []).push(e);
|
|
112
112
|
forEachDependent(e, visit);
|
|
113
113
|
};
|
|
114
114
|
forEachDependent(e, visit);
|
|
115
|
-
if (
|
|
115
|
+
if (n) for (const e of n) releaseIfSettledUnobserved(e);
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
// Error-dimension twin of settlePendingSource's blocked re-enqueue (#2949):
|
|
@@ -137,20 +137,22 @@ function releaseSettledDependents(e) {
|
|
|
137
137
|
* it through the ordinary error path (`commitPendingNode` → `notifyStatus`),
|
|
138
138
|
* dependents included. Not for a lane pass, nor a node with no shown value
|
|
139
139
|
* (born held — its error has no frame to wait behind): those publish now,
|
|
140
|
-
* as before. Returns whether the error was staged. */ function stageError(e,
|
|
140
|
+
* as before. Returns whether the error was staged. */ function stageError(e, n, t) {
|
|
141
141
|
// Held, or — for a pass's own throw — derived from a staging of a flush
|
|
142
142
|
// (REACTIVE_STAGED_READ is this pass's; a landing after the pass cannot
|
|
143
143
|
// read it, the flush it read a staging of has committed or parked since).
|
|
144
144
|
// Staged whether or not the flush has joined a hold yet: a pass that runs
|
|
145
145
|
// before its async sibling joins is the seam's to decide, like a value
|
|
146
146
|
// (#4109). A flush that parks nothing publishes it at its commit (S-38).
|
|
147
|
-
|
|
147
|
+
// Not a projection's derive: its readers learn its error through the
|
|
148
|
+
// family, which the commit's publish never reaches (#4189).
|
|
149
|
+
if (!(e.l & CONFIG_HELD || t && e.R & REACTIVE_STAGED_READ && globalQueue.q && !e.fam) || passLane) return false;
|
|
148
150
|
// The node answered (an error ends the wait — no longer pending, nor
|
|
149
151
|
// blocked); a shown error stays its display until the commit.
|
|
150
152
|
clearPendingSources(e);
|
|
151
|
-
if (e.T) e.T.
|
|
153
|
+
if (e.T) e.T.se = false;
|
|
152
154
|
if (!((e.m &= STATUS_ERROR | STATUS_UNINITIALIZED) & STATUS_ERROR)) setPendingError(e);
|
|
153
|
-
e.
|
|
155
|
+
e.ue = n instanceof StatusError ? n : new StatusError(e, n);
|
|
154
156
|
e.l |= CONFIG_STAGED | CONFIG_STAGED_ERROR;
|
|
155
157
|
// Readers parked on the flight re-run and derive from the staging; the
|
|
156
158
|
// verdict readers the walk wakes need a flush (#4106).
|
|
@@ -159,34 +161,34 @@ function releaseSettledDependents(e) {
|
|
|
159
161
|
return true;
|
|
160
162
|
}
|
|
161
163
|
|
|
162
|
-
function settleErroredDependents(e,
|
|
163
|
-
let
|
|
164
|
-
const r = new
|
|
164
|
+
function settleErroredDependents(e, n) {
|
|
165
|
+
let t = false;
|
|
166
|
+
const r = new NewSet;
|
|
165
167
|
const visit = e => {
|
|
166
168
|
if (r.has(e)) return;
|
|
167
169
|
r.add(e);
|
|
168
|
-
if (e.T?.F ===
|
|
170
|
+
if (e.T?.F === n) {
|
|
169
171
|
enqueueSub(e);
|
|
170
|
-
|
|
172
|
+
t = true;
|
|
171
173
|
}
|
|
172
174
|
forEachDependent(e, visit);
|
|
173
175
|
};
|
|
174
176
|
forEachDependent(e, visit);
|
|
175
|
-
if (
|
|
177
|
+
if (t) schedule();
|
|
176
178
|
}
|
|
177
179
|
|
|
178
180
|
// Retire `source` from pending state along the dependent graph rooted at `el`.
|
|
179
181
|
// By default, `el` is the source whose flight settled or was superseded.
|
|
180
182
|
// With a distinct `source`, `el` is a recovered computation that dropped it:
|
|
181
183
|
// the source may still be pending, so dependents with another path to it stay pending.
|
|
182
|
-
function settlePendingSource(e,
|
|
184
|
+
function settlePendingSource(e, n = e) {
|
|
183
185
|
// Landing and branch recovery already cleared el's own set. Superseded
|
|
184
186
|
// re-parks can retain an abandoned self entry (source === el), which must
|
|
185
187
|
// retire in the same walk as its propagated copies.
|
|
186
|
-
removePendingSource(e,
|
|
187
|
-
let
|
|
188
|
+
removePendingSource(e, n);
|
|
189
|
+
let t = false;
|
|
188
190
|
let r;
|
|
189
|
-
const o = new
|
|
191
|
+
const o = new NewSet;
|
|
190
192
|
const settle = f => {
|
|
191
193
|
if (o.has(f) ||
|
|
192
194
|
// A conditional dropped this source, but another dependency can still
|
|
@@ -195,7 +197,7 @@ function settlePendingSource(e, t = e) {
|
|
|
195
197
|
// carrying dependency may itself be a later branch of this same walk
|
|
196
198
|
// (two unchanged memos converging), and its visit must be free to
|
|
197
199
|
// re-examine this node once that branch has retired the source.
|
|
198
|
-
|
|
200
|
+
n !== e && retryReaches(f, n) || !removePendingSource(f, n))
|
|
199
201
|
// A19: a verdict reader holds no pending of its own (it was re-derived
|
|
200
202
|
// when the source went pending, `propagateStatus`), and a landing
|
|
201
203
|
// equal to the committed value notifies nobody — the source settling
|
|
@@ -203,28 +205,28 @@ function settlePendingSource(e, t = e) {
|
|
|
203
205
|
return f.l & CONFIG_VERDICT && enqueueSub(f);
|
|
204
206
|
o.add(f);
|
|
205
207
|
f.ie = clock;
|
|
206
|
-
const
|
|
208
|
+
const s = f.T?.fe?.values().next().value;
|
|
207
209
|
// STATUS_ERROR + pending sources only coexist via an errored loading
|
|
208
210
|
// window's park (notifyStatus(STATUS_ERROR) clears pending sources
|
|
209
211
|
// otherwise): the settled error stays the answer through the settle —
|
|
210
212
|
// nulling it here would have reads throw `null` until the re-enqueued
|
|
211
213
|
// retry lands, or lose it entirely if that retry parks again (#2989).
|
|
212
|
-
const
|
|
213
|
-
if (
|
|
214
|
-
if (!
|
|
214
|
+
const u = f.m & STATUS_ERROR;
|
|
215
|
+
if (s) {
|
|
216
|
+
if (!u) setPendingError(f, s);
|
|
215
217
|
} else {
|
|
216
218
|
f.m &= ~STATUS_PENDING;
|
|
217
|
-
if (!
|
|
219
|
+
if (!u) setPendingError(f);
|
|
218
220
|
// L2: a landing resumes the transaction waiting on it (maintainer: "it
|
|
219
221
|
// resumes the sync frame") — a held reader's re-pass over the landed
|
|
220
222
|
// value is that transaction's work, and the flush that carries it
|
|
221
223
|
// parks into it.
|
|
222
224
|
if (f.l & CONFIG_HELD) joinFuture(txOf(f));
|
|
223
|
-
if (f.T?.
|
|
225
|
+
if (f.T?.se) {
|
|
224
226
|
enqueueSub(f);
|
|
225
|
-
|
|
227
|
+
t = true;
|
|
226
228
|
}
|
|
227
|
-
if (f.T !== null) f.T.
|
|
229
|
+
if (f.T !== null) f.T.se = false;
|
|
228
230
|
// Fully settled with nobody watching: release candidate (#2934). Checked
|
|
229
231
|
// again at release time — deferred so unobserved() can't unlink subs
|
|
230
232
|
// lists this walk is still iterating.
|
|
@@ -236,7 +238,7 @@ function settlePendingSource(e, t = e) {
|
|
|
236
238
|
// Release before the flush schedule below: unobserved() pulls the node back
|
|
237
239
|
// out of the heap, so the enqueueSub above never recomputes a released node.
|
|
238
240
|
if (r) for (const e of r) releaseIfSettledUnobserved(e);
|
|
239
|
-
if (
|
|
241
|
+
if (t) schedule();
|
|
240
242
|
}
|
|
241
243
|
|
|
242
244
|
// Object-thenable detection (Promises/A+ shape).
|
|
@@ -245,14 +247,42 @@ function isThenable(e) {
|
|
|
245
247
|
}
|
|
246
248
|
|
|
247
249
|
/** Fire and clear a node's iterator-flight cancellation hook (#3122). */ function releaseFlightTeardown(e) {
|
|
248
|
-
const
|
|
249
|
-
if (
|
|
250
|
+
const n = e.T?.ae;
|
|
251
|
+
if (n != null) {
|
|
250
252
|
e.T.ae = null;
|
|
251
|
-
|
|
253
|
+
n();
|
|
252
254
|
}
|
|
253
255
|
}
|
|
254
256
|
|
|
255
|
-
|
|
257
|
+
/**
|
|
258
|
+
* Internal (not a package export): run `fn` when the current pass's flight
|
|
259
|
+
* is superseded, at the same instant as the iterator close above, instead of
|
|
260
|
+
* when the replacing pass commits (owner cleanups wait for that). Chains onto
|
|
261
|
+
* the node's teardown slot; fires once with it. A no-op unless the ambient
|
|
262
|
+
* owner is a computed in its own pass. `consumeIterator` fills the slot
|
|
263
|
+
* before its first pull, so a source opened in an async generator's body
|
|
264
|
+
* (`for await`) composes after the generator's close.
|
|
265
|
+
*/ function onSupersede(e) {
|
|
266
|
+
const n = context;
|
|
267
|
+
if (n === null || n.X === undefined || !(n.R & REACTIVE_RECOMPUTING_DEPS)) return;
|
|
268
|
+
const t = n.T === null;
|
|
269
|
+
const r = ext(n);
|
|
270
|
+
// Allocating the extension mid-run turns `_x?._inFlight` from undefined into
|
|
271
|
+
// null, which recompute would read as a flight this body registered itself.
|
|
272
|
+
// Restore undefined here, so the fix costs nothing for code that never
|
|
273
|
+
// calls onSupersede.
|
|
274
|
+
if (t) r.Se = undefined;
|
|
275
|
+
const o = r.ae;
|
|
276
|
+
r.ae = o === null ? e : () => {
|
|
277
|
+
try {
|
|
278
|
+
o();
|
|
279
|
+
} finally {
|
|
280
|
+
e();
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function handleAsync(e, n, t,
|
|
256
286
|
// The flight rejected (after the node's status is set): a projection
|
|
257
287
|
// tells its leaves' readers, who are not the node's dependents
|
|
258
288
|
// (store/projection.ts). `pending`: a NotReady rejection — the node is
|
|
@@ -260,24 +290,24 @@ function handleAsync(e, t, n,
|
|
|
260
290
|
r) {
|
|
261
291
|
let o = false;
|
|
262
292
|
let f = false;
|
|
263
|
-
if (typeof
|
|
293
|
+
if (typeof n === "object" && n !== null) {
|
|
264
294
|
untrack(() => {
|
|
265
|
-
o =
|
|
266
|
-
f = !o && isThenable(
|
|
295
|
+
o = n[Symbol.asyncIterator];
|
|
296
|
+
f = !o && isThenable(n);
|
|
267
297
|
});
|
|
268
298
|
}
|
|
269
299
|
if (!f && !o) {
|
|
270
300
|
if (e.T !== null) e.T.Se = null;
|
|
271
301
|
// A sync landing is the first real answer for a loadingValue node.
|
|
272
302
|
e.ce = false;
|
|
273
|
-
return
|
|
303
|
+
return n;
|
|
274
304
|
}
|
|
275
305
|
// Flight replacement relies on recompute's supersede release for iterator
|
|
276
306
|
// teardown (#3122): every handleAsync call — including the projection
|
|
277
307
|
// self-registration — runs during a recompute of `el`, which has already
|
|
278
308
|
// fired _flightTeardown. A future non-recompute registration path must
|
|
279
309
|
// release it here before overwriting _inFlight.
|
|
280
|
-
ext(e).Se =
|
|
310
|
+
ext(e).Se = n;
|
|
281
311
|
// The question this flight answers (A18 provenance, lanes.ts): the action
|
|
282
312
|
// whose slice asked it; mainline is always the current question.
|
|
283
313
|
e.T.Ee = question || MAINLINE_QUESTION;
|
|
@@ -288,9 +318,9 @@ r) {
|
|
|
288
318
|
// The landing retires only the flight's own entry (landStatus, #3373), so
|
|
289
319
|
// an entry that survived here would hold the node past its own answer.
|
|
290
320
|
e.T.fe = undefined;
|
|
291
|
-
let
|
|
321
|
+
let s;
|
|
292
322
|
const handleError = o => {
|
|
293
|
-
if (e.T?.Se !==
|
|
323
|
+
if (e.T?.Se !== n) return;
|
|
294
324
|
// NotReadyError from rejected promises should be treated as pending, not error
|
|
295
325
|
let f = o instanceof NotReadyError;
|
|
296
326
|
if (f && e.ce) {
|
|
@@ -306,7 +336,14 @@ r) {
|
|
|
306
336
|
// D-41: a held node's error is staged (`stageError`). A derived store's
|
|
307
337
|
// readers learn its error through the family, not the derive's
|
|
308
338
|
// dependents: it publishes now.
|
|
309
|
-
if (f ||
|
|
339
|
+
if (f || t || !stageError(e, o)) {
|
|
340
|
+
notifyStatus(e, f ? STATUS_PENDING : STATUS_ERROR, o);
|
|
341
|
+
// The error is this pass's answer, published now: it replaces the shown
|
|
342
|
+
// frame as a value's commit would (`commitPendingNode`), so the frame
|
|
343
|
+
// the pass parked goes instead of waiting for a later success. A no-op
|
|
344
|
+
// when nothing is parked.
|
|
345
|
+
if (!f) disposeChildren(e, false, true);
|
|
346
|
+
}
|
|
310
347
|
// A NotReady rejection is a landing into another pending source. The
|
|
311
348
|
// rejected flight will never settle its self entry, so transfer ownership
|
|
312
349
|
// after notifyStatus has propagated the replacement source.
|
|
@@ -319,16 +356,16 @@ r) {
|
|
|
319
356
|
r?.(o, f);
|
|
320
357
|
};
|
|
321
358
|
const asyncWrite = (r, o) => {
|
|
322
|
-
if (e.T?.Se !==
|
|
359
|
+
if (e.T?.Se !== n) return;
|
|
323
360
|
// If the node was dirtied by a newer write (optimistic override or regular),
|
|
324
361
|
// skip this stale async result — the upcoming flush will recompute the node
|
|
325
362
|
// with the new value, creating a fresh Promise that supersedes this one.
|
|
326
363
|
if (e.R & REACTIVE_DIRTY) return;
|
|
327
364
|
const f = e.m & STATUS_UNINITIALIZED;
|
|
328
365
|
landStatus(e);
|
|
329
|
-
if (
|
|
366
|
+
if (t) {
|
|
330
367
|
try {
|
|
331
|
-
|
|
368
|
+
t(r);
|
|
332
369
|
} catch (e) {
|
|
333
370
|
handleError(e);
|
|
334
371
|
return;
|
|
@@ -350,7 +387,7 @@ r) {
|
|
|
350
387
|
// (`_pendingValue` set inside setSignal) is not — commitPendingNode closes
|
|
351
388
|
// the window when the flush commits it, so no one-frame pulse can leak to
|
|
352
389
|
// observers between the landing and its commit (#2990).
|
|
353
|
-
if (e.
|
|
390
|
+
if (e.ue === NOT_PENDING) {
|
|
354
391
|
e.ce = false;
|
|
355
392
|
// The landing published: the dependency tail the flight's pass left
|
|
356
393
|
// linked goes now (A30, #3410). A staged landing has not replaced the
|
|
@@ -386,14 +423,14 @@ r) {
|
|
|
386
423
|
// write through asyncWrite, and close registration targets `owner` (el
|
|
387
424
|
// itself) directly. Returns whether a sync answer landed (first yield or
|
|
388
425
|
// empty completion) — meaningful only in the live posture.
|
|
389
|
-
const consumeIterator = (
|
|
390
|
-
const o =
|
|
426
|
+
const consumeIterator = (t, r) => {
|
|
427
|
+
const o = t[Symbol.asyncIterator]();
|
|
391
428
|
let f = false;
|
|
392
|
-
let
|
|
429
|
+
let u = false;
|
|
393
430
|
let i = !r;
|
|
394
431
|
const close = () => {
|
|
395
|
-
if (
|
|
396
|
-
|
|
432
|
+
if (u) return;
|
|
433
|
+
u = true;
|
|
397
434
|
try {
|
|
398
435
|
const e = o.return?.();
|
|
399
436
|
if (isThenable(e)) e.then(undefined, () => {});
|
|
@@ -402,7 +439,11 @@ r) {
|
|
|
402
439
|
cleanup(close, r);
|
|
403
440
|
// Flight-identity cancellation (#3122): the registration above is the
|
|
404
441
|
// owner-death backstop; the teardown slot fires at the _inFlight release
|
|
405
|
-
// sites so supersede stops this stream immediately.
|
|
442
|
+
// sites so supersede stops this stream immediately. Assigned, not
|
|
443
|
+
// chained (chaining here costs every app bytes): it replaces any
|
|
444
|
+
// `onSupersede` teardown this run registered earlier, which then falls
|
|
445
|
+
// back to the owner-cleanup channel. A returned `from()` is unaffected:
|
|
446
|
+
// `close` calls its `return()`, which runs its own teardown.
|
|
406
447
|
ext(e).ae = close;
|
|
407
448
|
// Release check before each next pull: an unobserved lazy node must tear
|
|
408
449
|
// down (its close above runs via disposal, closing the iterator) instead
|
|
@@ -418,7 +459,7 @@ r) {
|
|
|
418
459
|
}
|
|
419
460
|
};
|
|
420
461
|
const iterate = () => {
|
|
421
|
-
let
|
|
462
|
+
let t, r, l = false, a = false, S = true;
|
|
422
463
|
// Protocol tolerance, matching `for await`: `await` unwraps whatever
|
|
423
464
|
// next() returns — a thenable OR a bare IteratorResult. Real producers
|
|
424
465
|
// use the bare form as a promise-free fast path when a value is already
|
|
@@ -437,16 +478,16 @@ r) {
|
|
|
437
478
|
// silently dropped. (The deferred posture never has a caller, so
|
|
438
479
|
// initialRead starts false there and everything writes through.)
|
|
439
480
|
if (S && i) {
|
|
440
|
-
|
|
481
|
+
t = r;
|
|
441
482
|
l = true;
|
|
442
|
-
if (r.done)
|
|
443
|
-
} else if (e.T?.Se !==
|
|
483
|
+
if (r.done) u = true;
|
|
484
|
+
} else if (e.T?.Se !== n) {
|
|
444
485
|
return;
|
|
445
486
|
} else if (!r.done) {
|
|
446
487
|
f = true;
|
|
447
488
|
asyncWrite(r.value, iterateOrRelease);
|
|
448
489
|
} else {
|
|
449
|
-
|
|
490
|
+
u = true;
|
|
450
491
|
if (f) {
|
|
451
492
|
schedule();
|
|
452
493
|
flush();
|
|
@@ -456,30 +497,30 @@ r) {
|
|
|
456
497
|
}
|
|
457
498
|
settleAutodispose();
|
|
458
499
|
}
|
|
459
|
-
},
|
|
500
|
+
}, t => {
|
|
460
501
|
if (S && i) {
|
|
461
|
-
r =
|
|
502
|
+
r = t;
|
|
462
503
|
a = true;
|
|
463
|
-
} else if (e.T?.Se ===
|
|
464
|
-
|
|
465
|
-
handleError(
|
|
504
|
+
} else if (e.T?.Se === n) {
|
|
505
|
+
u = true;
|
|
506
|
+
handleError(t);
|
|
466
507
|
settleAutodispose();
|
|
467
508
|
}
|
|
468
509
|
});
|
|
469
510
|
S = false;
|
|
470
511
|
if (a) {
|
|
471
512
|
// Match the promise branch, but only rethrow during the initial read.
|
|
472
|
-
|
|
513
|
+
u = true;
|
|
473
514
|
handleError(r);
|
|
474
515
|
if (i) throw r;
|
|
475
516
|
return true;
|
|
476
517
|
}
|
|
477
|
-
if (l && !
|
|
478
|
-
|
|
518
|
+
if (l && !t.done) {
|
|
519
|
+
s = t.value;
|
|
479
520
|
f = true;
|
|
480
521
|
return iterate();
|
|
481
522
|
}
|
|
482
|
-
return l &&
|
|
523
|
+
return l && t.done;
|
|
483
524
|
};
|
|
484
525
|
const l = iterate();
|
|
485
526
|
// Later iterate() calls run from asyncWrite, where rethrowing would be unhandled.
|
|
@@ -489,31 +530,31 @@ r) {
|
|
|
489
530
|
// Landed-synchronously verdict for a LIVE iterator drain; null when no live
|
|
490
531
|
// drain ran (plain promise flight, or a deferred flatten). Drives the
|
|
491
532
|
// shared NotReady/loading tail below.
|
|
492
|
-
let
|
|
533
|
+
let u = null;
|
|
493
534
|
// Flatten one async level: a thenable that RESOLVES to an AsyncIterable —
|
|
494
535
|
// the shape every async stub returning a stream produces — consumes as the
|
|
495
536
|
// stream itself rather than settling on the iterable object. One level
|
|
496
537
|
// only: A+ `then` already collapses nested thenables, so the resolved
|
|
497
538
|
// value is never itself a thenable.
|
|
498
|
-
const flattenIfIterable = (e,
|
|
499
|
-
let
|
|
539
|
+
const flattenIfIterable = (e, n) => {
|
|
540
|
+
let t = false;
|
|
500
541
|
if (typeof e === "object" && e !== null) {
|
|
501
542
|
untrack(() => {
|
|
502
|
-
|
|
543
|
+
t = e[Symbol.asyncIterator];
|
|
503
544
|
});
|
|
504
545
|
}
|
|
505
|
-
if (!
|
|
506
|
-
|
|
507
|
-
const r = consumeIterator(e,
|
|
508
|
-
if (!
|
|
546
|
+
if (!t) return false;
|
|
547
|
+
s = undefined;
|
|
548
|
+
const r = consumeIterator(e, n);
|
|
549
|
+
if (!n) u = r;
|
|
509
550
|
return true;
|
|
510
551
|
};
|
|
511
552
|
if (f) {
|
|
512
|
-
let
|
|
513
|
-
|
|
553
|
+
let t = false, r = false, o, f = true;
|
|
554
|
+
n.then(r => {
|
|
514
555
|
if (f) {
|
|
515
|
-
|
|
516
|
-
|
|
556
|
+
s = r;
|
|
557
|
+
t = true;
|
|
517
558
|
} else {
|
|
518
559
|
// Flattening consumes the stream as the value: each yield lands
|
|
519
560
|
// through asyncWrite under this flight's identity; the first one
|
|
@@ -525,7 +566,7 @@ r) {
|
|
|
525
566
|
// compute catch above it: a protocol throw settles as this
|
|
526
567
|
// flight's error (#4112).
|
|
527
568
|
try {
|
|
528
|
-
if (e.T?.Se ===
|
|
569
|
+
if (e.T?.Se === n && !(e.R & REACTIVE_DISPOSED) && flattenIfIterable(r, e)) return;
|
|
529
570
|
asyncWrite(r);
|
|
530
571
|
} catch (e) {
|
|
531
572
|
handleError(e);
|
|
@@ -548,14 +589,14 @@ r) {
|
|
|
548
589
|
// momentarily read as `undefined`.
|
|
549
590
|
handleError(o);
|
|
550
591
|
throw o;
|
|
551
|
-
} else if (!
|
|
552
|
-
//
|
|
553
|
-
// work on a loadingValue node is
|
|
554
|
-
// boundaries
|
|
555
|
-
// lands through asyncWrite.
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
} else if (!flattenIfIterable(
|
|
592
|
+
} else if (!t) {
|
|
593
|
+
// Not landed: the shared tail below suspends, or — loading window —
|
|
594
|
+
// serves commit #0 (first-flight work on a loadingValue node is
|
|
595
|
+
// loading-class, invisible to boundaries; the flight itself is already
|
|
596
|
+
// registered in _inFlight and lands through asyncWrite). A promise is
|
|
597
|
+
// never an iterator, so nothing between here and there runs.
|
|
598
|
+
u = false;
|
|
599
|
+
} else if (!flattenIfIterable(s)) {
|
|
559
600
|
// Synchronously-resolved promise: the first real answer landed.
|
|
560
601
|
e.ce = false;
|
|
561
602
|
}
|
|
@@ -563,10 +604,10 @@ r) {
|
|
|
563
604
|
// are still inside the synchronous read): full initial-drain semantics
|
|
564
605
|
// apply and the shared tail below settles the verdict.
|
|
565
606
|
}
|
|
566
|
-
if (o) flattenIfIterable(
|
|
567
|
-
if (
|
|
568
|
-
if (!
|
|
569
|
-
// Loading window: serve commit #0
|
|
607
|
+
if (o) flattenIfIterable(n);
|
|
608
|
+
if (u !== null) {
|
|
609
|
+
if (!u) {
|
|
610
|
+
// Loading window: serve commit #0 instead of suspending.
|
|
570
611
|
if (e.ce) return e.Ie;
|
|
571
612
|
throw new NotReadyError(context);
|
|
572
613
|
}
|
|
@@ -574,16 +615,16 @@ r) {
|
|
|
574
615
|
// answer; async yields clear inside asyncWrite.
|
|
575
616
|
e.ce = false;
|
|
576
617
|
}
|
|
577
|
-
return
|
|
618
|
+
return s;
|
|
578
619
|
}
|
|
579
620
|
|
|
580
|
-
function clearStatus(e,
|
|
621
|
+
function clearStatus(e, n = false) {
|
|
581
622
|
if (e.T?.fe) clearPendingSources(e);
|
|
582
|
-
if (e.T?.
|
|
583
|
-
e.m =
|
|
623
|
+
if (e.T?.se) e.T.se = false;
|
|
624
|
+
e.m = n ? 0 : e.m & STATUS_UNINITIALIZED;
|
|
584
625
|
if (e.T?.F) setPendingError(e);
|
|
585
|
-
const
|
|
586
|
-
if (
|
|
626
|
+
const t = statusNotifierOf(e);
|
|
627
|
+
if (t) t.call(e);
|
|
587
628
|
}
|
|
588
629
|
|
|
589
630
|
/**
|
|
@@ -610,35 +651,33 @@ function clearStatus(e, t = false) {
|
|
|
610
651
|
* the first landing retires UNINITIALIZED. `_error` must move off self: a
|
|
611
652
|
* reader thrown NotReady(self) would park on a retired entry. Companions
|
|
612
653
|
* keep their verdict (pending before and after; the write re-syncs them).
|
|
613
|
-
*/ function landStatus(e,
|
|
614
|
-
const
|
|
654
|
+
*/ function landStatus(e, n = false) {
|
|
655
|
+
const t = e.T?.fe;
|
|
615
656
|
// (The full clear below drops the set whether or not self was retired first.)
|
|
616
|
-
if (
|
|
617
|
-
e.T.
|
|
618
|
-
if (
|
|
619
|
-
setPendingError(e,
|
|
620
|
-
} else clearStatus(e,
|
|
657
|
+
if (t && (t.delete(e), t.size)) {
|
|
658
|
+
e.T.se = false;
|
|
659
|
+
if (n) e.m = STATUS_PENDING;
|
|
660
|
+
setPendingError(e, t.values().next().value);
|
|
661
|
+
} else clearStatus(e, n);
|
|
621
662
|
}
|
|
622
663
|
|
|
623
|
-
function notifyStatus(e,
|
|
664
|
+
function notifyStatus(e, n, t) {
|
|
624
665
|
// Wrap regular errors to track source node
|
|
625
|
-
if (
|
|
626
|
-
const
|
|
666
|
+
if (n === STATUS_ERROR && !(t instanceof StatusError) && !(t instanceof NotReadyError)) t = new StatusError(e, t);
|
|
667
|
+
const r = n === STATUS_PENDING && t instanceof NotReadyError ? t.source : undefined;
|
|
627
668
|
// (CARVE 2: the lane assignment went with the engine.)
|
|
628
|
-
{
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
ext(e).F = n;
|
|
641
|
-
}
|
|
669
|
+
if (n === STATUS_PENDING && r) {
|
|
670
|
+
// D-41: a re-ask of a shown error keeps it the display (`_shownError`).
|
|
671
|
+
if (e.m & STATUS_ERROR) e.T.de = e.T.F;
|
|
672
|
+
addPendingSource(e, r);
|
|
673
|
+
e.m = STATUS_PENDING | e.m & STATUS_UNINITIALIZED;
|
|
674
|
+
// Preserve the current source on this propagation so readers park on
|
|
675
|
+
// the distinct pending source.
|
|
676
|
+
setPendingError(e, r, t);
|
|
677
|
+
} else {
|
|
678
|
+
clearPendingSources(e);
|
|
679
|
+
e.m = n | (n !== STATUS_ERROR ? e.m & STATUS_UNINITIALIZED : 0);
|
|
680
|
+
ext(e).F = t;
|
|
642
681
|
}
|
|
643
682
|
// Lanes: a written guess is an optimistic boundary (A17 — "the optimistic
|
|
644
683
|
// future value until we know otherwise"). Its own source in flight is its
|
|
@@ -646,28 +685,26 @@ function notifyStatus(e, t, n, r) {
|
|
|
646
685
|
// transition's to wait on — the frame it was asked in holds (A17: "a
|
|
647
686
|
// transition whose optimistic node is still pending on its own fetch is
|
|
648
687
|
// not complete"), through the node itself (`blocked`).
|
|
649
|
-
if (
|
|
688
|
+
if (n === STATUS_PENDING && e.l & CONFIG_GUESS) {
|
|
650
689
|
if (passLane === null) joinFuture(null);
|
|
651
690
|
return;
|
|
652
691
|
}
|
|
653
|
-
const
|
|
654
|
-
if (
|
|
655
|
-
|
|
656
|
-
f.call(e);
|
|
657
|
-
}
|
|
692
|
+
const o = statusNotifierOf(e);
|
|
693
|
+
if (o) {
|
|
694
|
+
o.call(e);
|
|
658
695
|
return;
|
|
659
696
|
}
|
|
660
|
-
propagateStatus(e,
|
|
697
|
+
propagateStatus(e, n, t);
|
|
661
698
|
}
|
|
662
699
|
|
|
663
700
|
/** The dependents' half of `notifyStatus`: `el`'s status reaches each
|
|
664
701
|
* subscriber as a propagated mark (or a re-derive). On its own for a store
|
|
665
702
|
* family's leaves, which are not the derive's dependents — the family
|
|
666
703
|
* propagates the derive's status from each leaf to the leaf's readers
|
|
667
|
-
* (store/projection.ts `wakeFamily`), exactly as a memo's would reach its. */ function propagateStatus(e,
|
|
668
|
-
const
|
|
669
|
-
forEachDependent(e, (
|
|
670
|
-
|
|
704
|
+
* (store/projection.ts `wakeFamily`), exactly as a memo's would reach its. */ function propagateStatus(e, n, t) {
|
|
705
|
+
const r = n === STATUS_PENDING && t instanceof NotReadyError ? t.source : undefined;
|
|
706
|
+
forEachDependent(e, (o, f) => {
|
|
707
|
+
o.ie = clock;
|
|
671
708
|
// A pending mark on a kept-tail link re-derives the subscriber instead of
|
|
672
709
|
// marking it (A30, #3494 review; fuzzer latest-1 #2141; #3519 review).
|
|
673
710
|
// Past `_depsTail` lie the committed frame's deps, kept by A30 because
|
|
@@ -687,8 +724,8 @@ function notifyStatus(e, t, n, r) {
|
|
|
687
724
|
// Inheriting the error would make a probe's reader errored. A boundary's
|
|
688
725
|
// output (CONFIG_REDERIVE, boundaries.ts) the same: content or fallback
|
|
689
726
|
// is its pass's call.
|
|
690
|
-
if (
|
|
691
|
-
enqueueSub(
|
|
727
|
+
if (o.l & (CONFIG_REDERIVE | CONFIG_VERDICT) || n === STATUS_PENDING && f.Te !== o.Ne) {
|
|
728
|
+
enqueueSub(o);
|
|
692
729
|
schedule();
|
|
693
730
|
return;
|
|
694
731
|
}
|
|
@@ -699,30 +736,28 @@ function notifyStatus(e, t, n, r) {
|
|
|
699
736
|
// frame still passes through — its pass is the frame's (#3372, compiled
|
|
700
737
|
// shape: the unmount it stages makes the observer a zombie whose say is
|
|
701
738
|
// moot for this transaction, A15 #3463).
|
|
702
|
-
if (
|
|
703
|
-
if (passLane === null && (
|
|
739
|
+
if (n === STATUS_PENDING && r && o.T?.fe?.has(r)) {
|
|
740
|
+
if (passLane === null && (o.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && globalQueue.q && !freshHook?.(o, r.ie === clock)) joinFuture(txOf(o));
|
|
704
741
|
return;
|
|
705
742
|
}
|
|
706
|
-
if (
|
|
743
|
+
if (n === STATUS_PENDING && r || n !== STATUS_PENDING && (o.T?.F !== t || o.T?.fe)) {
|
|
707
744
|
// The marked subscriber is queued for the commit sweep so its status
|
|
708
745
|
// bookkeeping (loading window, uninitialized clear) runs with the flush
|
|
709
746
|
// — with the lane, when the pass propagating is the lane's work and
|
|
710
747
|
// the dependent's pending is the lane's own flight (`laneStage`).
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
if (t === STATUS_PENDING && (r.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && !r.i) joinFuture(txOf(r));
|
|
721
|
-
}
|
|
748
|
+
// (No answer: a pending propagation leaves a written guess a guess.)
|
|
749
|
+
if (laneStageHook && passLane !== null) laneStageHook(o, passLane, true, e); else {
|
|
750
|
+
queuePendingNode(o);
|
|
751
|
+
// A15 (#3443): pending propagates onto a held memo without
|
|
752
|
+
// recomputing it, and the propagation itself enters the memo's
|
|
753
|
+
// transaction — the flight flows into a memo that transaction
|
|
754
|
+
// holds, so the write that started it is held with it. A render
|
|
755
|
+
// effect's membership is its pass's (`notify`), never sticky.
|
|
756
|
+
if (n === STATUS_PENDING && (o.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && !o.i) joinFuture(txOf(o));
|
|
722
757
|
}
|
|
723
|
-
notifyStatus(
|
|
758
|
+
notifyStatus(o, n, t);
|
|
724
759
|
}
|
|
725
760
|
});
|
|
726
761
|
}
|
|
727
762
|
|
|
728
|
-
export { addPendingSource, clearStatus, forEachDependent, handleAsync, isThenable, notifyStatus, parkLoadingWindow, propagateStatus, releaseFlightTeardown, releaseIfSettledUnobserved, releaseSettledDependents, setPendingError, settleErroredDependents, settlePendingSource, stageError };
|
|
763
|
+
export { addPendingSource, clearStatus, forEachDependent, handleAsync, isThenable, notifyStatus, onSupersede, parkLoadingWindow, propagateStatus, releaseFlightTeardown, releaseIfSettledUnobserved, releaseSettledDependents, setPendingError, settleErroredDependents, settlePendingSource, stageError };
|