@solidjs/signals 2.0.0-rc.15 → 2.0.0-rc.17
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 +2268 -1734
- package/dist/dev.attribution.js +75 -52
- package/dist/dev.js +1348 -658
- package/dist/observe/affects-marks.js +86 -0
- package/dist/observe/affects.js +22 -73
- package/dist/observe/boundaries.js +192 -190
- package/dist/observe/core/async.js +177 -131
- package/dist/observe/core/attribution-queries.js +1 -1
- package/dist/observe/core/attribution.js +176 -159
- package/dist/observe/core/constants.js +17 -3
- package/dist/observe/core/context.js +5 -3
- package/dist/observe/core/core.js +520 -364
- package/dist/observe/core/dev.js +66 -38
- package/dist/observe/core/effect.js +50 -48
- package/dist/observe/core/error.js +5 -3
- package/dist/observe/core/external.js +5 -6
- package/dist/observe/core/graph.js +60 -44
- package/dist/observe/core/heap.js +33 -33
- package/dist/observe/core/identities.js +62 -0
- package/dist/observe/core/lanes.js +238 -260
- package/dist/observe/core/owner.js +117 -79
- package/dist/observe/core/scheduler.js +381 -217
- package/dist/observe/core/truth.js +45 -0
- package/dist/observe/core/verdict.js +91 -50
- package/dist/observe/deferred.js +184 -0
- package/dist/observe/index.js +7 -1
- package/dist/observe/map.js +345 -260
- package/dist/observe/reveal.js +76 -76
- package/dist/observe/signals.js +47 -31
- package/dist/observe/store/affects.js +18 -18
- package/dist/observe/store/edges.js +39 -0
- package/dist/observe/store/optimistic.js +146 -100
- package/dist/observe/store/projection.js +68 -55
- package/dist/observe/store/store.js +180 -118
- package/dist/prod/affects-marks.js +86 -0
- package/dist/prod/affects.js +23 -72
- package/dist/prod/boundaries.js +156 -154
- package/dist/prod/core/async.js +173 -127
- package/dist/prod/core/constants.js +17 -3
- package/dist/prod/core/context.js +5 -3
- package/dist/prod/core/core.js +495 -343
- package/dist/prod/core/effect.js +70 -68
- package/dist/prod/core/error.js +5 -3
- package/dist/prod/core/external.js +5 -6
- package/dist/prod/core/graph.js +60 -44
- package/dist/prod/core/heap.js +38 -38
- package/dist/prod/core/identities.js +62 -0
- package/dist/prod/core/lanes.js +230 -252
- package/dist/prod/core/owner.js +114 -77
- package/dist/prod/core/scheduler.js +327 -163
- package/dist/prod/core/truth.js +45 -0
- package/dist/prod/core/verdict.js +90 -49
- package/dist/prod/deferred.js +182 -0
- package/dist/prod/index.js +7 -1
- package/dist/prod/map.js +360 -275
- package/dist/prod/reveal.js +76 -76
- package/dist/prod/signals.js +42 -26
- package/dist/prod/store/affects.js +6 -6
- package/dist/prod/store/edges.js +39 -0
- package/dist/prod/store/optimistic.js +143 -98
- package/dist/prod/store/projection.js +25 -19
- package/dist/prod/store/store.js +157 -100
- package/dist/types/affects-marks.d.ts +14 -0
- package/dist/types/boundaries.d.ts +1 -1
- package/dist/types/core/async.d.ts +10 -0
- package/dist/types/core/constants.d.ts +13 -1
- package/dist/types/core/core.d.ts +54 -5
- package/dist/types/core/dev.d.ts +51 -13
- package/dist/types/core/error.d.ts +9 -1
- package/dist/types/core/graph.d.ts +11 -0
- package/dist/types/core/identities.d.ts +17 -0
- package/dist/types/core/index.d.ts +2 -1
- package/dist/types/core/lanes.d.ts +2 -14
- package/dist/types/core/owner.d.ts +9 -3
- package/dist/types/core/scheduler.d.ts +70 -32
- package/dist/types/core/truth.d.ts +16 -0
- package/dist/types/core/types.d.ts +21 -0
- package/dist/types/core/verdict.d.ts +32 -0
- package/dist/types/deferred.d.ts +28 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/map.d.ts +2 -2
- package/dist/types/signals.d.ts +28 -7
- package/dist/types/store/edges.d.ts +9 -0
- package/dist/types/store/index.d.ts +10 -3
- package/dist/types/store/optimistic.d.ts +3 -2
- package/dist/types/store/projection.d.ts +4 -3
- package/dist/types/store/store.d.ts +1 -1
- package/dist/types/store/target.d.ts +16 -8
- package/dist/types/store/types.d.ts +14 -0
- package/package.json +1 -1
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import {
|
|
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";
|
|
2
2
|
|
|
3
3
|
import { attrHooks } from "./attribution-hooks.js";
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { ext, statusNotifierOf, untrack, context, setSignal } from "./core.js";
|
|
6
6
|
|
|
7
7
|
import "./dev.js";
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { StatusError, NotReadyError } from "./error.js";
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import { unobserved, trimStaleDeps } from "./graph.js";
|
|
12
12
|
|
|
13
13
|
import { enqueueSub } from "./heap.js";
|
|
14
14
|
|
|
15
15
|
import { cleanup } from "./owner.js";
|
|
16
16
|
|
|
17
|
-
import { question, MAINLINE_QUESTION,
|
|
17
|
+
import { passLane, joinFuture, question, MAINLINE_QUESTION, globalQueue, schedule, clock, flush, freshHook, txOf, laneStageHook, queuePendingNode } 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 —
|
|
@@ -22,22 +22,22 @@ import { question, MAINLINE_QUESTION, passLane, joinFuture, clock, schedule, flu
|
|
|
22
22
|
// overlapping source landed beside the Set and removePendingSource refused
|
|
23
23
|
// to clear it, stranding the Set members' pending forever (#2893).
|
|
24
24
|
function addPendingSource(e, t) {
|
|
25
|
-
if (e.
|
|
26
|
-
(ext(e).
|
|
25
|
+
if (e.T?.fe?.has(t)) return false;
|
|
26
|
+
(ext(e).fe ??= new Set).add(t);
|
|
27
27
|
return true;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
function removePendingSource(e, t) {
|
|
31
|
-
const n = e.
|
|
31
|
+
const n = e.T?.fe;
|
|
32
32
|
if (!n?.delete(t)) return false;
|
|
33
|
-
if (!n.size) e.
|
|
33
|
+
if (!n.size) e.T.fe = undefined;
|
|
34
34
|
return true;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
function clearPendingSources(e) {
|
|
38
38
|
// This set is node-owned and never shared; dropping the sole reference
|
|
39
39
|
// releases the set and every entry without a redundant clear() walk.
|
|
40
|
-
if (e.
|
|
40
|
+
if (e.T !== null) e.T.fe = undefined;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
// A rejection-pending only resolves through the settle sweep over the
|
|
@@ -46,9 +46,9 @@ function clearPendingSources(e) {
|
|
|
46
46
|
// (pending sources propagate the origin node, so this covers any depth).
|
|
47
47
|
// Also guards branch-local recovery: another dependency may still need the source.
|
|
48
48
|
function retryReaches(e, t) {
|
|
49
|
-
for (let n = e.
|
|
50
|
-
const e = n.
|
|
51
|
-
if (e === t || e.
|
|
49
|
+
for (let n = e.C; n; n = n.G) {
|
|
50
|
+
const e = n.S;
|
|
51
|
+
if (e === t || e.T?.fe?.has(t)) return true;
|
|
52
52
|
}
|
|
53
53
|
return false;
|
|
54
54
|
}
|
|
@@ -60,32 +60,32 @@ function retryReaches(e, t) {
|
|
|
60
60
|
* with NO read-visible pending status and no downstream propagation. Commit
|
|
61
61
|
* #0 keeps serving.
|
|
62
62
|
*/ function parkLoadingWindow(e, t) {
|
|
63
|
-
ext(e).
|
|
63
|
+
ext(e).se = true;
|
|
64
64
|
if (t.source) addPendingSource(e, t.source);
|
|
65
65
|
// A settled error is the node's answer ("the error stays the answer until
|
|
66
66
|
// this retry can actually run") — the park must not replace it: reads
|
|
67
67
|
// throw `_error` while STATUS_ERROR is set, and overwriting it here leaks
|
|
68
68
|
// a pending-class NotReadyError from a read-invisible park (#2989).
|
|
69
|
-
if (!(e.
|
|
69
|
+
if (!(e.m & STATUS_ERROR)) setPendingError(e, t.source, t);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
function setPendingError(e, t, n) {
|
|
73
73
|
if (!t) {
|
|
74
|
-
if (e.
|
|
74
|
+
if (e.T !== null) e.T.F = null;
|
|
75
75
|
return;
|
|
76
76
|
}
|
|
77
77
|
if (n instanceof NotReadyError && n.source === t) {
|
|
78
78
|
ext(e).F = n;
|
|
79
79
|
return;
|
|
80
80
|
}
|
|
81
|
-
const r = e.
|
|
81
|
+
const r = e.T?.F;
|
|
82
82
|
if (!(r instanceof NotReadyError) || r.source !== t) {
|
|
83
83
|
ext(e).F = new NotReadyError(t);
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
function forEachDependent(e, t) {
|
|
88
|
-
for (let n = e.
|
|
88
|
+
for (let n = e.o; n !== null; n = n.t) t(n.u, n);
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
// Queue a node to re-run on the next flush (used both when a pending source
|
|
@@ -99,7 +99,7 @@ function forEachDependent(e, t) {
|
|
|
99
99
|
// callbacks handle their own release (settleAutodispose in handleAsync); this
|
|
100
100
|
// covers derivatively-pending dependents, which have no callbacks of their own.
|
|
101
101
|
function releaseIfSettledUnobserved(e) {
|
|
102
|
-
e.
|
|
102
|
+
e.X && e.l & CONFIG_AUTO_DISPOSE && !e.o && !(e.R & REACTIVE_ZOMBIE) && !(e.m & STATUS_PENDING) && unobserved(e);
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
// Error-path sweep: notifyStatus(STATUS_ERROR) clears dependents' pending
|
|
@@ -112,7 +112,7 @@ function releaseSettledDependents(e) {
|
|
|
112
112
|
const visit = e => {
|
|
113
113
|
if (n.has(e)) return;
|
|
114
114
|
n.add(e);
|
|
115
|
-
if (!e.
|
|
115
|
+
if (!e.o && e.l & CONFIG_AUTO_DISPOSE) (t ??= []).push(e);
|
|
116
116
|
forEachDependent(e, visit);
|
|
117
117
|
};
|
|
118
118
|
forEachDependent(e, visit);
|
|
@@ -134,13 +134,44 @@ function releaseSettledDependents(e) {
|
|
|
134
134
|
// (releaseSettledDependents shape): identity holders can sit below an
|
|
135
135
|
// intermediate whose own error state has since been scrubbed or replaced
|
|
136
136
|
// (e.g. an error boundary's tree node).
|
|
137
|
+
/** D-41: an error a pass of a held node found is an answer of
|
|
138
|
+
* that frame — it ends the wait like a value and shows when a value would.
|
|
139
|
+
* Staged like one: `_pendingValue` carries it (CONFIG_STAGED_ERROR), readers
|
|
140
|
+
* in the hold derive from it (`serve` throws it), and the commit publishes
|
|
141
|
+
* it through the ordinary error path (`commitPendingNode` → `notifyStatus`),
|
|
142
|
+
* dependents included. Not for a lane pass, nor a node with no shown value
|
|
143
|
+
* (born held — its error has no frame to wait behind): those publish now,
|
|
144
|
+
* as before. Returns whether the error was staged. */ function stageError(e, t, n) {
|
|
145
|
+
// Held, or — for a pass's own throw — derived from a staging of a flush
|
|
146
|
+
// (REACTIVE_STAGED_READ is this pass's; a landing after the pass cannot
|
|
147
|
+
// read it, the flush it read a staging of has committed or parked since).
|
|
148
|
+
// Staged whether or not the flush has joined a hold yet: a pass that runs
|
|
149
|
+
// before its async sibling joins is the seam's to decide, like a value
|
|
150
|
+
// (#4109). A flush that parks nothing publishes it at its commit (S-38).
|
|
151
|
+
// Not a projection's derive: its readers learn its error through the
|
|
152
|
+
// family, which the commit's publish never reaches (#4189).
|
|
153
|
+
if (!(e.l & CONFIG_HELD || n && e.R & REACTIVE_STAGED_READ && globalQueue.q && !e.fam) || passLane) return false;
|
|
154
|
+
// The node answered (an error ends the wait — no longer pending, nor
|
|
155
|
+
// blocked); a shown error stays its display until the commit.
|
|
156
|
+
clearPendingSources(e);
|
|
157
|
+
if (e.T) e.T.se = false;
|
|
158
|
+
if (!((e.m &= STATUS_ERROR | STATUS_UNINITIALIZED) & STATUS_ERROR)) setPendingError(e);
|
|
159
|
+
e.ue = t instanceof StatusError ? t : new StatusError(e, t);
|
|
160
|
+
e.l |= CONFIG_STAGED | CONFIG_STAGED_ERROR;
|
|
161
|
+
// Readers parked on the flight re-run and derive from the staging; the
|
|
162
|
+
// verdict readers the walk wakes need a flush (#4106).
|
|
163
|
+
settlePendingSource(e);
|
|
164
|
+
schedule();
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
|
|
137
168
|
function settleErroredDependents(e, t) {
|
|
138
169
|
let n = false;
|
|
139
170
|
const r = new Set;
|
|
140
171
|
const visit = e => {
|
|
141
172
|
if (r.has(e)) return;
|
|
142
173
|
r.add(e);
|
|
143
|
-
if (e.
|
|
174
|
+
if (e.T?.F === t) {
|
|
144
175
|
enqueueSub(e);
|
|
145
176
|
n = true;
|
|
146
177
|
}
|
|
@@ -175,35 +206,35 @@ function settlePendingSource(e, t = e) {
|
|
|
175
206
|
// when the source went pending, `propagateStatus`), and a landing
|
|
176
207
|
// equal to the committed value notifies nobody — the source settling
|
|
177
208
|
// is its verdict changing: it runs again.
|
|
178
|
-
return f.
|
|
209
|
+
return f.l & CONFIG_VERDICT && enqueueSub(f);
|
|
179
210
|
o.add(f);
|
|
180
|
-
f.
|
|
181
|
-
const
|
|
211
|
+
f.ie = clock;
|
|
212
|
+
const s = f.T?.fe?.values().next().value;
|
|
182
213
|
// STATUS_ERROR + pending sources only coexist via an errored loading
|
|
183
214
|
// window's park (notifyStatus(STATUS_ERROR) clears pending sources
|
|
184
215
|
// otherwise): the settled error stays the answer through the settle —
|
|
185
216
|
// nulling it here would have reads throw `null` until the re-enqueued
|
|
186
217
|
// retry lands, or lose it entirely if that retry parks again (#2989).
|
|
187
|
-
const
|
|
188
|
-
if (
|
|
189
|
-
if (!
|
|
218
|
+
const u = f.m & STATUS_ERROR;
|
|
219
|
+
if (s) {
|
|
220
|
+
if (!u) setPendingError(f, s);
|
|
190
221
|
} else {
|
|
191
|
-
f.
|
|
192
|
-
if (!
|
|
222
|
+
f.m &= ~STATUS_PENDING;
|
|
223
|
+
if (!u) setPendingError(f);
|
|
193
224
|
// L2: a landing resumes the transaction waiting on it (maintainer: "it
|
|
194
225
|
// resumes the sync frame") — a held reader's re-pass over the landed
|
|
195
226
|
// value is that transaction's work, and the flush that carries it
|
|
196
227
|
// parks into it.
|
|
197
|
-
if (f.
|
|
198
|
-
if (f.
|
|
228
|
+
if (f.l & CONFIG_HELD) joinFuture(txOf(f));
|
|
229
|
+
if (f.T?.se) {
|
|
199
230
|
enqueueSub(f);
|
|
200
231
|
n = true;
|
|
201
232
|
}
|
|
202
|
-
if (f.
|
|
233
|
+
if (f.T !== null) f.T.se = false;
|
|
203
234
|
// Fully settled with nobody watching: release candidate (#2934). Checked
|
|
204
235
|
// again at release time — deferred so unobserved() can't unlink subs
|
|
205
236
|
// lists this walk is still iterating.
|
|
206
|
-
if (!f.
|
|
237
|
+
if (!f.o && f.l & CONFIG_AUTO_DISPOSE) (r ??= []).push(f);
|
|
207
238
|
}
|
|
208
239
|
forEachDependent(f, settle);
|
|
209
240
|
};
|
|
@@ -220,9 +251,9 @@ function isThenable(e) {
|
|
|
220
251
|
}
|
|
221
252
|
|
|
222
253
|
/** Fire and clear a node's iterator-flight cancellation hook (#3122). */ function releaseFlightTeardown(e) {
|
|
223
|
-
const t = e.
|
|
254
|
+
const t = e.T?.ae;
|
|
224
255
|
if (t != null) {
|
|
225
|
-
e.
|
|
256
|
+
e.T.ae = null;
|
|
226
257
|
t();
|
|
227
258
|
}
|
|
228
259
|
}
|
|
@@ -242,9 +273,9 @@ r) {
|
|
|
242
273
|
});
|
|
243
274
|
}
|
|
244
275
|
if (!f && !o) {
|
|
245
|
-
if (e.
|
|
276
|
+
if (e.T !== null) e.T.Se = null;
|
|
246
277
|
// A sync landing is the first real answer for a loadingValue node.
|
|
247
|
-
e.
|
|
278
|
+
e.ce = false;
|
|
248
279
|
return t;
|
|
249
280
|
}
|
|
250
281
|
// Flight replacement relies on recompute's supersede release for iterator
|
|
@@ -252,57 +283,60 @@ r) {
|
|
|
252
283
|
// self-registration — runs during a recompute of `el`, which has already
|
|
253
284
|
// fired _flightTeardown. A future non-recompute registration path must
|
|
254
285
|
// release it here before overwriting _inFlight.
|
|
255
|
-
ext(e).
|
|
286
|
+
ext(e).Se = t;
|
|
256
287
|
// The question this flight answers (A18 provenance, lanes.ts): the action
|
|
257
288
|
// whose slice asked it; mainline is always the current question.
|
|
258
|
-
e.
|
|
289
|
+
e.T.Ee = question || MAINLINE_QUESTION;
|
|
259
290
|
// The run that asked this flight read every input without throwing: an
|
|
260
291
|
// input still in flight was masked for it (an active override, A17), so
|
|
261
292
|
// pending state those inputs propagated onto the node earlier does not
|
|
262
293
|
// describe this answer. Drop it — the flight is the node's pending now.
|
|
263
294
|
// The landing retires only the flight's own entry (landStatus, #3373), so
|
|
264
295
|
// an entry that survived here would hold the node past its own answer.
|
|
265
|
-
e.
|
|
296
|
+
e.T.fe = undefined;
|
|
266
297
|
// Attribution hook: a new flight is registered. Fired here (not in the
|
|
267
298
|
// branches below) so every flight shape — plain thenable, iterator, the
|
|
268
299
|
// flattened combinations — is announced exactly once, while the recompute
|
|
269
300
|
// frame that caused it is still on the engine's stack. Not inside a try
|
|
270
301
|
// (#2883 — see attribution-hooks.ts).
|
|
271
302
|
if (attrHooks !== null) attrHooks.flightStart(e, t);
|
|
272
|
-
let
|
|
273
|
-
const handleError =
|
|
274
|
-
if (e.
|
|
303
|
+
let s;
|
|
304
|
+
const handleError = o => {
|
|
305
|
+
if (e.T?.Se !== t) return;
|
|
275
306
|
// NotReadyError from rejected promises should be treated as pending, not error
|
|
276
|
-
let
|
|
277
|
-
if (
|
|
307
|
+
let f = o instanceof NotReadyError;
|
|
308
|
+
if (f && e.ce) {
|
|
278
309
|
// Loading window: the flight died waiting on an unready source. Keep
|
|
279
310
|
// serving commit #0 — same parking as recompute's catch for sync
|
|
280
311
|
// dependency throws. The dead flight is released so the clock-gated
|
|
281
312
|
// error-retry pull (updateIfNecessary) can also re-ask.
|
|
282
|
-
if (e.
|
|
283
|
-
parkLoadingWindow(e,
|
|
284
|
-
e.
|
|
313
|
+
if (e.T !== null) e.T.Se = null;
|
|
314
|
+
parkLoadingWindow(e, o);
|
|
315
|
+
e.ie = clock;
|
|
285
316
|
return;
|
|
286
317
|
}
|
|
287
|
-
|
|
318
|
+
// D-41: a held node's error is staged (`stageError`). A derived store's
|
|
319
|
+
// readers learn its error through the family, not the derive's
|
|
320
|
+
// dependents: it publishes now.
|
|
321
|
+
if (f || n || !stageError(e, o)) notifyStatus(e, f ? STATUS_PENDING : STATUS_ERROR, o);
|
|
288
322
|
// A NotReady rejection is a landing into another pending source. The
|
|
289
323
|
// rejected flight will never settle its self entry, so transfer ownership
|
|
290
324
|
// after notifyStatus has propagated the replacement source.
|
|
291
|
-
if (
|
|
292
|
-
e.
|
|
325
|
+
if (f) settlePendingSource(e);
|
|
326
|
+
e.ie = clock;
|
|
293
327
|
// A real error settles derivatively-pending dependents (notifyStatus
|
|
294
328
|
// cleared their pending sources), so stranded lazy ones release here —
|
|
295
329
|
// the error twin of settlePendingSource's release (#2934).
|
|
296
|
-
if (!
|
|
297
|
-
r?.(
|
|
330
|
+
if (!f) releaseSettledDependents(e);
|
|
331
|
+
r?.(o, f);
|
|
298
332
|
};
|
|
299
333
|
const asyncWrite = (r, o) => {
|
|
300
|
-
if (e.
|
|
334
|
+
if (e.T?.Se !== t) return;
|
|
301
335
|
// If the node was dirtied by a newer write (optimistic override or regular),
|
|
302
336
|
// skip this stale async result — the upcoming flush will recompute the node
|
|
303
337
|
// with the new value, creating a fresh Promise that supersedes this one.
|
|
304
338
|
if (e.R & REACTIVE_DIRTY) return;
|
|
305
|
-
const f =
|
|
339
|
+
const f = e.m & STATUS_UNINITIALIZED;
|
|
306
340
|
landStatus(e);
|
|
307
341
|
// Attribution hook: lets the engine snapshot state before the landing
|
|
308
342
|
// branches, so it can tell whether the plain path's setSignal committed a
|
|
@@ -315,6 +349,9 @@ r) {
|
|
|
315
349
|
handleError(e);
|
|
316
350
|
return;
|
|
317
351
|
}
|
|
352
|
+
// A store derive's landing commits through its setter: unheld, the
|
|
353
|
+
// shown error leaves now; held, at the derive's commit.
|
|
354
|
+
if (!(e.l & CONFIG_HELD)) e.T.de = undefined;
|
|
318
355
|
if (f) landStatus(e, true);
|
|
319
356
|
// Attribution hook: this path landed through the setter (derived
|
|
320
357
|
// stores, projections), which does not go through setSignal, so the
|
|
@@ -330,14 +367,9 @@ r) {
|
|
|
330
367
|
// CARVE 2: the override-covered landing (hold + A18 supersession) and the
|
|
331
368
|
// lane-routed landing (derived override, lane effect queue) went with
|
|
332
369
|
// the optimistic engine; every landing is the plain setSignal. A node
|
|
333
|
-
// already held stages its answer into that hold (`setSignal`).
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
} catch (t) {
|
|
337
|
-
// Same containment as above: setSignal's comparator throw is the only
|
|
338
|
-
// pre-commit failure here, and there is no user callsite to throw to.
|
|
339
|
-
notifyStatus(e, STATUS_ERROR, t);
|
|
340
|
-
}
|
|
370
|
+
// already held stages its answer into that hold (`setSignal`). Its
|
|
371
|
+
// comparator throw is contained there (`isSame`, A3).
|
|
372
|
+
setSignal(e, () => r);
|
|
341
373
|
// Attribution hook: this path landed through setSignal, whose write
|
|
342
374
|
// hook already saw any committed change — direct=false lets the engine
|
|
343
375
|
// reclassify that write as an async landing iff it actually committed.
|
|
@@ -349,8 +381,8 @@ r) {
|
|
|
349
381
|
// (`_pendingValue` set inside setSignal) is not — commitPendingNode closes
|
|
350
382
|
// the window when the flush commits it, so no one-frame pulse can leak to
|
|
351
383
|
// observers between the landing and its commit (#2990).
|
|
352
|
-
if (e.
|
|
353
|
-
e.
|
|
384
|
+
if (e.ue === NOT_PENDING) {
|
|
385
|
+
e.ce = false;
|
|
354
386
|
// The landing published: the dependency tail the flight's pass left
|
|
355
387
|
// linked goes now (A30, #3410). A staged landing has not replaced the
|
|
356
388
|
// committed frame yet; `commitPendingNode` trims it.
|
|
@@ -369,7 +401,7 @@ r) {
|
|
|
369
401
|
// Returns whether the node released, so the iterator branch can stop
|
|
370
402
|
// pulling values instead of pumping an unobserved stream forever (#2935).
|
|
371
403
|
const settleAutodispose = () => {
|
|
372
|
-
if (e.
|
|
404
|
+
if (e.l & CONFIG_AUTO_DISPOSE && !e.o && !(e.m & STATUS_PENDING)) {
|
|
373
405
|
unobserved(e);
|
|
374
406
|
return true;
|
|
375
407
|
}
|
|
@@ -382,46 +414,52 @@ r) {
|
|
|
382
414
|
// close registration uses the ambient owner; and DEFERRED (the flattening
|
|
383
415
|
// path — a thenable resolved to an iterable in a later microtask), where
|
|
384
416
|
// there is no caller to serve and no ambient owner: sync-settled steps
|
|
385
|
-
// write through asyncWrite, and close registration
|
|
386
|
-
//
|
|
387
|
-
//
|
|
388
|
-
// meaningful only in the live posture.
|
|
417
|
+
// write through asyncWrite, and close registration targets `owner` (el
|
|
418
|
+
// itself) directly. Returns whether a sync answer landed (first yield or
|
|
419
|
+
// empty completion) — meaningful only in the live posture.
|
|
389
420
|
const consumeIterator = (n, r) => {
|
|
390
421
|
const o = n[Symbol.asyncIterator]();
|
|
391
422
|
let f = false;
|
|
392
|
-
let
|
|
423
|
+
let u = false;
|
|
393
424
|
let i = !r;
|
|
394
425
|
const close = () => {
|
|
395
|
-
if (
|
|
396
|
-
|
|
426
|
+
if (u) return;
|
|
427
|
+
u = true;
|
|
397
428
|
try {
|
|
398
429
|
const e = o.return?.();
|
|
399
430
|
if (isThenable(e)) e.then(undefined, () => {});
|
|
400
431
|
} catch {}
|
|
401
432
|
};
|
|
402
|
-
|
|
433
|
+
cleanup(close, r);
|
|
403
434
|
// Flight-identity cancellation (#3122): the registration above is the
|
|
404
435
|
// owner-death backstop; the teardown slot fires at the _inFlight release
|
|
405
436
|
// sites so supersede stops this stream immediately.
|
|
406
|
-
ext(e).
|
|
437
|
+
ext(e).ae = close;
|
|
407
438
|
// Release check before each next pull: an unobserved lazy node must tear
|
|
408
439
|
// down (its close above runs via disposal, closing the iterator) instead
|
|
409
440
|
// of pumping the stream forever with zero subscribers (#2935).
|
|
441
|
+
// Later pulls run from an async continuation with no compute catch above
|
|
442
|
+
// them: a throw invoking the protocol (next(), the step's then) settles
|
|
443
|
+
// as this flight's error instead of escaping as a rejection (#4112).
|
|
410
444
|
const iterateOrRelease = () => {
|
|
411
|
-
|
|
445
|
+
try {
|
|
446
|
+
if (!settleAutodispose()) iterate();
|
|
447
|
+
} catch (e) {
|
|
448
|
+
handleError(e);
|
|
449
|
+
}
|
|
412
450
|
};
|
|
413
451
|
const iterate = () => {
|
|
414
|
-
let n, r, l = false, a = false,
|
|
452
|
+
let n, r, l = false, a = false, S = true;
|
|
415
453
|
// Protocol tolerance, matching `for await`: `await` unwraps whatever
|
|
416
454
|
// next() returns — a thenable OR a bare IteratorResult. Real producers
|
|
417
455
|
// use the bare form as a promise-free fast path when a value is already
|
|
418
456
|
// buffered (seroval's deserialized streams do), so a bare result is
|
|
419
457
|
// assimilated as an already-settled step instead of crashing on `.then`.
|
|
420
|
-
const
|
|
421
|
-
const
|
|
422
|
-
then: e => void e(
|
|
458
|
+
const c = o.next();
|
|
459
|
+
const E = isThenable(c) ? c : {
|
|
460
|
+
then: e => void e(c)
|
|
423
461
|
};
|
|
424
|
-
|
|
462
|
+
E.then(r => {
|
|
425
463
|
// The sync stash only serves the INITIAL drain (handleAsync's caller
|
|
426
464
|
// consumes syncValue / throws NotReady from it). A sync-settled step
|
|
427
465
|
// after an async gap — seroval buffering values between pulls, a
|
|
@@ -429,17 +467,17 @@ r) {
|
|
|
429
467
|
// stash: it must write through the async path or the value is
|
|
430
468
|
// silently dropped. (The deferred posture never has a caller, so
|
|
431
469
|
// initialRead starts false there and everything writes through.)
|
|
432
|
-
if (
|
|
470
|
+
if (S && i) {
|
|
433
471
|
n = r;
|
|
434
472
|
l = true;
|
|
435
|
-
if (r.done)
|
|
436
|
-
} else if (e.
|
|
473
|
+
if (r.done) u = true;
|
|
474
|
+
} else if (e.T?.Se !== t) {
|
|
437
475
|
return;
|
|
438
476
|
} else if (!r.done) {
|
|
439
477
|
f = true;
|
|
440
478
|
asyncWrite(r.value, iterateOrRelease);
|
|
441
479
|
} else {
|
|
442
|
-
|
|
480
|
+
u = true;
|
|
443
481
|
if (f) {
|
|
444
482
|
schedule();
|
|
445
483
|
flush();
|
|
@@ -450,25 +488,25 @@ r) {
|
|
|
450
488
|
settleAutodispose();
|
|
451
489
|
}
|
|
452
490
|
}, n => {
|
|
453
|
-
if (
|
|
491
|
+
if (S && i) {
|
|
454
492
|
r = n;
|
|
455
493
|
a = true;
|
|
456
|
-
} else if (e.
|
|
457
|
-
|
|
494
|
+
} else if (e.T?.Se === t) {
|
|
495
|
+
u = true;
|
|
458
496
|
handleError(n);
|
|
459
497
|
settleAutodispose();
|
|
460
498
|
}
|
|
461
499
|
});
|
|
462
|
-
|
|
500
|
+
S = false;
|
|
463
501
|
if (a) {
|
|
464
502
|
// Match the promise branch, but only rethrow during the initial read.
|
|
465
|
-
|
|
503
|
+
u = true;
|
|
466
504
|
handleError(r);
|
|
467
505
|
if (i) throw r;
|
|
468
506
|
return true;
|
|
469
507
|
}
|
|
470
508
|
if (l && !n.done) {
|
|
471
|
-
|
|
509
|
+
s = n.value;
|
|
472
510
|
f = true;
|
|
473
511
|
return iterate();
|
|
474
512
|
}
|
|
@@ -482,7 +520,7 @@ r) {
|
|
|
482
520
|
// Landed-synchronously verdict for a LIVE iterator drain; null when no live
|
|
483
521
|
// drain ran (plain promise flight, or a deferred flatten). Drives the
|
|
484
522
|
// shared NotReady/loading tail below.
|
|
485
|
-
let
|
|
523
|
+
let u = null;
|
|
486
524
|
// Flatten one async level: a thenable that RESOLVES to an AsyncIterable —
|
|
487
525
|
// the shape every async stub returning a stream produces — consumes as the
|
|
488
526
|
// stream itself rather than settling on the iterable object. One level
|
|
@@ -496,27 +534,33 @@ r) {
|
|
|
496
534
|
});
|
|
497
535
|
}
|
|
498
536
|
if (!n) return false;
|
|
537
|
+
s = undefined;
|
|
499
538
|
const r = consumeIterator(e, t);
|
|
500
|
-
if (!t)
|
|
539
|
+
if (!t) u = r;
|
|
501
540
|
return true;
|
|
502
541
|
};
|
|
503
542
|
if (f) {
|
|
504
543
|
let n = false, r = false, o, f = true;
|
|
505
|
-
// Close registration for the flattening path. Consumption starts in a
|
|
506
|
-
// microtask where the ambient owner is gone (or worse, someone else's),
|
|
507
|
-
// so `cleanup()` can't be used — the close targets el's disposal list
|
|
508
|
-
// directly, exactly where a live cleanup() during this recompute would
|
|
509
|
-
// have put it. Only a flight that actually flattens becomes
|
|
510
|
-
// disposal-bearing.
|
|
511
|
-
const registerDeferredClose = t => {
|
|
512
|
-
if (!e.Re) e.Re = t; else if (Array.isArray(e.Re)) e.Re.push(t); else e.Re = [ e.Re, t ];
|
|
513
|
-
};
|
|
514
544
|
t.then(r => {
|
|
515
545
|
if (f) {
|
|
516
|
-
|
|
546
|
+
s = r;
|
|
517
547
|
n = true;
|
|
518
|
-
} else
|
|
519
|
-
|
|
548
|
+
} else {
|
|
549
|
+
// Flattening consumes the stream as the value: each yield lands
|
|
550
|
+
// through asyncWrite under this flight's identity; the first one
|
|
551
|
+
// clears pending exactly like a plain promise resolution would.
|
|
552
|
+
// Disposed nodes never start a pump (their disposal list already
|
|
553
|
+
// ran, so nothing could close the iterator). Consumption starts in
|
|
554
|
+
// a microtask where the ambient owner is gone, so the close
|
|
555
|
+
// registers on el's own disposal list. Iterator setup here has no
|
|
556
|
+
// compute catch above it: a protocol throw settles as this
|
|
557
|
+
// flight's error (#4112).
|
|
558
|
+
try {
|
|
559
|
+
if (e.T?.Se === t && !(e.R & REACTIVE_DISPOSED) && flattenIfIterable(r, e)) return;
|
|
560
|
+
asyncWrite(r);
|
|
561
|
+
} catch (e) {
|
|
562
|
+
handleError(e);
|
|
563
|
+
}
|
|
520
564
|
settleAutodispose();
|
|
521
565
|
}
|
|
522
566
|
}, e => {
|
|
@@ -540,35 +584,35 @@ r) {
|
|
|
540
584
|
// work on a loadingValue node is loading-class (invisible to
|
|
541
585
|
// boundaries); the flight itself is already registered in _inFlight and
|
|
542
586
|
// lands through asyncWrite.
|
|
543
|
-
if (e.
|
|
587
|
+
if (e.ce) return e.Ie;
|
|
544
588
|
throw new NotReadyError(context);
|
|
545
|
-
} else if (!flattenIfIterable(
|
|
589
|
+
} else if (!flattenIfIterable(s)) {
|
|
546
590
|
// Synchronously-resolved promise: the first real answer landed.
|
|
547
|
-
e.
|
|
591
|
+
e.ce = false;
|
|
548
592
|
}
|
|
549
593
|
// A sync-resolved promise holding an AsyncIterable flattened LIVE (we
|
|
550
594
|
// are still inside the synchronous read): full initial-drain semantics
|
|
551
595
|
// apply and the shared tail below settles the verdict.
|
|
552
596
|
}
|
|
553
597
|
if (o) flattenIfIterable(t);
|
|
554
|
-
if (
|
|
555
|
-
if (!
|
|
598
|
+
if (u !== null) {
|
|
599
|
+
if (!u) {
|
|
556
600
|
// Loading window: serve commit #0 (see the promise branch above).
|
|
557
|
-
if (e.
|
|
601
|
+
if (e.ce) return e.Ie;
|
|
558
602
|
throw new NotReadyError(context);
|
|
559
603
|
}
|
|
560
604
|
// A sync first yield (or immediate empty completion) is the first real
|
|
561
605
|
// answer; async yields clear inside asyncWrite.
|
|
562
|
-
e.
|
|
606
|
+
e.ce = false;
|
|
563
607
|
}
|
|
564
|
-
return
|
|
608
|
+
return s;
|
|
565
609
|
}
|
|
566
610
|
|
|
567
611
|
function clearStatus(e, t = false) {
|
|
568
|
-
if (e.
|
|
569
|
-
if (e.
|
|
570
|
-
e.
|
|
571
|
-
if (e.
|
|
612
|
+
if (e.T?.fe) clearPendingSources(e);
|
|
613
|
+
if (e.T?.se) if (e.T !== null) e.T.se = false;
|
|
614
|
+
e.m = t ? 0 : e.m & STATUS_UNINITIALIZED;
|
|
615
|
+
if (e.T?.F) setPendingError(e);
|
|
572
616
|
const n = statusNotifierOf(e);
|
|
573
617
|
if (n) n.call(e);
|
|
574
618
|
}
|
|
@@ -598,11 +642,11 @@ function clearStatus(e, t = false) {
|
|
|
598
642
|
* reader thrown NotReady(self) would park on a retired entry. Companions
|
|
599
643
|
* keep their verdict (pending before and after; the write re-syncs them).
|
|
600
644
|
*/ function landStatus(e, t = false) {
|
|
601
|
-
const n = e.
|
|
645
|
+
const n = e.T?.fe;
|
|
602
646
|
// (The full clear below drops the set whether or not self was retired first.)
|
|
603
647
|
if (n && (n.delete(e), n.size)) {
|
|
604
|
-
e.
|
|
605
|
-
if (t) e.
|
|
648
|
+
e.T.se = false;
|
|
649
|
+
if (t) e.m = STATUS_PENDING;
|
|
606
650
|
setPendingError(e, n.values().next().value);
|
|
607
651
|
} else clearStatus(e, t);
|
|
608
652
|
}
|
|
@@ -614,14 +658,16 @@ function notifyStatus(e, t, n, r) {
|
|
|
614
658
|
// (CARVE 2: the lane assignment went with the engine.)
|
|
615
659
|
{
|
|
616
660
|
if (t === STATUS_PENDING && o) {
|
|
661
|
+
// D-41: a re-ask of a shown error keeps it the display (`_shownError`).
|
|
662
|
+
if (e.m & STATUS_ERROR) e.T.de = e.T.F;
|
|
617
663
|
addPendingSource(e, o);
|
|
618
|
-
e.
|
|
664
|
+
e.m = STATUS_PENDING | e.m & STATUS_UNINITIALIZED;
|
|
619
665
|
// Preserve the current source on this propagation so readers park on
|
|
620
666
|
// the distinct pending source.
|
|
621
667
|
setPendingError(e, o, n);
|
|
622
668
|
} else {
|
|
623
669
|
clearPendingSources(e);
|
|
624
|
-
e.
|
|
670
|
+
e.m = t | (t !== STATUS_ERROR ? e.m & STATUS_UNINITIALIZED : 0);
|
|
625
671
|
ext(e).F = n;
|
|
626
672
|
}
|
|
627
673
|
}
|
|
@@ -631,7 +677,7 @@ function notifyStatus(e, t, n, r) {
|
|
|
631
677
|
// transition's to wait on — the frame it was asked in holds (A17: "a
|
|
632
678
|
// transition whose optimistic node is still pending on its own fetch is
|
|
633
679
|
// not complete"), through the node itself (`blocked`).
|
|
634
|
-
if (t === STATUS_PENDING && e.
|
|
680
|
+
if (t === STATUS_PENDING && e.l & CONFIG_GUESS) {
|
|
635
681
|
if (passLane === null) joinFuture(null);
|
|
636
682
|
return;
|
|
637
683
|
}
|
|
@@ -652,7 +698,7 @@ function notifyStatus(e, t, n, r) {
|
|
|
652
698
|
* (store/projection.ts `wakeFamily`), exactly as a memo's would reach its. */ function propagateStatus(e, t, n, r) {
|
|
653
699
|
const o = t === STATUS_PENDING && n instanceof NotReadyError ? n.source : undefined;
|
|
654
700
|
forEachDependent(e, (r, f) => {
|
|
655
|
-
r.
|
|
701
|
+
r.ie = clock;
|
|
656
702
|
// A pending mark on a kept-tail link re-derives the subscriber instead of
|
|
657
703
|
// marking it (A30, #3494 review; fuzzer latest-1 #2141; #3519 review).
|
|
658
704
|
// Past `_depsTail` lie the committed frame's deps, kept by A30 because
|
|
@@ -672,7 +718,7 @@ function notifyStatus(e, t, n, r) {
|
|
|
672
718
|
// Inheriting the error would make a probe's reader errored. A boundary's
|
|
673
719
|
// output (CONFIG_REDERIVE, boundaries.ts) the same: content or fallback
|
|
674
720
|
// is its pass's call.
|
|
675
|
-
if (r.
|
|
721
|
+
if (r.l & (CONFIG_REDERIVE | CONFIG_VERDICT) || t === STATUS_PENDING && f.Te !== r.Ne) {
|
|
676
722
|
enqueueSub(r);
|
|
677
723
|
schedule();
|
|
678
724
|
return;
|
|
@@ -684,25 +730,25 @@ function notifyStatus(e, t, n, r) {
|
|
|
684
730
|
// frame still passes through — its pass is the frame's (#3372, compiled
|
|
685
731
|
// shape: the unmount it stages makes the observer a zombie whose say is
|
|
686
732
|
// moot for this transaction, A15 #3463).
|
|
687
|
-
if (t === STATUS_PENDING && o && r.
|
|
688
|
-
if (passLane === null && (r.
|
|
733
|
+
if (t === STATUS_PENDING && o && r.T?.fe?.has(o)) {
|
|
734
|
+
if (passLane === null && (r.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && globalQueue.q && !freshHook?.(r, o.ie === clock)) joinFuture(txOf(r));
|
|
689
735
|
return;
|
|
690
736
|
}
|
|
691
|
-
if (t === STATUS_PENDING && o || t !== STATUS_PENDING && (r.
|
|
737
|
+
if (t === STATUS_PENDING && o || t !== STATUS_PENDING && (r.T?.F !== n || r.T?.fe)) {
|
|
692
738
|
// The marked subscriber is queued for the commit sweep so its status
|
|
693
739
|
// bookkeeping (loading window, uninitialized clear) runs with the flush
|
|
694
740
|
// — with the lane, when the pass propagating is the lane's work and
|
|
695
741
|
// the dependent's pending is the lane's own flight (`laneStage`).
|
|
696
742
|
{
|
|
697
743
|
// (No answer: a pending propagation leaves a written guess a guess.)
|
|
698
|
-
if (passLane !== null)
|
|
744
|
+
if (laneStageHook && passLane !== null) laneStageHook(r, passLane, true, e); else {
|
|
699
745
|
queuePendingNode(r);
|
|
700
746
|
// A15 (#3443): pending propagates onto a held memo without
|
|
701
747
|
// recomputing it, and the propagation itself enters the memo's
|
|
702
748
|
// transaction — the flight flows into a memo that transaction
|
|
703
749
|
// holds, so the write that started it is held with it. A render
|
|
704
750
|
// effect's membership is its pass's (`notify`), never sticky.
|
|
705
|
-
if (t === STATUS_PENDING && (r.
|
|
751
|
+
if (t === STATUS_PENDING && (r.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && !r.i) joinFuture(txOf(r));
|
|
706
752
|
}
|
|
707
753
|
}
|
|
708
754
|
notifyStatus(r, t, n);
|
|
@@ -710,4 +756,4 @@ function notifyStatus(e, t, n, r) {
|
|
|
710
756
|
});
|
|
711
757
|
}
|
|
712
758
|
|
|
713
|
-
export { addPendingSource, clearStatus, forEachDependent, handleAsync, isThenable, notifyStatus, parkLoadingWindow, propagateStatus, releaseFlightTeardown, releaseSettledDependents, setPendingError, settleErroredDependents, settlePendingSource };
|
|
759
|
+
export { addPendingSource, clearStatus, forEachDependent, handleAsync, isThenable, notifyStatus, parkLoadingWindow, propagateStatus, releaseFlightTeardown, releaseIfSettledUnobserved, releaseSettledDependents, setPendingError, settleErroredDependents, settlePendingSource, stageError };
|