@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
|
@@ -1,8 +1,8 @@
|
|
|
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
3
|
import { attrHooks } from "./attribution-hooks.js";
|
|
4
4
|
|
|
5
|
-
import { ext, statusNotifierOf,
|
|
5
|
+
import { ext, statusNotifierOf, context, untrack, setSignal } from "./core.js";
|
|
6
6
|
|
|
7
7
|
import "./dev.js";
|
|
8
8
|
|
|
@@ -12,7 +12,7 @@ import { unobserved, trimStaleDeps } from "./graph.js";
|
|
|
12
12
|
|
|
13
13
|
import { enqueueSub } from "./heap.js";
|
|
14
14
|
|
|
15
|
-
import { cleanup } from "./owner.js";
|
|
15
|
+
import { disposeChildren, cleanup } from "./owner.js";
|
|
16
16
|
|
|
17
17
|
import { passLane, joinFuture, question, MAINLINE_QUESTION, globalQueue, schedule, clock, flush, freshHook, txOf, laneStageHook, queuePendingNode } from "./scheduler.js";
|
|
18
18
|
|
|
@@ -22,22 +22,22 @@ import { passLane, joinFuture, question, MAINLINE_QUESTION, globalQueue, schedul
|
|
|
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.T?.
|
|
26
|
-
(ext(e).
|
|
25
|
+
if (e.T?.se?.has(t)) return false;
|
|
26
|
+
(ext(e).se ??= new NewSet).add(t);
|
|
27
27
|
return true;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
function removePendingSource(e, t) {
|
|
31
|
-
const n = e.T?.
|
|
31
|
+
const n = e.T?.se;
|
|
32
32
|
if (!n?.delete(t)) return false;
|
|
33
|
-
if (!n.size) e.T.
|
|
33
|
+
if (!n.size) e.T.se = 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.T !== null) e.T.
|
|
40
|
+
if (e.T !== null) e.T.se = undefined;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
// A rejection-pending only resolves through the settle sweep over the
|
|
@@ -48,7 +48,7 @@ function clearPendingSources(e) {
|
|
|
48
48
|
function retryReaches(e, t) {
|
|
49
49
|
for (let n = e.C; n; n = n.G) {
|
|
50
50
|
const e = n.S;
|
|
51
|
-
if (e === t || e.T?.
|
|
51
|
+
if (e === t || e.T?.se?.has(t)) return true;
|
|
52
52
|
}
|
|
53
53
|
return false;
|
|
54
54
|
}
|
|
@@ -60,7 +60,7 @@ 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).fe = 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
|
|
@@ -108,7 +108,7 @@ function releaseIfSettledUnobserved(e) {
|
|
|
108
108
|
// Collect-then-release so unobserved() never unlinks under the walk.
|
|
109
109
|
function releaseSettledDependents(e) {
|
|
110
110
|
let t;
|
|
111
|
-
const n = new
|
|
111
|
+
const n = new NewSet;
|
|
112
112
|
const visit = e => {
|
|
113
113
|
if (n.has(e)) return;
|
|
114
114
|
n.add(e);
|
|
@@ -154,9 +154,9 @@ function releaseSettledDependents(e) {
|
|
|
154
154
|
// The node answered (an error ends the wait — no longer pending, nor
|
|
155
155
|
// blocked); a shown error stays its display until the commit.
|
|
156
156
|
clearPendingSources(e);
|
|
157
|
-
if (e.T) e.T.
|
|
157
|
+
if (e.T) e.T.fe = false;
|
|
158
158
|
if (!((e.m &= STATUS_ERROR | STATUS_UNINITIALIZED) & STATUS_ERROR)) setPendingError(e);
|
|
159
|
-
e.
|
|
159
|
+
e.ie = t instanceof StatusError ? t : new StatusError(e, t);
|
|
160
160
|
e.l |= CONFIG_STAGED | CONFIG_STAGED_ERROR;
|
|
161
161
|
// Readers parked on the flight re-run and derive from the staging; the
|
|
162
162
|
// verdict readers the walk wakes need a flush (#4106).
|
|
@@ -167,7 +167,7 @@ function releaseSettledDependents(e) {
|
|
|
167
167
|
|
|
168
168
|
function settleErroredDependents(e, t) {
|
|
169
169
|
let n = false;
|
|
170
|
-
const r = new
|
|
170
|
+
const r = new NewSet;
|
|
171
171
|
const visit = e => {
|
|
172
172
|
if (r.has(e)) return;
|
|
173
173
|
r.add(e);
|
|
@@ -192,51 +192,51 @@ function settlePendingSource(e, t = e) {
|
|
|
192
192
|
removePendingSource(e, t);
|
|
193
193
|
let n = false;
|
|
194
194
|
let r;
|
|
195
|
-
const o = new
|
|
196
|
-
const settle =
|
|
197
|
-
if (o.has(
|
|
195
|
+
const o = new NewSet;
|
|
196
|
+
const settle = s => {
|
|
197
|
+
if (o.has(s) ||
|
|
198
198
|
// A conditional dropped this source, but another dependency can still
|
|
199
199
|
// carry it. Only retire pending state inherited through the recovered
|
|
200
200
|
// branch. Deliberately NOT marked visited on this early return: the
|
|
201
201
|
// carrying dependency may itself be a later branch of this same walk
|
|
202
202
|
// (two unchanged memos converging), and its visit must be free to
|
|
203
203
|
// re-examine this node once that branch has retired the source.
|
|
204
|
-
t !== e && retryReaches(
|
|
204
|
+
t !== e && retryReaches(s, t) || !removePendingSource(s, t))
|
|
205
205
|
// A19: a verdict reader holds no pending of its own (it was re-derived
|
|
206
206
|
// when the source went pending, `propagateStatus`), and a landing
|
|
207
207
|
// equal to the committed value notifies nobody — the source settling
|
|
208
208
|
// is its verdict changing: it runs again.
|
|
209
|
-
return
|
|
210
|
-
o.add(
|
|
211
|
-
|
|
212
|
-
const
|
|
209
|
+
return s.l & CONFIG_VERDICT && enqueueSub(s);
|
|
210
|
+
o.add(s);
|
|
211
|
+
s.ae = clock;
|
|
212
|
+
const f = s.T?.se?.values().next().value;
|
|
213
213
|
// STATUS_ERROR + pending sources only coexist via an errored loading
|
|
214
214
|
// window's park (notifyStatus(STATUS_ERROR) clears pending sources
|
|
215
215
|
// otherwise): the settled error stays the answer through the settle —
|
|
216
216
|
// nulling it here would have reads throw `null` until the re-enqueued
|
|
217
217
|
// retry lands, or lose it entirely if that retry parks again (#2989).
|
|
218
|
-
const u =
|
|
219
|
-
if (
|
|
220
|
-
if (!u) setPendingError(
|
|
218
|
+
const u = s.m & STATUS_ERROR;
|
|
219
|
+
if (f) {
|
|
220
|
+
if (!u) setPendingError(s, f);
|
|
221
221
|
} else {
|
|
222
|
-
|
|
223
|
-
if (!u) setPendingError(
|
|
222
|
+
s.m &= ~STATUS_PENDING;
|
|
223
|
+
if (!u) setPendingError(s);
|
|
224
224
|
// L2: a landing resumes the transaction waiting on it (maintainer: "it
|
|
225
225
|
// resumes the sync frame") — a held reader's re-pass over the landed
|
|
226
226
|
// value is that transaction's work, and the flush that carries it
|
|
227
227
|
// parks into it.
|
|
228
|
-
if (
|
|
229
|
-
if (
|
|
230
|
-
enqueueSub(
|
|
228
|
+
if (s.l & CONFIG_HELD) joinFuture(txOf(s));
|
|
229
|
+
if (s.T?.fe) {
|
|
230
|
+
enqueueSub(s);
|
|
231
231
|
n = true;
|
|
232
232
|
}
|
|
233
|
-
if (
|
|
233
|
+
if (s.T !== null) s.T.fe = false;
|
|
234
234
|
// Fully settled with nobody watching: release candidate (#2934). Checked
|
|
235
235
|
// again at release time — deferred so unobserved() can't unlink subs
|
|
236
236
|
// lists this walk is still iterating.
|
|
237
|
-
if (!
|
|
237
|
+
if (!s.o && s.l & CONFIG_AUTO_DISPOSE) (r ??= []).push(s);
|
|
238
238
|
}
|
|
239
|
-
forEachDependent(
|
|
239
|
+
forEachDependent(s, settle);
|
|
240
240
|
};
|
|
241
241
|
forEachDependent(e, settle);
|
|
242
242
|
// Release before the flush schedule below: unobserved() pulls the node back
|
|
@@ -251,13 +251,41 @@ function isThenable(e) {
|
|
|
251
251
|
}
|
|
252
252
|
|
|
253
253
|
/** Fire and clear a node's iterator-flight cancellation hook (#3122). */ function releaseFlightTeardown(e) {
|
|
254
|
-
const t = e.T?.
|
|
254
|
+
const t = e.T?.Se;
|
|
255
255
|
if (t != null) {
|
|
256
|
-
e.T.
|
|
256
|
+
e.T.Se = null;
|
|
257
257
|
t();
|
|
258
258
|
}
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
+
/**
|
|
262
|
+
* Internal (not a package export): run `fn` when the current pass's flight
|
|
263
|
+
* is superseded, at the same instant as the iterator close above, instead of
|
|
264
|
+
* when the replacing pass commits (owner cleanups wait for that). Chains onto
|
|
265
|
+
* the node's teardown slot; fires once with it. A no-op unless the ambient
|
|
266
|
+
* owner is a computed in its own pass. `consumeIterator` fills the slot
|
|
267
|
+
* before its first pull, so a source opened in an async generator's body
|
|
268
|
+
* (`for await`) composes after the generator's close.
|
|
269
|
+
*/ function onSupersede(e) {
|
|
270
|
+
const t = context;
|
|
271
|
+
if (t === null || t.X === undefined || !(t.R & REACTIVE_RECOMPUTING_DEPS)) return;
|
|
272
|
+
const n = t.T === null;
|
|
273
|
+
const r = ext(t);
|
|
274
|
+
// Allocating the extension mid-run turns `_x?._inFlight` from undefined into
|
|
275
|
+
// null, which recompute would read as a flight this body registered itself.
|
|
276
|
+
// Restore undefined here, so the fix costs nothing for code that never
|
|
277
|
+
// calls onSupersede.
|
|
278
|
+
if (n) r.Ee = undefined;
|
|
279
|
+
const o = r.Se;
|
|
280
|
+
r.Se = o === null ? e : () => {
|
|
281
|
+
try {
|
|
282
|
+
o();
|
|
283
|
+
} finally {
|
|
284
|
+
e();
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
261
289
|
function handleAsync(e, t, n,
|
|
262
290
|
// The flight rejected (after the node's status is set): a projection
|
|
263
291
|
// tells its leaves' readers, who are not the node's dependents
|
|
@@ -265,15 +293,15 @@ function handleAsync(e, t, n,
|
|
|
265
293
|
// pending on another source, not errored.
|
|
266
294
|
r) {
|
|
267
295
|
let o = false;
|
|
268
|
-
let
|
|
296
|
+
let s = false;
|
|
269
297
|
if (typeof t === "object" && t !== null) {
|
|
270
298
|
untrack(() => {
|
|
271
299
|
o = t[Symbol.asyncIterator];
|
|
272
|
-
|
|
300
|
+
s = !o && isThenable(t);
|
|
273
301
|
});
|
|
274
302
|
}
|
|
275
|
-
if (!
|
|
276
|
-
if (e.T !== null) e.T.
|
|
303
|
+
if (!s && !o) {
|
|
304
|
+
if (e.T !== null) e.T.Ee = null;
|
|
277
305
|
// A sync landing is the first real answer for a loadingValue node.
|
|
278
306
|
e.ce = false;
|
|
279
307
|
return t;
|
|
@@ -283,60 +311,67 @@ r) {
|
|
|
283
311
|
// self-registration — runs during a recompute of `el`, which has already
|
|
284
312
|
// fired _flightTeardown. A future non-recompute registration path must
|
|
285
313
|
// release it here before overwriting _inFlight.
|
|
286
|
-
ext(e).
|
|
314
|
+
ext(e).Ee = t;
|
|
287
315
|
// The question this flight answers (A18 provenance, lanes.ts): the action
|
|
288
316
|
// whose slice asked it; mainline is always the current question.
|
|
289
|
-
e.T.
|
|
317
|
+
e.T.de = question || MAINLINE_QUESTION;
|
|
290
318
|
// The run that asked this flight read every input without throwing: an
|
|
291
319
|
// input still in flight was masked for it (an active override, A17), so
|
|
292
320
|
// pending state those inputs propagated onto the node earlier does not
|
|
293
321
|
// describe this answer. Drop it — the flight is the node's pending now.
|
|
294
322
|
// The landing retires only the flight's own entry (landStatus, #3373), so
|
|
295
323
|
// an entry that survived here would hold the node past its own answer.
|
|
296
|
-
e.T.
|
|
324
|
+
e.T.se = undefined;
|
|
297
325
|
// Attribution hook: a new flight is registered. Fired here (not in the
|
|
298
326
|
// branches below) so every flight shape — plain thenable, iterator, the
|
|
299
327
|
// flattened combinations — is announced exactly once, while the recompute
|
|
300
328
|
// frame that caused it is still on the engine's stack. Not inside a try
|
|
301
329
|
// (#2883 — see attribution-hooks.ts).
|
|
302
330
|
if (attrHooks !== null) attrHooks.flightStart(e, t);
|
|
303
|
-
let
|
|
331
|
+
let f;
|
|
304
332
|
const handleError = o => {
|
|
305
|
-
if (e.T?.
|
|
333
|
+
if (e.T?.Ee !== t) return;
|
|
306
334
|
// NotReadyError from rejected promises should be treated as pending, not error
|
|
307
|
-
let
|
|
308
|
-
if (
|
|
335
|
+
let s = o instanceof NotReadyError;
|
|
336
|
+
if (s && e.ce) {
|
|
309
337
|
// Loading window: the flight died waiting on an unready source. Keep
|
|
310
338
|
// serving commit #0 — same parking as recompute's catch for sync
|
|
311
339
|
// dependency throws. The dead flight is released so the clock-gated
|
|
312
340
|
// error-retry pull (updateIfNecessary) can also re-ask.
|
|
313
|
-
if (e.T !== null) e.T.
|
|
341
|
+
if (e.T !== null) e.T.Ee = null;
|
|
314
342
|
parkLoadingWindow(e, o);
|
|
315
|
-
e.
|
|
343
|
+
e.ae = clock;
|
|
316
344
|
return;
|
|
317
345
|
}
|
|
318
346
|
// D-41: a held node's error is staged (`stageError`). A derived store's
|
|
319
347
|
// readers learn its error through the family, not the derive's
|
|
320
348
|
// dependents: it publishes now.
|
|
321
|
-
if (
|
|
349
|
+
if (s || n || !stageError(e, o)) {
|
|
350
|
+
notifyStatus(e, s ? STATUS_PENDING : STATUS_ERROR, o);
|
|
351
|
+
// The error is this pass's answer, published now: it replaces the shown
|
|
352
|
+
// frame as a value's commit would (`commitPendingNode`), so the frame
|
|
353
|
+
// the pass parked goes instead of waiting for a later success. A no-op
|
|
354
|
+
// when nothing is parked.
|
|
355
|
+
if (!s) disposeChildren(e, false, true);
|
|
356
|
+
}
|
|
322
357
|
// A NotReady rejection is a landing into another pending source. The
|
|
323
358
|
// rejected flight will never settle its self entry, so transfer ownership
|
|
324
359
|
// after notifyStatus has propagated the replacement source.
|
|
325
|
-
if (
|
|
326
|
-
e.
|
|
360
|
+
if (s) settlePendingSource(e);
|
|
361
|
+
e.ae = clock;
|
|
327
362
|
// A real error settles derivatively-pending dependents (notifyStatus
|
|
328
363
|
// cleared their pending sources), so stranded lazy ones release here —
|
|
329
364
|
// the error twin of settlePendingSource's release (#2934).
|
|
330
|
-
if (!
|
|
331
|
-
r?.(o,
|
|
365
|
+
if (!s) releaseSettledDependents(e);
|
|
366
|
+
r?.(o, s);
|
|
332
367
|
};
|
|
333
368
|
const asyncWrite = (r, o) => {
|
|
334
|
-
if (e.T?.
|
|
369
|
+
if (e.T?.Ee !== t) return;
|
|
335
370
|
// If the node was dirtied by a newer write (optimistic override or regular),
|
|
336
371
|
// skip this stale async result — the upcoming flush will recompute the node
|
|
337
372
|
// with the new value, creating a fresh Promise that supersedes this one.
|
|
338
373
|
if (e.R & REACTIVE_DIRTY) return;
|
|
339
|
-
const
|
|
374
|
+
const s = e.m & STATUS_UNINITIALIZED;
|
|
340
375
|
landStatus(e);
|
|
341
376
|
// Attribution hook: lets the engine snapshot state before the landing
|
|
342
377
|
// branches, so it can tell whether the plain path's setSignal committed a
|
|
@@ -350,9 +385,15 @@ r) {
|
|
|
350
385
|
return;
|
|
351
386
|
}
|
|
352
387
|
// A store derive's landing commits through its setter: unheld, the
|
|
353
|
-
// shown error leaves now
|
|
354
|
-
|
|
355
|
-
|
|
388
|
+
// shown error leaves now, and so does a commit-beneath-a-flight's
|
|
389
|
+
// CONFIG_INPUTS_PUBLISHED (its first flight's, #4269) — left standing,
|
|
390
|
+
// a verdict reader of the next flight would suspend instead of being
|
|
391
|
+
// served the screen; held, both at the derive's commit.
|
|
392
|
+
if (!(e.l & CONFIG_HELD)) {
|
|
393
|
+
e.T.Te = undefined;
|
|
394
|
+
e.l &= ~CONFIG_INPUTS_PUBLISHED;
|
|
395
|
+
}
|
|
396
|
+
if (s) landStatus(e, true);
|
|
356
397
|
// Attribution hook: this path landed through the setter (derived
|
|
357
398
|
// stores, projections), which does not go through setSignal, so the
|
|
358
399
|
// plain branch's asyncEnd never ran and the flight stayed open — the
|
|
@@ -381,7 +422,7 @@ r) {
|
|
|
381
422
|
// (`_pendingValue` set inside setSignal) is not — commitPendingNode closes
|
|
382
423
|
// the window when the flush commits it, so no one-frame pulse can leak to
|
|
383
424
|
// observers between the landing and its commit (#2990).
|
|
384
|
-
if (e.
|
|
425
|
+
if (e.ie === NOT_PENDING) {
|
|
385
426
|
e.ce = false;
|
|
386
427
|
// The landing published: the dependency tail the flight's pass left
|
|
387
428
|
// linked goes now (A30, #3410). A staged landing has not replaced the
|
|
@@ -419,7 +460,7 @@ r) {
|
|
|
419
460
|
// empty completion) — meaningful only in the live posture.
|
|
420
461
|
const consumeIterator = (n, r) => {
|
|
421
462
|
const o = n[Symbol.asyncIterator]();
|
|
422
|
-
let
|
|
463
|
+
let s = false;
|
|
423
464
|
let u = false;
|
|
424
465
|
let i = !r;
|
|
425
466
|
const close = () => {
|
|
@@ -433,8 +474,12 @@ r) {
|
|
|
433
474
|
cleanup(close, r);
|
|
434
475
|
// Flight-identity cancellation (#3122): the registration above is the
|
|
435
476
|
// owner-death backstop; the teardown slot fires at the _inFlight release
|
|
436
|
-
// sites so supersede stops this stream immediately.
|
|
437
|
-
|
|
477
|
+
// sites so supersede stops this stream immediately. Assigned, not
|
|
478
|
+
// chained (chaining here costs every app bytes): it replaces any
|
|
479
|
+
// `onSupersede` teardown this run registered earlier, which then falls
|
|
480
|
+
// back to the owner-cleanup channel. A returned `from()` is unaffected:
|
|
481
|
+
// `close` calls its `return()`, which runs its own teardown.
|
|
482
|
+
ext(e).Se = close;
|
|
438
483
|
// Release check before each next pull: an unobserved lazy node must tear
|
|
439
484
|
// down (its close above runs via disposal, closing the iterator) instead
|
|
440
485
|
// of pumping the stream forever with zero subscribers (#2935).
|
|
@@ -455,11 +500,11 @@ r) {
|
|
|
455
500
|
// use the bare form as a promise-free fast path when a value is already
|
|
456
501
|
// buffered (seroval's deserialized streams do), so a bare result is
|
|
457
502
|
// assimilated as an already-settled step instead of crashing on `.then`.
|
|
458
|
-
const
|
|
459
|
-
const
|
|
460
|
-
then: e => void e(
|
|
503
|
+
const E = o.next();
|
|
504
|
+
const c = isThenable(E) ? E : {
|
|
505
|
+
then: e => void e(E)
|
|
461
506
|
};
|
|
462
|
-
|
|
507
|
+
c.then(r => {
|
|
463
508
|
// The sync stash only serves the INITIAL drain (handleAsync's caller
|
|
464
509
|
// consumes syncValue / throws NotReady from it). A sync-settled step
|
|
465
510
|
// after an async gap — seroval buffering values between pulls, a
|
|
@@ -471,14 +516,14 @@ r) {
|
|
|
471
516
|
n = r;
|
|
472
517
|
l = true;
|
|
473
518
|
if (r.done) u = true;
|
|
474
|
-
} else if (e.T?.
|
|
519
|
+
} else if (e.T?.Ee !== t) {
|
|
475
520
|
return;
|
|
476
521
|
} else if (!r.done) {
|
|
477
|
-
|
|
522
|
+
s = true;
|
|
478
523
|
asyncWrite(r.value, iterateOrRelease);
|
|
479
524
|
} else {
|
|
480
525
|
u = true;
|
|
481
|
-
if (
|
|
526
|
+
if (s) {
|
|
482
527
|
schedule();
|
|
483
528
|
flush();
|
|
484
529
|
} else {
|
|
@@ -491,7 +536,7 @@ r) {
|
|
|
491
536
|
if (S && i) {
|
|
492
537
|
r = n;
|
|
493
538
|
a = true;
|
|
494
|
-
} else if (e.T?.
|
|
539
|
+
} else if (e.T?.Ee === t) {
|
|
495
540
|
u = true;
|
|
496
541
|
handleError(n);
|
|
497
542
|
settleAutodispose();
|
|
@@ -506,8 +551,8 @@ r) {
|
|
|
506
551
|
return true;
|
|
507
552
|
}
|
|
508
553
|
if (l && !n.done) {
|
|
509
|
-
|
|
510
|
-
|
|
554
|
+
f = n.value;
|
|
555
|
+
s = true;
|
|
511
556
|
return iterate();
|
|
512
557
|
}
|
|
513
558
|
return l && n.done;
|
|
@@ -515,7 +560,7 @@ r) {
|
|
|
515
560
|
const l = iterate();
|
|
516
561
|
// Later iterate() calls run from asyncWrite, where rethrowing would be unhandled.
|
|
517
562
|
i = false;
|
|
518
|
-
return
|
|
563
|
+
return s || l;
|
|
519
564
|
};
|
|
520
565
|
// Landed-synchronously verdict for a LIVE iterator drain; null when no live
|
|
521
566
|
// drain ran (plain promise flight, or a deferred flatten). Drives the
|
|
@@ -534,16 +579,16 @@ r) {
|
|
|
534
579
|
});
|
|
535
580
|
}
|
|
536
581
|
if (!n) return false;
|
|
537
|
-
|
|
582
|
+
f = undefined;
|
|
538
583
|
const r = consumeIterator(e, t);
|
|
539
584
|
if (!t) u = r;
|
|
540
585
|
return true;
|
|
541
586
|
};
|
|
542
|
-
if (
|
|
543
|
-
let n = false, r = false, o,
|
|
587
|
+
if (s) {
|
|
588
|
+
let n = false, r = false, o, s = true;
|
|
544
589
|
t.then(r => {
|
|
545
|
-
if (
|
|
546
|
-
|
|
590
|
+
if (s) {
|
|
591
|
+
f = r;
|
|
547
592
|
n = true;
|
|
548
593
|
} else {
|
|
549
594
|
// Flattening consumes the stream as the value: each yield lands
|
|
@@ -556,7 +601,7 @@ r) {
|
|
|
556
601
|
// compute catch above it: a protocol throw settles as this
|
|
557
602
|
// flight's error (#4112).
|
|
558
603
|
try {
|
|
559
|
-
if (e.T?.
|
|
604
|
+
if (e.T?.Ee === t && !(e.R & REACTIVE_DISPOSED) && flattenIfIterable(r, e)) return;
|
|
560
605
|
asyncWrite(r);
|
|
561
606
|
} catch (e) {
|
|
562
607
|
handleError(e);
|
|
@@ -564,7 +609,7 @@ r) {
|
|
|
564
609
|
settleAutodispose();
|
|
565
610
|
}
|
|
566
611
|
}, e => {
|
|
567
|
-
if (
|
|
612
|
+
if (s) {
|
|
568
613
|
o = e;
|
|
569
614
|
r = true;
|
|
570
615
|
} else {
|
|
@@ -572,7 +617,7 @@ r) {
|
|
|
572
617
|
settleAutodispose();
|
|
573
618
|
}
|
|
574
619
|
});
|
|
575
|
-
|
|
620
|
+
s = false;
|
|
576
621
|
if (r) {
|
|
577
622
|
// Settle through the same status path an async rejection uses, then
|
|
578
623
|
// unwind the in-progress synchronous read so the errored node isn't
|
|
@@ -580,13 +625,13 @@ r) {
|
|
|
580
625
|
handleError(o);
|
|
581
626
|
throw o;
|
|
582
627
|
} else if (!n) {
|
|
583
|
-
//
|
|
584
|
-
// work on a loadingValue node is
|
|
585
|
-
// boundaries
|
|
586
|
-
// lands through asyncWrite.
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
} else if (!flattenIfIterable(
|
|
628
|
+
// Not landed: the shared tail below suspends, or — loading window —
|
|
629
|
+
// serves commit #0 (first-flight work on a loadingValue node is
|
|
630
|
+
// loading-class, invisible to boundaries; the flight itself is already
|
|
631
|
+
// registered in _inFlight and lands through asyncWrite). A promise is
|
|
632
|
+
// never an iterator, so nothing between here and there runs.
|
|
633
|
+
u = false;
|
|
634
|
+
} else if (!flattenIfIterable(f)) {
|
|
590
635
|
// Synchronously-resolved promise: the first real answer landed.
|
|
591
636
|
e.ce = false;
|
|
592
637
|
}
|
|
@@ -597,20 +642,20 @@ r) {
|
|
|
597
642
|
if (o) flattenIfIterable(t);
|
|
598
643
|
if (u !== null) {
|
|
599
644
|
if (!u) {
|
|
600
|
-
// Loading window: serve commit #0
|
|
601
|
-
if (e.ce) return e.
|
|
645
|
+
// Loading window: serve commit #0 instead of suspending.
|
|
646
|
+
if (e.ce) return e.le;
|
|
602
647
|
throw new NotReadyError(context);
|
|
603
648
|
}
|
|
604
649
|
// A sync first yield (or immediate empty completion) is the first real
|
|
605
650
|
// answer; async yields clear inside asyncWrite.
|
|
606
651
|
e.ce = false;
|
|
607
652
|
}
|
|
608
|
-
return
|
|
653
|
+
return f;
|
|
609
654
|
}
|
|
610
655
|
|
|
611
656
|
function clearStatus(e, t = false) {
|
|
612
|
-
if (e.T?.
|
|
613
|
-
if (e.T?.
|
|
657
|
+
if (e.T?.se) clearPendingSources(e);
|
|
658
|
+
if (e.T?.fe) e.T.fe = false;
|
|
614
659
|
e.m = t ? 0 : e.m & STATUS_UNINITIALIZED;
|
|
615
660
|
if (e.T?.F) setPendingError(e);
|
|
616
661
|
const n = statusNotifierOf(e);
|
|
@@ -642,34 +687,35 @@ function clearStatus(e, t = false) {
|
|
|
642
687
|
* reader thrown NotReady(self) would park on a retired entry. Companions
|
|
643
688
|
* keep their verdict (pending before and after; the write re-syncs them).
|
|
644
689
|
*/ function landStatus(e, t = false) {
|
|
645
|
-
const n = e.T?.
|
|
690
|
+
const n = e.T?.se;
|
|
646
691
|
// (The full clear below drops the set whether or not self was retired first.)
|
|
647
692
|
if (n && (n.delete(e), n.size)) {
|
|
648
|
-
e.T.
|
|
693
|
+
e.T.fe = false;
|
|
649
694
|
if (t) e.m = STATUS_PENDING;
|
|
650
695
|
setPendingError(e, n.values().next().value);
|
|
651
696
|
} else clearStatus(e, t);
|
|
652
697
|
}
|
|
653
698
|
|
|
654
|
-
function notifyStatus(e, t, n
|
|
699
|
+
function notifyStatus(e, t, n) {
|
|
655
700
|
// Wrap regular errors to track source node
|
|
656
701
|
if (t === STATUS_ERROR && !(n instanceof StatusError) && !(n instanceof NotReadyError)) n = new StatusError(e, n);
|
|
657
|
-
const
|
|
702
|
+
const r = t === STATUS_PENDING && n instanceof NotReadyError ? n.source : undefined;
|
|
658
703
|
// (CARVE 2: the lane assignment went with the engine.)
|
|
659
|
-
{
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
704
|
+
if (t === STATUS_PENDING && r) {
|
|
705
|
+
// D-41: a re-ask of a shown error keeps it the display (`_shownError`).
|
|
706
|
+
if (e.m & STATUS_ERROR) e.T.Te = e.T.F;
|
|
707
|
+
addPendingSource(e, r);
|
|
708
|
+
// A re-ask of an error that answered the first read: still no value.
|
|
709
|
+
e.m = STATUS_PENDING | (e.m & STATUS_NO_VALUE ? STATUS_UNINITIALIZED : e.m & STATUS_UNINITIALIZED);
|
|
710
|
+
// Preserve the current source on this propagation so readers park on
|
|
711
|
+
// the distinct pending source.
|
|
712
|
+
setPendingError(e, r, n);
|
|
713
|
+
} else {
|
|
714
|
+
clearPendingSources(e);
|
|
715
|
+
// An error answering a node with no value marks it so; a re-ask of it
|
|
716
|
+
// (pending) is uninitialized again; a write (STATUS_NONE) gives it one.
|
|
717
|
+
e.m = t | (t === STATUS_ERROR ? e.m & (STATUS_UNINITIALIZED | STATUS_NO_VALUE) ? STATUS_NO_VALUE : 0 : t === STATUS_PENDING && e.m & STATUS_NO_VALUE ? STATUS_UNINITIALIZED : e.m & STATUS_UNINITIALIZED);
|
|
718
|
+
ext(e).F = n;
|
|
673
719
|
}
|
|
674
720
|
// Lanes: a written guess is an optimistic boundary (A17 — "the optimistic
|
|
675
721
|
// future value until we know otherwise"). Its own source in flight is its
|
|
@@ -681,11 +727,9 @@ function notifyStatus(e, t, n, r) {
|
|
|
681
727
|
if (passLane === null) joinFuture(null);
|
|
682
728
|
return;
|
|
683
729
|
}
|
|
684
|
-
const
|
|
685
|
-
if (
|
|
686
|
-
|
|
687
|
-
f.call(e);
|
|
688
|
-
}
|
|
730
|
+
const o = statusNotifierOf(e);
|
|
731
|
+
if (o) {
|
|
732
|
+
o.call(e);
|
|
689
733
|
return;
|
|
690
734
|
}
|
|
691
735
|
propagateStatus(e, t, n);
|
|
@@ -695,10 +739,10 @@ function notifyStatus(e, t, n, r) {
|
|
|
695
739
|
* subscriber as a propagated mark (or a re-derive). On its own for a store
|
|
696
740
|
* family's leaves, which are not the derive's dependents — the family
|
|
697
741
|
* propagates the derive's status from each leaf to the leaf's readers
|
|
698
|
-
* (store/projection.ts `wakeFamily`), exactly as a memo's would reach its. */ function propagateStatus(e, t, n
|
|
699
|
-
const
|
|
700
|
-
forEachDependent(e, (
|
|
701
|
-
|
|
742
|
+
* (store/projection.ts `wakeFamily`), exactly as a memo's would reach its. */ function propagateStatus(e, t, n) {
|
|
743
|
+
const r = t === STATUS_PENDING && n instanceof NotReadyError ? n.source : undefined;
|
|
744
|
+
forEachDependent(e, (o, s) => {
|
|
745
|
+
o.ae = clock;
|
|
702
746
|
// A pending mark on a kept-tail link re-derives the subscriber instead of
|
|
703
747
|
// marking it (A30, #3494 review; fuzzer latest-1 #2141; #3519 review).
|
|
704
748
|
// Past `_depsTail` lie the committed frame's deps, kept by A30 because
|
|
@@ -718,8 +762,8 @@ function notifyStatus(e, t, n, r) {
|
|
|
718
762
|
// Inheriting the error would make a probe's reader errored. A boundary's
|
|
719
763
|
// output (CONFIG_REDERIVE, boundaries.ts) the same: content or fallback
|
|
720
764
|
// is its pass's call.
|
|
721
|
-
if (
|
|
722
|
-
enqueueSub(
|
|
765
|
+
if (o.l & (CONFIG_REDERIVE | CONFIG_VERDICT) || t === STATUS_PENDING && s.Ie !== o.Ne) {
|
|
766
|
+
enqueueSub(o);
|
|
723
767
|
schedule();
|
|
724
768
|
return;
|
|
725
769
|
}
|
|
@@ -730,30 +774,31 @@ function notifyStatus(e, t, n, r) {
|
|
|
730
774
|
// frame still passes through — its pass is the frame's (#3372, compiled
|
|
731
775
|
// shape: the unmount it stages makes the observer a zombie whose say is
|
|
732
776
|
// moot for this transaction, A15 #3463).
|
|
733
|
-
if (t === STATUS_PENDING &&
|
|
734
|
-
if (passLane === null && (
|
|
777
|
+
if (t === STATUS_PENDING && r && o.T?.se?.has(r)) {
|
|
778
|
+
if (passLane === null && (o.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && globalQueue.q && !freshHook?.(o, r.ae === clock)) joinFuture(txOf(o));
|
|
735
779
|
return;
|
|
736
780
|
}
|
|
737
|
-
if (t === STATUS_PENDING &&
|
|
781
|
+
if (t === STATUS_PENDING && r || t !== STATUS_PENDING && (o.T?.F !== n || o.T?.se)) {
|
|
738
782
|
// The marked subscriber is queued for the commit sweep so its status
|
|
739
783
|
// bookkeeping (loading window, uninitialized clear) runs with the flush
|
|
740
784
|
// — with the lane, when the pass propagating is the lane's work and
|
|
741
785
|
// the dependent's pending is the lane's own flight (`laneStage`).
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
if (t === STATUS_PENDING && (r.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && !r.i) joinFuture(txOf(r));
|
|
752
|
-
}
|
|
786
|
+
// (No answer: a pending propagation leaves a written guess a guess.)
|
|
787
|
+
if (laneStageHook && passLane !== null) laneStageHook(o, passLane, true, e); else {
|
|
788
|
+
queuePendingNode(o);
|
|
789
|
+
// A15 (#3443): pending propagates onto a held memo without
|
|
790
|
+
// recomputing it, and the propagation itself enters the memo's
|
|
791
|
+
// transaction — the flight flows into a memo that transaction
|
|
792
|
+
// holds, so the write that started it is held with it. A render
|
|
793
|
+
// effect's membership is its pass's (`notify`), never sticky.
|
|
794
|
+
if (t === STATUS_PENDING && (o.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && !o.i) joinFuture(txOf(o));
|
|
753
795
|
}
|
|
754
|
-
notifyStatus(
|
|
796
|
+
notifyStatus(o, t, n);
|
|
797
|
+
// A derived store's derive (store/projection.ts): marked, not re-run —
|
|
798
|
+
// its leaves' readers are not its dependents, so the family walks them.
|
|
799
|
+
o.fam?.st(t);
|
|
755
800
|
}
|
|
756
801
|
});
|
|
757
802
|
}
|
|
758
803
|
|
|
759
|
-
export { addPendingSource, clearStatus, forEachDependent, handleAsync, isThenable, notifyStatus, parkLoadingWindow, propagateStatus, releaseFlightTeardown, releaseIfSettledUnobserved, releaseSettledDependents, setPendingError, settleErroredDependents, settlePendingSource, stageError };
|
|
804
|
+
export { addPendingSource, clearStatus, forEachDependent, handleAsync, isThenable, notifyStatus, onSupersede, parkLoadingWindow, propagateStatus, releaseFlightTeardown, releaseIfSettledUnobserved, releaseSettledDependents, setPendingError, settleErroredDependents, settlePendingSource, stageError };
|