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