@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
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_NO_VALUE, 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, CONFIG_INPUTS_PUBLISHED, 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 (!
|
|
70
|
-
if (e.T !== null) e.T.
|
|
68
|
+
function setPendingError(e, n, t) {
|
|
69
|
+
if (!n) {
|
|
70
|
+
if (e.T !== null) e.T.U = null;
|
|
71
71
|
return;
|
|
72
72
|
}
|
|
73
|
-
if (
|
|
74
|
-
ext(e).
|
|
73
|
+
if (t instanceof NotReadyError && t.source === n) {
|
|
74
|
+
ext(e).U = t;
|
|
75
75
|
return;
|
|
76
76
|
}
|
|
77
|
-
const r = e.T?.
|
|
78
|
-
if (!(r instanceof NotReadyError) || r.source !==
|
|
79
|
-
ext(e).
|
|
77
|
+
const r = e.T?.U;
|
|
78
|
+
if (!(r instanceof NotReadyError) || r.source !== n) {
|
|
79
|
+
ext(e).U = 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,7 +137,7 @@ 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).
|
|
@@ -146,13 +146,13 @@ function releaseSettledDependents(e) {
|
|
|
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
148
|
// family, which the commit's publish never reaches (#4189).
|
|
149
|
-
if (!(e.l & CONFIG_HELD ||
|
|
149
|
+
if (!(e.l & CONFIG_HELD || t && e.R & REACTIVE_STAGED_READ && globalQueue.q && !e.fam) || passLane) return false;
|
|
150
150
|
// The node answered (an error ends the wait — no longer pending, nor
|
|
151
151
|
// blocked); a shown error stays its display until the commit.
|
|
152
152
|
clearPendingSources(e);
|
|
153
|
-
if (e.T) e.T.
|
|
153
|
+
if (e.T) e.T.se = false;
|
|
154
154
|
if (!((e.m &= STATUS_ERROR | STATUS_UNINITIALIZED) & STATUS_ERROR)) setPendingError(e);
|
|
155
|
-
e.
|
|
155
|
+
e.ie = n instanceof StatusError ? n : new StatusError(e, n);
|
|
156
156
|
e.l |= CONFIG_STAGED | CONFIG_STAGED_ERROR;
|
|
157
157
|
// Readers parked on the flight re-run and derive from the staging; the
|
|
158
158
|
// verdict readers the walk wakes need a flush (#4106).
|
|
@@ -161,34 +161,34 @@ function releaseSettledDependents(e) {
|
|
|
161
161
|
return true;
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
-
function settleErroredDependents(e,
|
|
165
|
-
let
|
|
166
|
-
const r = new
|
|
164
|
+
function settleErroredDependents(e, n) {
|
|
165
|
+
let t = false;
|
|
166
|
+
const r = new NewSet;
|
|
167
167
|
const visit = e => {
|
|
168
168
|
if (r.has(e)) return;
|
|
169
169
|
r.add(e);
|
|
170
|
-
if (e.T?.
|
|
170
|
+
if (e.T?.U === n) {
|
|
171
171
|
enqueueSub(e);
|
|
172
|
-
|
|
172
|
+
t = true;
|
|
173
173
|
}
|
|
174
174
|
forEachDependent(e, visit);
|
|
175
175
|
};
|
|
176
176
|
forEachDependent(e, visit);
|
|
177
|
-
if (
|
|
177
|
+
if (t) schedule();
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
// Retire `source` from pending state along the dependent graph rooted at `el`.
|
|
181
181
|
// By default, `el` is the source whose flight settled or was superseded.
|
|
182
182
|
// With a distinct `source`, `el` is a recovered computation that dropped it:
|
|
183
183
|
// the source may still be pending, so dependents with another path to it stay pending.
|
|
184
|
-
function settlePendingSource(e,
|
|
184
|
+
function settlePendingSource(e, n = e) {
|
|
185
185
|
// Landing and branch recovery already cleared el's own set. Superseded
|
|
186
186
|
// re-parks can retain an abandoned self entry (source === el), which must
|
|
187
187
|
// retire in the same walk as its propagated copies.
|
|
188
|
-
removePendingSource(e,
|
|
189
|
-
let
|
|
188
|
+
removePendingSource(e, n);
|
|
189
|
+
let t = false;
|
|
190
190
|
let r;
|
|
191
|
-
const o = new
|
|
191
|
+
const o = new NewSet;
|
|
192
192
|
const settle = f => {
|
|
193
193
|
if (o.has(f) ||
|
|
194
194
|
// A conditional dropped this source, but another dependency can still
|
|
@@ -197,36 +197,36 @@ function settlePendingSource(e, t = e) {
|
|
|
197
197
|
// carrying dependency may itself be a later branch of this same walk
|
|
198
198
|
// (two unchanged memos converging), and its visit must be free to
|
|
199
199
|
// re-examine this node once that branch has retired the source.
|
|
200
|
-
|
|
200
|
+
n !== e && retryReaches(f, n) || !removePendingSource(f, n))
|
|
201
201
|
// A19: a verdict reader holds no pending of its own (it was re-derived
|
|
202
202
|
// when the source went pending, `propagateStatus`), and a landing
|
|
203
203
|
// equal to the committed value notifies nobody — the source settling
|
|
204
204
|
// is its verdict changing: it runs again.
|
|
205
205
|
return f.l & CONFIG_VERDICT && enqueueSub(f);
|
|
206
206
|
o.add(f);
|
|
207
|
-
f.
|
|
208
|
-
const
|
|
207
|
+
f.Se = clock;
|
|
208
|
+
const s = f.T?.fe?.values().next().value;
|
|
209
209
|
// STATUS_ERROR + pending sources only coexist via an errored loading
|
|
210
210
|
// window's park (notifyStatus(STATUS_ERROR) clears pending sources
|
|
211
211
|
// otherwise): the settled error stays the answer through the settle —
|
|
212
212
|
// nulling it here would have reads throw `null` until the re-enqueued
|
|
213
213
|
// retry lands, or lose it entirely if that retry parks again (#2989).
|
|
214
|
-
const
|
|
215
|
-
if (
|
|
216
|
-
if (!
|
|
214
|
+
const u = f.m & STATUS_ERROR;
|
|
215
|
+
if (s) {
|
|
216
|
+
if (!u) setPendingError(f, s);
|
|
217
217
|
} else {
|
|
218
218
|
f.m &= ~STATUS_PENDING;
|
|
219
|
-
if (!
|
|
219
|
+
if (!u) setPendingError(f);
|
|
220
220
|
// L2: a landing resumes the transaction waiting on it (maintainer: "it
|
|
221
221
|
// resumes the sync frame") — a held reader's re-pass over the landed
|
|
222
222
|
// value is that transaction's work, and the flush that carries it
|
|
223
223
|
// parks into it.
|
|
224
224
|
if (f.l & CONFIG_HELD) joinFuture(txOf(f));
|
|
225
|
-
if (f.T?.
|
|
225
|
+
if (f.T?.se) {
|
|
226
226
|
enqueueSub(f);
|
|
227
|
-
|
|
227
|
+
t = true;
|
|
228
228
|
}
|
|
229
|
-
if (f.T !== null) f.T.
|
|
229
|
+
if (f.T !== null) f.T.se = false;
|
|
230
230
|
// Fully settled with nobody watching: release candidate (#2934). Checked
|
|
231
231
|
// again at release time — deferred so unobserved() can't unlink subs
|
|
232
232
|
// lists this walk is still iterating.
|
|
@@ -238,7 +238,7 @@ function settlePendingSource(e, t = e) {
|
|
|
238
238
|
// Release before the flush schedule below: unobserved() pulls the node back
|
|
239
239
|
// out of the heap, so the enqueueSub above never recomputes a released node.
|
|
240
240
|
if (r) for (const e of r) releaseIfSettledUnobserved(e);
|
|
241
|
-
if (
|
|
241
|
+
if (t) schedule();
|
|
242
242
|
}
|
|
243
243
|
|
|
244
244
|
// Object-thenable detection (Promises/A+ shape).
|
|
@@ -247,14 +247,42 @@ function isThenable(e) {
|
|
|
247
247
|
}
|
|
248
248
|
|
|
249
249
|
/** Fire and clear a node's iterator-flight cancellation hook (#3122). */ function releaseFlightTeardown(e) {
|
|
250
|
-
const
|
|
251
|
-
if (
|
|
250
|
+
const n = e.T?.ae;
|
|
251
|
+
if (n != null) {
|
|
252
252
|
e.T.ae = null;
|
|
253
|
-
|
|
253
|
+
n();
|
|
254
254
|
}
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
-
|
|
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.Ee = 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,
|
|
258
286
|
// The flight rejected (after the node's status is set): a projection
|
|
259
287
|
// tells its leaves' readers, who are not the node's dependents
|
|
260
288
|
// (store/projection.ts). `pending`: a NotReady rejection — the node is
|
|
@@ -262,27 +290,27 @@ function handleAsync(e, t, n,
|
|
|
262
290
|
r) {
|
|
263
291
|
let o = false;
|
|
264
292
|
let f = false;
|
|
265
|
-
if (typeof
|
|
293
|
+
if (typeof n === "object" && n !== null) {
|
|
266
294
|
untrack(() => {
|
|
267
|
-
o =
|
|
268
|
-
f = !o && isThenable(
|
|
295
|
+
o = n[Symbol.asyncIterator];
|
|
296
|
+
f = !o && isThenable(n);
|
|
269
297
|
});
|
|
270
298
|
}
|
|
271
299
|
if (!f && !o) {
|
|
272
|
-
if (e.T !== null) e.T.
|
|
300
|
+
if (e.T !== null) e.T.Ee = null;
|
|
273
301
|
// A sync landing is the first real answer for a loadingValue node.
|
|
274
302
|
e.ce = false;
|
|
275
|
-
return
|
|
303
|
+
return n;
|
|
276
304
|
}
|
|
277
305
|
// Flight replacement relies on recompute's supersede release for iterator
|
|
278
306
|
// teardown (#3122): every handleAsync call — including the projection
|
|
279
307
|
// self-registration — runs during a recompute of `el`, which has already
|
|
280
308
|
// fired _flightTeardown. A future non-recompute registration path must
|
|
281
309
|
// release it here before overwriting _inFlight.
|
|
282
|
-
ext(e).
|
|
310
|
+
ext(e).Ee = n;
|
|
283
311
|
// The question this flight answers (A18 provenance, lanes.ts): the action
|
|
284
312
|
// whose slice asked it; mainline is always the current question.
|
|
285
|
-
e.T.
|
|
313
|
+
e.T.Te = question || MAINLINE_QUESTION;
|
|
286
314
|
// The run that asked this flight read every input without throwing: an
|
|
287
315
|
// input still in flight was masked for it (an active override, A17), so
|
|
288
316
|
// pending state those inputs propagated onto the node earlier does not
|
|
@@ -290,9 +318,9 @@ r) {
|
|
|
290
318
|
// The landing retires only the flight's own entry (landStatus, #3373), so
|
|
291
319
|
// an entry that survived here would hold the node past its own answer.
|
|
292
320
|
e.T.fe = undefined;
|
|
293
|
-
let
|
|
321
|
+
let s;
|
|
294
322
|
const handleError = o => {
|
|
295
|
-
if (e.T?.
|
|
323
|
+
if (e.T?.Ee !== n) return;
|
|
296
324
|
// NotReadyError from rejected promises should be treated as pending, not error
|
|
297
325
|
let f = o instanceof NotReadyError;
|
|
298
326
|
if (f && e.ce) {
|
|
@@ -300,20 +328,27 @@ r) {
|
|
|
300
328
|
// serving commit #0 — same parking as recompute's catch for sync
|
|
301
329
|
// dependency throws. The dead flight is released so the clock-gated
|
|
302
330
|
// error-retry pull (updateIfNecessary) can also re-ask.
|
|
303
|
-
if (e.T !== null) e.T.
|
|
331
|
+
if (e.T !== null) e.T.Ee = null;
|
|
304
332
|
parkLoadingWindow(e, o);
|
|
305
|
-
e.
|
|
333
|
+
e.Se = clock;
|
|
306
334
|
return;
|
|
307
335
|
}
|
|
308
336
|
// D-41: a held node's error is staged (`stageError`). A derived store's
|
|
309
337
|
// readers learn its error through the family, not the derive's
|
|
310
338
|
// dependents: it publishes now.
|
|
311
|
-
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
|
+
}
|
|
312
347
|
// A NotReady rejection is a landing into another pending source. The
|
|
313
348
|
// rejected flight will never settle its self entry, so transfer ownership
|
|
314
349
|
// after notifyStatus has propagated the replacement source.
|
|
315
350
|
if (f) settlePendingSource(e);
|
|
316
|
-
e.
|
|
351
|
+
e.Se = clock;
|
|
317
352
|
// A real error settles derivatively-pending dependents (notifyStatus
|
|
318
353
|
// cleared their pending sources), so stranded lazy ones release here —
|
|
319
354
|
// the error twin of settlePendingSource's release (#2934).
|
|
@@ -321,23 +356,29 @@ r) {
|
|
|
321
356
|
r?.(o, f);
|
|
322
357
|
};
|
|
323
358
|
const asyncWrite = (r, o) => {
|
|
324
|
-
if (e.T?.
|
|
359
|
+
if (e.T?.Ee !== n) return;
|
|
325
360
|
// If the node was dirtied by a newer write (optimistic override or regular),
|
|
326
361
|
// skip this stale async result — the upcoming flush will recompute the node
|
|
327
362
|
// with the new value, creating a fresh Promise that supersedes this one.
|
|
328
363
|
if (e.R & REACTIVE_DIRTY) return;
|
|
329
364
|
const f = e.m & STATUS_UNINITIALIZED;
|
|
330
365
|
landStatus(e);
|
|
331
|
-
if (
|
|
366
|
+
if (t) {
|
|
332
367
|
try {
|
|
333
|
-
|
|
368
|
+
t(r);
|
|
334
369
|
} catch (e) {
|
|
335
370
|
handleError(e);
|
|
336
371
|
return;
|
|
337
372
|
}
|
|
338
373
|
// A store derive's landing commits through its setter: unheld, the
|
|
339
|
-
// shown error leaves now
|
|
340
|
-
|
|
374
|
+
// shown error leaves now, and so does a commit-beneath-a-flight's
|
|
375
|
+
// CONFIG_INPUTS_PUBLISHED (its first flight's, #4269) — left standing,
|
|
376
|
+
// a verdict reader of the next flight would suspend instead of being
|
|
377
|
+
// served the screen; held, both at the derive's commit.
|
|
378
|
+
if (!(e.l & CONFIG_HELD)) {
|
|
379
|
+
e.T.Ie = undefined;
|
|
380
|
+
e.l &= ~CONFIG_INPUTS_PUBLISHED;
|
|
381
|
+
}
|
|
341
382
|
if (f) landStatus(e, true);
|
|
342
383
|
} else {
|
|
343
384
|
// CARVE 2: the override-covered landing (hold + A18 supersession) and the
|
|
@@ -352,7 +393,7 @@ r) {
|
|
|
352
393
|
// (`_pendingValue` set inside setSignal) is not — commitPendingNode closes
|
|
353
394
|
// the window when the flush commits it, so no one-frame pulse can leak to
|
|
354
395
|
// observers between the landing and its commit (#2990).
|
|
355
|
-
if (e.
|
|
396
|
+
if (e.ie === NOT_PENDING) {
|
|
356
397
|
e.ce = false;
|
|
357
398
|
// The landing published: the dependency tail the flight's pass left
|
|
358
399
|
// linked goes now (A30, #3410). A staged landing has not replaced the
|
|
@@ -388,14 +429,14 @@ r) {
|
|
|
388
429
|
// write through asyncWrite, and close registration targets `owner` (el
|
|
389
430
|
// itself) directly. Returns whether a sync answer landed (first yield or
|
|
390
431
|
// empty completion) — meaningful only in the live posture.
|
|
391
|
-
const consumeIterator = (
|
|
392
|
-
const o =
|
|
432
|
+
const consumeIterator = (t, r) => {
|
|
433
|
+
const o = t[Symbol.asyncIterator]();
|
|
393
434
|
let f = false;
|
|
394
|
-
let
|
|
435
|
+
let u = false;
|
|
395
436
|
let i = !r;
|
|
396
437
|
const close = () => {
|
|
397
|
-
if (
|
|
398
|
-
|
|
438
|
+
if (u) return;
|
|
439
|
+
u = true;
|
|
399
440
|
try {
|
|
400
441
|
const e = o.return?.();
|
|
401
442
|
if (isThenable(e)) e.then(undefined, () => {});
|
|
@@ -404,7 +445,11 @@ r) {
|
|
|
404
445
|
cleanup(close, r);
|
|
405
446
|
// Flight-identity cancellation (#3122): the registration above is the
|
|
406
447
|
// owner-death backstop; the teardown slot fires at the _inFlight release
|
|
407
|
-
// sites so supersede stops this stream immediately.
|
|
448
|
+
// sites so supersede stops this stream immediately. Assigned, not
|
|
449
|
+
// chained (chaining here costs every app bytes): it replaces any
|
|
450
|
+
// `onSupersede` teardown this run registered earlier, which then falls
|
|
451
|
+
// back to the owner-cleanup channel. A returned `from()` is unaffected:
|
|
452
|
+
// `close` calls its `return()`, which runs its own teardown.
|
|
408
453
|
ext(e).ae = close;
|
|
409
454
|
// Release check before each next pull: an unobserved lazy node must tear
|
|
410
455
|
// down (its close above runs via disposal, closing the iterator) instead
|
|
@@ -420,17 +465,17 @@ r) {
|
|
|
420
465
|
}
|
|
421
466
|
};
|
|
422
467
|
const iterate = () => {
|
|
423
|
-
let
|
|
468
|
+
let t, r, l = false, S = false, a = true;
|
|
424
469
|
// Protocol tolerance, matching `for await`: `await` unwraps whatever
|
|
425
470
|
// next() returns — a thenable OR a bare IteratorResult. Real producers
|
|
426
471
|
// use the bare form as a promise-free fast path when a value is already
|
|
427
472
|
// buffered (seroval's deserialized streams do), so a bare result is
|
|
428
473
|
// assimilated as an already-settled step instead of crashing on `.then`.
|
|
429
|
-
const
|
|
430
|
-
const
|
|
431
|
-
then: e => void e(
|
|
474
|
+
const E = o.next();
|
|
475
|
+
const c = isThenable(E) ? E : {
|
|
476
|
+
then: e => void e(E)
|
|
432
477
|
};
|
|
433
|
-
|
|
478
|
+
c.then(r => {
|
|
434
479
|
// The sync stash only serves the INITIAL drain (handleAsync's caller
|
|
435
480
|
// consumes syncValue / throws NotReady from it). A sync-settled step
|
|
436
481
|
// after an async gap — seroval buffering values between pulls, a
|
|
@@ -438,17 +483,17 @@ r) {
|
|
|
438
483
|
// stash: it must write through the async path or the value is
|
|
439
484
|
// silently dropped. (The deferred posture never has a caller, so
|
|
440
485
|
// initialRead starts false there and everything writes through.)
|
|
441
|
-
if (
|
|
442
|
-
|
|
486
|
+
if (a && i) {
|
|
487
|
+
t = r;
|
|
443
488
|
l = true;
|
|
444
|
-
if (r.done)
|
|
445
|
-
} else if (e.T?.
|
|
489
|
+
if (r.done) u = true;
|
|
490
|
+
} else if (e.T?.Ee !== n) {
|
|
446
491
|
return;
|
|
447
492
|
} else if (!r.done) {
|
|
448
493
|
f = true;
|
|
449
494
|
asyncWrite(r.value, iterateOrRelease);
|
|
450
495
|
} else {
|
|
451
|
-
|
|
496
|
+
u = true;
|
|
452
497
|
if (f) {
|
|
453
498
|
schedule();
|
|
454
499
|
flush();
|
|
@@ -458,30 +503,30 @@ r) {
|
|
|
458
503
|
}
|
|
459
504
|
settleAutodispose();
|
|
460
505
|
}
|
|
461
|
-
},
|
|
462
|
-
if (
|
|
463
|
-
r =
|
|
464
|
-
|
|
465
|
-
} else if (e.T?.
|
|
466
|
-
|
|
467
|
-
handleError(
|
|
506
|
+
}, t => {
|
|
507
|
+
if (a && i) {
|
|
508
|
+
r = t;
|
|
509
|
+
S = true;
|
|
510
|
+
} else if (e.T?.Ee === n) {
|
|
511
|
+
u = true;
|
|
512
|
+
handleError(t);
|
|
468
513
|
settleAutodispose();
|
|
469
514
|
}
|
|
470
515
|
});
|
|
471
|
-
|
|
472
|
-
if (
|
|
516
|
+
a = false;
|
|
517
|
+
if (S) {
|
|
473
518
|
// Match the promise branch, but only rethrow during the initial read.
|
|
474
|
-
|
|
519
|
+
u = true;
|
|
475
520
|
handleError(r);
|
|
476
521
|
if (i) throw r;
|
|
477
522
|
return true;
|
|
478
523
|
}
|
|
479
|
-
if (l && !
|
|
480
|
-
|
|
524
|
+
if (l && !t.done) {
|
|
525
|
+
s = t.value;
|
|
481
526
|
f = true;
|
|
482
527
|
return iterate();
|
|
483
528
|
}
|
|
484
|
-
return l &&
|
|
529
|
+
return l && t.done;
|
|
485
530
|
};
|
|
486
531
|
const l = iterate();
|
|
487
532
|
// Later iterate() calls run from asyncWrite, where rethrowing would be unhandled.
|
|
@@ -491,31 +536,31 @@ r) {
|
|
|
491
536
|
// Landed-synchronously verdict for a LIVE iterator drain; null when no live
|
|
492
537
|
// drain ran (plain promise flight, or a deferred flatten). Drives the
|
|
493
538
|
// shared NotReady/loading tail below.
|
|
494
|
-
let
|
|
539
|
+
let u = null;
|
|
495
540
|
// Flatten one async level: a thenable that RESOLVES to an AsyncIterable —
|
|
496
541
|
// the shape every async stub returning a stream produces — consumes as the
|
|
497
542
|
// stream itself rather than settling on the iterable object. One level
|
|
498
543
|
// only: A+ `then` already collapses nested thenables, so the resolved
|
|
499
544
|
// value is never itself a thenable.
|
|
500
|
-
const flattenIfIterable = (e,
|
|
501
|
-
let
|
|
545
|
+
const flattenIfIterable = (e, n) => {
|
|
546
|
+
let t = false;
|
|
502
547
|
if (typeof e === "object" && e !== null) {
|
|
503
548
|
untrack(() => {
|
|
504
|
-
|
|
549
|
+
t = e[Symbol.asyncIterator];
|
|
505
550
|
});
|
|
506
551
|
}
|
|
507
|
-
if (!
|
|
508
|
-
|
|
509
|
-
const r = consumeIterator(e,
|
|
510
|
-
if (!
|
|
552
|
+
if (!t) return false;
|
|
553
|
+
s = undefined;
|
|
554
|
+
const r = consumeIterator(e, n);
|
|
555
|
+
if (!n) u = r;
|
|
511
556
|
return true;
|
|
512
557
|
};
|
|
513
558
|
if (f) {
|
|
514
|
-
let
|
|
515
|
-
|
|
559
|
+
let t = false, r = false, o, f = true;
|
|
560
|
+
n.then(r => {
|
|
516
561
|
if (f) {
|
|
517
|
-
|
|
518
|
-
|
|
562
|
+
s = r;
|
|
563
|
+
t = true;
|
|
519
564
|
} else {
|
|
520
565
|
// Flattening consumes the stream as the value: each yield lands
|
|
521
566
|
// through asyncWrite under this flight's identity; the first one
|
|
@@ -527,7 +572,7 @@ r) {
|
|
|
527
572
|
// compute catch above it: a protocol throw settles as this
|
|
528
573
|
// flight's error (#4112).
|
|
529
574
|
try {
|
|
530
|
-
if (e.T?.
|
|
575
|
+
if (e.T?.Ee === n && !(e.R & REACTIVE_DISPOSED) && flattenIfIterable(r, e)) return;
|
|
531
576
|
asyncWrite(r);
|
|
532
577
|
} catch (e) {
|
|
533
578
|
handleError(e);
|
|
@@ -550,14 +595,14 @@ r) {
|
|
|
550
595
|
// momentarily read as `undefined`.
|
|
551
596
|
handleError(o);
|
|
552
597
|
throw o;
|
|
553
|
-
} else if (!
|
|
554
|
-
//
|
|
555
|
-
// work on a loadingValue node is
|
|
556
|
-
// boundaries
|
|
557
|
-
// lands through asyncWrite.
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
} else if (!flattenIfIterable(
|
|
598
|
+
} else if (!t) {
|
|
599
|
+
// Not landed: the shared tail below suspends, or — loading window —
|
|
600
|
+
// serves commit #0 (first-flight work on a loadingValue node is
|
|
601
|
+
// loading-class, invisible to boundaries; the flight itself is already
|
|
602
|
+
// registered in _inFlight and lands through asyncWrite). A promise is
|
|
603
|
+
// never an iterator, so nothing between here and there runs.
|
|
604
|
+
u = false;
|
|
605
|
+
} else if (!flattenIfIterable(s)) {
|
|
561
606
|
// Synchronously-resolved promise: the first real answer landed.
|
|
562
607
|
e.ce = false;
|
|
563
608
|
}
|
|
@@ -565,27 +610,27 @@ r) {
|
|
|
565
610
|
// are still inside the synchronous read): full initial-drain semantics
|
|
566
611
|
// apply and the shared tail below settles the verdict.
|
|
567
612
|
}
|
|
568
|
-
if (o) flattenIfIterable(
|
|
569
|
-
if (
|
|
570
|
-
if (!
|
|
571
|
-
// Loading window: serve commit #0
|
|
572
|
-
if (e.ce) return e.
|
|
613
|
+
if (o) flattenIfIterable(n);
|
|
614
|
+
if (u !== null) {
|
|
615
|
+
if (!u) {
|
|
616
|
+
// Loading window: serve commit #0 instead of suspending.
|
|
617
|
+
if (e.ce) return e.le;
|
|
573
618
|
throw new NotReadyError(context);
|
|
574
619
|
}
|
|
575
620
|
// A sync first yield (or immediate empty completion) is the first real
|
|
576
621
|
// answer; async yields clear inside asyncWrite.
|
|
577
622
|
e.ce = false;
|
|
578
623
|
}
|
|
579
|
-
return
|
|
624
|
+
return s;
|
|
580
625
|
}
|
|
581
626
|
|
|
582
|
-
function clearStatus(e,
|
|
627
|
+
function clearStatus(e, n = false) {
|
|
583
628
|
if (e.T?.fe) clearPendingSources(e);
|
|
584
|
-
if (e.T?.
|
|
585
|
-
e.m =
|
|
586
|
-
if (e.T?.
|
|
587
|
-
const
|
|
588
|
-
if (
|
|
629
|
+
if (e.T?.se) e.T.se = false;
|
|
630
|
+
e.m = n ? 0 : e.m & STATUS_UNINITIALIZED;
|
|
631
|
+
if (e.T?.U) setPendingError(e);
|
|
632
|
+
const t = statusNotifierOf(e);
|
|
633
|
+
if (t) t.call(e);
|
|
589
634
|
}
|
|
590
635
|
|
|
591
636
|
/**
|
|
@@ -612,35 +657,36 @@ function clearStatus(e, t = false) {
|
|
|
612
657
|
* the first landing retires UNINITIALIZED. `_error` must move off self: a
|
|
613
658
|
* reader thrown NotReady(self) would park on a retired entry. Companions
|
|
614
659
|
* keep their verdict (pending before and after; the write re-syncs them).
|
|
615
|
-
*/ function landStatus(e,
|
|
616
|
-
const
|
|
660
|
+
*/ function landStatus(e, n = false) {
|
|
661
|
+
const t = e.T?.fe;
|
|
617
662
|
// (The full clear below drops the set whether or not self was retired first.)
|
|
618
|
-
if (
|
|
619
|
-
e.T.
|
|
620
|
-
if (
|
|
621
|
-
setPendingError(e,
|
|
622
|
-
} else clearStatus(e,
|
|
663
|
+
if (t && (t.delete(e), t.size)) {
|
|
664
|
+
e.T.se = false;
|
|
665
|
+
if (n) e.m = STATUS_PENDING;
|
|
666
|
+
setPendingError(e, t.values().next().value);
|
|
667
|
+
} else clearStatus(e, n);
|
|
623
668
|
}
|
|
624
669
|
|
|
625
|
-
function notifyStatus(e,
|
|
670
|
+
function notifyStatus(e, n, t) {
|
|
626
671
|
// Wrap regular errors to track source node
|
|
627
|
-
if (
|
|
628
|
-
const
|
|
672
|
+
if (n === STATUS_ERROR && !(t instanceof StatusError) && !(t instanceof NotReadyError)) t = new StatusError(e, t);
|
|
673
|
+
const r = n === STATUS_PENDING && t instanceof NotReadyError ? t.source : undefined;
|
|
629
674
|
// (CARVE 2: the lane assignment went with the engine.)
|
|
630
|
-
{
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
675
|
+
if (n === STATUS_PENDING && r) {
|
|
676
|
+
// D-41: a re-ask of a shown error keeps it the display (`_shownError`).
|
|
677
|
+
if (e.m & STATUS_ERROR) e.T.Ie = e.T.U;
|
|
678
|
+
addPendingSource(e, r);
|
|
679
|
+
// A re-ask of an error that answered the first read: still no value.
|
|
680
|
+
e.m = STATUS_PENDING | (e.m & STATUS_NO_VALUE ? STATUS_UNINITIALIZED : e.m & STATUS_UNINITIALIZED);
|
|
681
|
+
// Preserve the current source on this propagation so readers park on
|
|
682
|
+
// the distinct pending source.
|
|
683
|
+
setPendingError(e, r, t);
|
|
684
|
+
} else {
|
|
685
|
+
clearPendingSources(e);
|
|
686
|
+
// An error answering a node with no value marks it so; a re-ask of it
|
|
687
|
+
// (pending) is uninitialized again; a write (STATUS_NONE) gives it one.
|
|
688
|
+
e.m = n | (n === STATUS_ERROR ? e.m & (STATUS_UNINITIALIZED | STATUS_NO_VALUE) ? STATUS_NO_VALUE : 0 : n === STATUS_PENDING && e.m & STATUS_NO_VALUE ? STATUS_UNINITIALIZED : e.m & STATUS_UNINITIALIZED);
|
|
689
|
+
ext(e).U = t;
|
|
644
690
|
}
|
|
645
691
|
// Lanes: a written guess is an optimistic boundary (A17 — "the optimistic
|
|
646
692
|
// future value until we know otherwise"). Its own source in flight is its
|
|
@@ -648,28 +694,26 @@ function notifyStatus(e, t, n, r) {
|
|
|
648
694
|
// transition's to wait on — the frame it was asked in holds (A17: "a
|
|
649
695
|
// transition whose optimistic node is still pending on its own fetch is
|
|
650
696
|
// not complete"), through the node itself (`blocked`).
|
|
651
|
-
if (
|
|
697
|
+
if (n === STATUS_PENDING && e.l & CONFIG_GUESS) {
|
|
652
698
|
if (passLane === null) joinFuture(null);
|
|
653
699
|
return;
|
|
654
700
|
}
|
|
655
|
-
const
|
|
656
|
-
if (
|
|
657
|
-
|
|
658
|
-
f.call(e);
|
|
659
|
-
}
|
|
701
|
+
const o = statusNotifierOf(e);
|
|
702
|
+
if (o) {
|
|
703
|
+
o.call(e);
|
|
660
704
|
return;
|
|
661
705
|
}
|
|
662
|
-
propagateStatus(e,
|
|
706
|
+
propagateStatus(e, n, t);
|
|
663
707
|
}
|
|
664
708
|
|
|
665
709
|
/** The dependents' half of `notifyStatus`: `el`'s status reaches each
|
|
666
710
|
* subscriber as a propagated mark (or a re-derive). On its own for a store
|
|
667
711
|
* family's leaves, which are not the derive's dependents — the family
|
|
668
712
|
* propagates the derive's status from each leaf to the leaf's readers
|
|
669
|
-
* (store/projection.ts `wakeFamily`), exactly as a memo's would reach its. */ function propagateStatus(e,
|
|
670
|
-
const
|
|
671
|
-
forEachDependent(e, (
|
|
672
|
-
|
|
713
|
+
* (store/projection.ts `wakeFamily`), exactly as a memo's would reach its. */ function propagateStatus(e, n, t) {
|
|
714
|
+
const r = n === STATUS_PENDING && t instanceof NotReadyError ? t.source : undefined;
|
|
715
|
+
forEachDependent(e, (o, f) => {
|
|
716
|
+
o.Se = clock;
|
|
673
717
|
// A pending mark on a kept-tail link re-derives the subscriber instead of
|
|
674
718
|
// marking it (A30, #3494 review; fuzzer latest-1 #2141; #3519 review).
|
|
675
719
|
// Past `_depsTail` lie the committed frame's deps, kept by A30 because
|
|
@@ -689,8 +733,8 @@ function notifyStatus(e, t, n, r) {
|
|
|
689
733
|
// Inheriting the error would make a probe's reader errored. A boundary's
|
|
690
734
|
// output (CONFIG_REDERIVE, boundaries.ts) the same: content or fallback
|
|
691
735
|
// is its pass's call.
|
|
692
|
-
if (
|
|
693
|
-
enqueueSub(
|
|
736
|
+
if (o.l & (CONFIG_REDERIVE | CONFIG_VERDICT) || n === STATUS_PENDING && f.de !== o.Ne) {
|
|
737
|
+
enqueueSub(o);
|
|
694
738
|
schedule();
|
|
695
739
|
return;
|
|
696
740
|
}
|
|
@@ -701,30 +745,31 @@ function notifyStatus(e, t, n, r) {
|
|
|
701
745
|
// frame still passes through — its pass is the frame's (#3372, compiled
|
|
702
746
|
// shape: the unmount it stages makes the observer a zombie whose say is
|
|
703
747
|
// moot for this transaction, A15 #3463).
|
|
704
|
-
if (
|
|
705
|
-
if (passLane === null && (
|
|
748
|
+
if (n === STATUS_PENDING && r && o.T?.fe?.has(r)) {
|
|
749
|
+
if (passLane === null && (o.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && globalQueue.q && !freshHook?.(o, r.Se === clock)) joinFuture(txOf(o));
|
|
706
750
|
return;
|
|
707
751
|
}
|
|
708
|
-
if (
|
|
752
|
+
if (n === STATUS_PENDING && r || n !== STATUS_PENDING && (o.T?.U !== t || o.T?.fe)) {
|
|
709
753
|
// The marked subscriber is queued for the commit sweep so its status
|
|
710
754
|
// bookkeeping (loading window, uninitialized clear) runs with the flush
|
|
711
755
|
// — with the lane, when the pass propagating is the lane's work and
|
|
712
756
|
// the dependent's pending is the lane's own flight (`laneStage`).
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
if (t === STATUS_PENDING && (r.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && !r.i) joinFuture(txOf(r));
|
|
723
|
-
}
|
|
757
|
+
// (No answer: a pending propagation leaves a written guess a guess.)
|
|
758
|
+
if (laneStageHook && passLane !== null) laneStageHook(o, passLane, true, e); else {
|
|
759
|
+
queuePendingNode(o);
|
|
760
|
+
// A15 (#3443): pending propagates onto a held memo without
|
|
761
|
+
// recomputing it, and the propagation itself enters the memo's
|
|
762
|
+
// transaction — the flight flows into a memo that transaction
|
|
763
|
+
// holds, so the write that started it is held with it. A render
|
|
764
|
+
// effect's membership is its pass's (`notify`), never sticky.
|
|
765
|
+
if (n === STATUS_PENDING && (o.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && !o.i) joinFuture(txOf(o));
|
|
724
766
|
}
|
|
725
|
-
notifyStatus(
|
|
767
|
+
notifyStatus(o, n, t);
|
|
768
|
+
// A derived store's derive (store/projection.ts): marked, not re-run —
|
|
769
|
+
// its leaves' readers are not its dependents, so the family walks them.
|
|
770
|
+
o.fam?.st(n);
|
|
726
771
|
}
|
|
727
772
|
});
|
|
728
773
|
}
|
|
729
774
|
|
|
730
|
-
export { addPendingSource, clearStatus, forEachDependent, handleAsync, isThenable, notifyStatus, parkLoadingWindow, propagateStatus, releaseFlightTeardown, releaseIfSettledUnobserved, releaseSettledDependents, setPendingError, settleErroredDependents, settlePendingSource, stageError };
|
|
775
|
+
export { addPendingSource, clearStatus, forEachDependent, handleAsync, isThenable, notifyStatus, onSupersede, parkLoadingWindow, propagateStatus, releaseFlightTeardown, releaseIfSettledUnobserved, releaseSettledDependents, setPendingError, settleErroredDependents, settlePendingSource, stageError };
|