@solidjs/signals 2.0.0-rc.7 → 2.0.0-rc.9
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 +6357 -0
- package/dist/dev.attribution.js +2485 -0
- package/dist/dev.js +2851 -7027
- package/dist/observe/affects.js +129 -0
- package/dist/observe/attribution.js +7 -0
- package/dist/observe/boundaries.js +641 -0
- package/dist/observe/core/action.js +177 -0
- package/dist/observe/core/async.js +846 -0
- package/dist/observe/core/attribution-costs.js +66 -0
- package/dist/observe/core/attribution-feedback.js +282 -0
- package/dist/observe/core/attribution-hooks.js +82 -0
- package/dist/observe/core/attribution-queries.js +28 -0
- package/dist/observe/core/attribution.js +2095 -0
- package/dist/observe/core/constants.js +248 -0
- package/dist/{types-cjs/core/context.d.cts → observe/core/context.js} +37 -12
- package/dist/observe/core/core.js +1789 -0
- package/dist/observe/core/dev.js +293 -0
- package/dist/observe/core/effect.js +195 -0
- package/dist/observe/core/error-hooks.js +71 -0
- package/dist/observe/core/error.js +80 -0
- package/dist/observe/core/external.js +98 -0
- package/dist/observe/core/graph.js +155 -0
- package/dist/observe/core/heap.js +147 -0
- package/dist/observe/core/invariants.js +45 -0
- package/dist/observe/core/lanes.js +205 -0
- package/dist/observe/core/optimistic.js +585 -0
- package/dist/observe/core/owner.js +322 -0
- package/dist/observe/core/scheduler.js +1428 -0
- package/dist/observe/core/verdict.js +669 -0
- package/dist/observe/index.js +59 -0
- package/dist/observe/map.js +484 -0
- package/dist/{types-cjs/signals.d.cts → observe/signals.js} +311 -317
- package/dist/observe/store/index.js +40 -0
- package/dist/observe/store/next/optimistic.js +660 -0
- package/dist/observe/store/next/projection.js +255 -0
- package/dist/observe/store/next/reconcile.js +375 -0
- package/dist/observe/store/next/store.js +2327 -0
- package/dist/observe/store/next/target.js +51 -0
- package/dist/observe/store/store.js +325 -0
- package/dist/observe/store/storePath.js +103 -0
- package/dist/observe/store/utils.js +1023 -0
- package/dist/prod/attribution.js +41 -0
- package/dist/prod/boundaries.js +128 -76
- package/dist/prod/core/action.js +65 -37
- package/dist/prod/core/async.js +324 -172
- package/dist/prod/core/constants.js +57 -1
- package/dist/prod/core/context.js +3 -3
- package/dist/prod/core/core.js +1003 -364
- package/dist/prod/core/dev.js +17 -1
- package/dist/prod/core/effect.js +76 -42
- package/dist/prod/core/error-hooks.js +71 -0
- package/dist/prod/core/external.js +4 -4
- package/dist/prod/core/graph.js +37 -37
- package/dist/prod/core/heap.js +61 -56
- package/dist/prod/core/lanes.js +107 -63
- package/dist/prod/core/optimistic.js +374 -93
- package/dist/prod/core/owner.js +62 -46
- package/dist/prod/core/scheduler.js +653 -286
- package/dist/prod/core/verdict.js +245 -127
- package/dist/prod/index.js +15 -5
- package/dist/prod/map.js +292 -141
- package/dist/prod/signals.js +29 -16
- package/dist/prod/store/next/optimistic.js +77 -55
- package/dist/prod/store/next/projection.js +31 -20
- package/dist/prod/store/next/reconcile.js +87 -84
- package/dist/prod/store/next/store.js +718 -401
- package/dist/prod/store/next/target.js +32 -10
- package/dist/prod/store/store.js +12 -14
- package/dist/prod/store/utils.js +950 -130
- package/dist/types/attribution.d.ts +20 -0
- package/dist/types/attribution.prod.d.ts +22 -0
- package/dist/types/boundaries.d.ts +10 -1
- package/dist/types/core/action.d.ts +12 -5
- package/dist/types/core/async.d.ts +1 -1
- package/dist/types/core/attribution-costs.d.ts +35 -0
- package/dist/types/core/attribution-feedback.d.ts +133 -0
- package/dist/types/core/attribution-hooks.d.ts +147 -9
- package/dist/types/core/attribution-queries.d.ts +10 -0
- package/dist/types/core/attribution.d.ts +216 -182
- package/dist/types/core/constants.d.ts +56 -0
- package/dist/types/core/core.d.ts +190 -2
- package/dist/types/core/dev.d.ts +246 -28
- package/dist/types/core/effect.d.ts +8 -1
- package/dist/types/core/error-hooks.d.ts +71 -0
- package/dist/types/core/index.d.ts +4 -1
- package/dist/types/core/invariants.d.ts +4 -0
- package/dist/types/core/lanes.d.ts +39 -5
- package/dist/types/core/scheduler.d.ts +153 -0
- package/dist/types/core/types.d.ts +32 -9
- package/dist/types/index.d.ts +10 -3
- package/dist/types/map.d.ts +2 -0
- package/dist/types/signals.d.ts +8 -0
- package/dist/types/store/index.d.ts +2 -0
- package/dist/types/store/next/optimistic.d.ts +1 -1
- package/dist/types/store/next/store.d.ts +11 -5
- package/dist/types/store/next/target.d.ts +37 -10
- package/dist/types/store/utils.d.ts +178 -0
- package/package.json +21 -17
- package/dist/node.cjs +0 -10541
- package/dist/node.dev.cjs +0 -13724
- package/dist/types-cjs/affects.d.cts +0 -47
- package/dist/types-cjs/boundaries.d.cts +0 -175
- package/dist/types-cjs/core/action.d.cts +0 -64
- package/dist/types-cjs/core/async.d.cts +0 -23
- package/dist/types-cjs/core/attribution-hooks.d.cts +0 -127
- package/dist/types-cjs/core/attribution.d.cts +0 -495
- package/dist/types-cjs/core/constants.d.cts +0 -159
- package/dist/types-cjs/core/core.d.cts +0 -185
- package/dist/types-cjs/core/dev.d.cts +0 -136
- package/dist/types-cjs/core/effect.d.cts +0 -30
- package/dist/types-cjs/core/error.d.cts +0 -56
- package/dist/types-cjs/core/external.d.cts +0 -15
- package/dist/types-cjs/core/graph.d.cts +0 -28
- package/dist/types-cjs/core/heap.d.cts +0 -24
- package/dist/types-cjs/core/index.d.cts +0 -13
- package/dist/types-cjs/core/invariants.d.cts +0 -59
- package/dist/types-cjs/core/lanes.d.cts +0 -54
- package/dist/types-cjs/core/optimistic.d.cts +0 -6
- package/dist/types-cjs/core/owner.d.cts +0 -124
- package/dist/types-cjs/core/scheduler.d.cts +0 -236
- package/dist/types-cjs/core/types.d.cts +0 -204
- package/dist/types-cjs/core/verdict.d.cts +0 -2
- package/dist/types-cjs/index.d.cts +0 -10
- package/dist/types-cjs/map.d.cts +0 -69
- package/dist/types-cjs/package.json +0 -3
- package/dist/types-cjs/store/index.d.cts +0 -18
- package/dist/types-cjs/store/next/optimistic.d.cts +0 -25
- package/dist/types-cjs/store/next/projection.d.cts +0 -8
- package/dist/types-cjs/store/next/reconcile.d.cts +0 -10
- package/dist/types-cjs/store/next/store.d.cts +0 -121
- package/dist/types-cjs/store/next/target.d.cts +0 -166
- package/dist/types-cjs/store/store.d.cts +0 -143
- package/dist/types-cjs/store/storePath.d.cts +0 -58
- package/dist/types-cjs/store/utils.d.cts +0 -74
package/dist/prod/core/async.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { STATUS_UNINITIALIZED, CONFIG_CHILD_COMPANIONS, STATUS_ERROR, STATUS_PENDING, REACTIVE_DIRTY, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING,
|
|
1
|
+
import { STATUS_UNINITIALIZED, CONFIG_CHILD_COMPANIONS, STATUS_ERROR, STATUS_PENDING, CONFIG_DERIVED_OVERRIDE, CONFIG_INPUTS_PUBLISHED, REACTIVE_DIRTY, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING, unwrapOverride, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE, REACTIVE_DISPOSED } from "./constants.js";
|
|
2
2
|
|
|
3
|
-
import { untrack, ext, statusNotifierOf, setSignal, context } from "./core.js";
|
|
3
|
+
import { untrack, ext, statusNotifierOf, hasActiveOverride, setSignal, context } from "./core.js";
|
|
4
4
|
|
|
5
5
|
import "./invariants.js";
|
|
6
6
|
|
|
@@ -10,11 +10,11 @@ import { trimStaleDeps, unobserved } from "./graph.js";
|
|
|
10
10
|
|
|
11
11
|
import { enqueueSub } from "./heap.js";
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import { assignOrMergeLane, resolveLane, resolveTransition } from "./lanes.js";
|
|
14
14
|
|
|
15
15
|
import { cleanup } from "./owner.js";
|
|
16
16
|
|
|
17
|
-
import { GlobalQueue, schedule, flush, queuePendingNode, insertSubs, clock, currentTransition, globalQueue } from "./scheduler.js";
|
|
17
|
+
import { GlobalQueue, schedule, flush, queuePendingNode, insertSubs, clock, waitingTransition, currentTransition, globalQueue, enterWaiting, setOrigin, origin } 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,35 @@ import { GlobalQueue, schedule, flush, queuePendingNode, insertSubs, clock, curr
|
|
|
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, n) {
|
|
25
|
-
if (e.o?.
|
|
26
|
-
(ext(e).
|
|
25
|
+
if (e.o?.ae?.has(n)) return false;
|
|
26
|
+
(ext(e).ae ??= new Set).add(n);
|
|
27
27
|
return true;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
function removePendingSource(e, n) {
|
|
31
|
-
const t = e.o?.
|
|
31
|
+
const t = e.o?.ae;
|
|
32
32
|
if (!t?.delete(n)) return false;
|
|
33
|
-
if (!t.size) e.o.
|
|
33
|
+
if (!t.size) e.o.ae = 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.o !== null) e.o.
|
|
40
|
+
if (e.o !== null) e.o.ae = undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// A rejection-pending only resolves through the settle sweep over the
|
|
44
|
+
// SOURCE's subscribers, so it is retryable iff a tracked read created that
|
|
45
|
+
// edge: a dep that IS the source, or one whose own pending chain carries it
|
|
46
|
+
// (pending sources propagate the origin node, so this covers any depth).
|
|
47
|
+
// Also guards branch-local recovery: another dependency may still need the source.
|
|
48
|
+
function retryReaches(e, n) {
|
|
49
|
+
for (let t = e.Se; t; t = t.de) {
|
|
50
|
+
const e = t.Ee.Te || t.Ee;
|
|
51
|
+
if (e === n || e.o?.ae?.has(n)) return true;
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
41
54
|
}
|
|
42
55
|
|
|
43
56
|
/**
|
|
@@ -47,7 +60,7 @@ function clearPendingSources(e) {
|
|
|
47
60
|
* with NO read-visible pending status, no downstream propagation, no
|
|
48
61
|
* transition, no lane registration. Commit #0 keeps serving.
|
|
49
62
|
*/ function parkLoadingWindow(e, n) {
|
|
50
|
-
ext(e).
|
|
63
|
+
ext(e).Ie = true;
|
|
51
64
|
if (n.source) addPendingSource(e, n.source);
|
|
52
65
|
// A settled error is the node's answer ("the error stays the answer until
|
|
53
66
|
// this retry can actually run") — the park must not replace it: reads
|
|
@@ -72,10 +85,10 @@ function setPendingError(e, n, t) {
|
|
|
72
85
|
}
|
|
73
86
|
|
|
74
87
|
function forEachDependent(e, n) {
|
|
75
|
-
for (let t = e.u; t !== null; t = t.
|
|
88
|
+
for (let t = e.u; t !== null; t = t.Ne) n(t._e, t);
|
|
76
89
|
// `?? null`: affects() marks route plain signals (no `_child` slot) through here.
|
|
77
|
-
for (let t = e.o?.i ?? null; t !== null; t = t.
|
|
78
|
-
for (let e = t.u; e !== null; e = e.
|
|
90
|
+
for (let t = e.o?.i ?? null; t !== null; t = t.De) {
|
|
91
|
+
for (let e = t.u; e !== null; e = e.Ne) n(e._e, e);
|
|
79
92
|
}
|
|
80
93
|
}
|
|
81
94
|
|
|
@@ -90,7 +103,7 @@ function forEachDependent(e, n) {
|
|
|
90
103
|
// callbacks handle their own release (settleAutodispose in handleAsync); this
|
|
91
104
|
// covers derivatively-pending dependents, which have no callbacks of their own.
|
|
92
105
|
function releaseIfSettledUnobserved(e) {
|
|
93
|
-
e.
|
|
106
|
+
e.ce && e.T & CONFIG_AUTO_DISPOSE && !e.u && !(e.ue & REACTIVE_ZOMBIE) && !(e.S & STATUS_PENDING) && unobserved(e);
|
|
94
107
|
}
|
|
95
108
|
|
|
96
109
|
// Error-path sweep: notifyStatus(STATUS_ERROR) clears dependents' pending
|
|
@@ -119,9 +132,9 @@ function releaseSettledDependents(e) {
|
|
|
119
132
|
// object identity down the whole dependent tree, and holding it is exactly
|
|
120
133
|
// the "blocked on this error" marker — re-enqueue those holders so they
|
|
121
134
|
// re-run: fresh values commit and flow, and a dependent with another
|
|
122
|
-
// still-broken source simply re-errors.
|
|
123
|
-
//
|
|
124
|
-
//
|
|
135
|
+
// still-broken source simply re-errors. Pending recovery uses
|
|
136
|
+
// settlePendingSource to clear inherited status and retry blocked readers.
|
|
137
|
+
// Walks the full dependent graph
|
|
125
138
|
// (releaseSettledDependents shape): identity holders can sit below an
|
|
126
139
|
// intermediate whose own error state has since been scrubbed or replaced
|
|
127
140
|
// (e.g. an error boundary's tree node).
|
|
@@ -141,51 +154,63 @@ function settleErroredDependents(e, n) {
|
|
|
141
154
|
if (t) schedule();
|
|
142
155
|
}
|
|
143
156
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
157
|
+
// Retire `source` from pending state along the dependent graph rooted at `el`.
|
|
158
|
+
// By default, `el` is the source whose flight settled or was superseded.
|
|
159
|
+
// With a distinct `source`, `el` is a recovered computation that dropped it:
|
|
160
|
+
// the source may still be pending, so dependents with another path to it stay pending.
|
|
161
|
+
function settlePendingSource(e, n = e) {
|
|
162
|
+
// Landing and branch recovery already cleared el's own set. Superseded
|
|
163
|
+
// re-parks can retain an abandoned self entry (source === el), which must
|
|
164
|
+
// retire in the same walk as its propagated copies.
|
|
165
|
+
removePendingSource(e, n);
|
|
166
|
+
let t = false;
|
|
167
|
+
let r;
|
|
168
|
+
const i = new Set;
|
|
152
169
|
// Companion updates no-op without the verdict layer (null hook).
|
|
153
|
-
const o = GlobalQueue.
|
|
154
|
-
const settle =
|
|
155
|
-
if (
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
170
|
+
const o = GlobalQueue.Oe;
|
|
171
|
+
const settle = s => {
|
|
172
|
+
if (i.has(s)) return;
|
|
173
|
+
// A conditional dropped this source, but another dependency can still
|
|
174
|
+
// carry it. Only retire pending state inherited through the recovered
|
|
175
|
+
// branch. Deliberately NOT marked visited on this early return: the
|
|
176
|
+
// carrying dependency may itself be a later branch of this same walk
|
|
177
|
+
// (two unchanged memos converging), and its visit must be free to
|
|
178
|
+
// re-examine this node once that branch has retired the source.
|
|
179
|
+
if (n !== e && retryReaches(s, n)) return;
|
|
180
|
+
if (!removePendingSource(s, n)) return;
|
|
181
|
+
i.add(s);
|
|
182
|
+
s.Pe = clock;
|
|
183
|
+
const u = s.o?.ae?.values().next().value;
|
|
159
184
|
// STATUS_ERROR + pending sources only coexist via an errored loading
|
|
160
185
|
// window's park (notifyStatus(STATUS_ERROR) clears pending sources
|
|
161
186
|
// otherwise): the settled error stays the answer through the settle —
|
|
162
187
|
// nulling it here would have reads throw `null` until the re-enqueued
|
|
163
188
|
// retry lands, or lose it entirely if that retry parks again (#2989).
|
|
164
|
-
const
|
|
165
|
-
if (
|
|
166
|
-
if (!
|
|
167
|
-
o?.(
|
|
189
|
+
const l = s.S & STATUS_ERROR;
|
|
190
|
+
if (u) {
|
|
191
|
+
if (!l) setPendingError(s, u);
|
|
192
|
+
o?.(s);
|
|
168
193
|
} else {
|
|
169
|
-
|
|
170
|
-
if (!
|
|
171
|
-
o?.(
|
|
172
|
-
if (
|
|
173
|
-
enqueueSub(
|
|
174
|
-
|
|
194
|
+
s.S &= ~STATUS_PENDING;
|
|
195
|
+
if (!l) setPendingError(s);
|
|
196
|
+
o?.(s);
|
|
197
|
+
if (s.o?.Ie) {
|
|
198
|
+
enqueueSub(s);
|
|
199
|
+
t = true;
|
|
175
200
|
}
|
|
176
|
-
if (
|
|
201
|
+
if (s.o !== null) s.o.Ie = false;
|
|
177
202
|
// Fully settled with nobody watching: release candidate (#2934). Checked
|
|
178
203
|
// again at release time — deferred so unobserved() can't unlink subs
|
|
179
204
|
// lists this walk is still iterating.
|
|
180
|
-
if (!
|
|
205
|
+
if (!s.u && s.T & CONFIG_AUTO_DISPOSE) (r ??= []).push(s);
|
|
181
206
|
}
|
|
182
|
-
forEachDependent(
|
|
207
|
+
forEachDependent(s, settle);
|
|
183
208
|
};
|
|
184
209
|
forEachDependent(e, settle);
|
|
185
210
|
// Release before the flush schedule below: unobserved() pulls the node back
|
|
186
211
|
// out of the heap, so the enqueueSub above never recomputes a released node.
|
|
187
|
-
if (
|
|
188
|
-
if (
|
|
212
|
+
if (r) for (const e of r) releaseIfSettledUnobserved(e);
|
|
213
|
+
if (t) schedule();
|
|
189
214
|
}
|
|
190
215
|
|
|
191
216
|
// Object-thenable detection (Promises/A+ shape).
|
|
@@ -194,26 +219,26 @@ function isThenable(e) {
|
|
|
194
219
|
}
|
|
195
220
|
|
|
196
221
|
/** Fire and clear a node's iterator-flight cancellation hook (#3122). */ function releaseFlightTeardown(e) {
|
|
197
|
-
const n = e.o?.
|
|
222
|
+
const n = e.o?.Ae;
|
|
198
223
|
if (n != null) {
|
|
199
|
-
e.o.
|
|
224
|
+
e.o.Ae = null;
|
|
200
225
|
n();
|
|
201
226
|
}
|
|
202
227
|
}
|
|
203
228
|
|
|
204
229
|
function handleAsync(e, n, t) {
|
|
205
230
|
let r = false;
|
|
206
|
-
let
|
|
231
|
+
let i = false;
|
|
207
232
|
if (typeof n === "object" && n !== null) {
|
|
208
233
|
untrack(() => {
|
|
209
234
|
r = n[Symbol.asyncIterator];
|
|
210
|
-
|
|
235
|
+
i = !r && isThenable(n);
|
|
211
236
|
});
|
|
212
237
|
}
|
|
213
|
-
if (!
|
|
214
|
-
if (e.o !== null) e.o.
|
|
238
|
+
if (!i && !r) {
|
|
239
|
+
if (e.o !== null) e.o.Re = null;
|
|
215
240
|
// A sync landing is the first real answer for a loadingValue node.
|
|
216
|
-
e.
|
|
241
|
+
e.ge = false;
|
|
217
242
|
return n;
|
|
218
243
|
}
|
|
219
244
|
// Flight replacement relies on recompute's supersede release for iterator
|
|
@@ -221,8 +246,20 @@ function handleAsync(e, n, t) {
|
|
|
221
246
|
// self-registration — runs during a recompute of `el`, which has already
|
|
222
247
|
// fired _flightTeardown. A future non-recompute registration path must
|
|
223
248
|
// release it here before overwriting _inFlight.
|
|
224
|
-
ext(e).
|
|
225
|
-
|
|
249
|
+
ext(e).Re = n;
|
|
250
|
+
// The run that asked this flight read every input without throwing: an
|
|
251
|
+
// input still in flight was masked for it (an active override, A17), so
|
|
252
|
+
// pending state those inputs propagated onto the node earlier does not
|
|
253
|
+
// describe this answer. Drop it — the flight is the node's pending now.
|
|
254
|
+
// The landing retires only the flight's own entry (landStatus, #3373), so
|
|
255
|
+
// an entry that survived here would hold the node past its own answer.
|
|
256
|
+
e.o.ae = undefined;
|
|
257
|
+
// Provenance of the question this flight asks (#3331): the action whose
|
|
258
|
+
// window is registering it, or the flight whose landing is. Its landings
|
|
259
|
+
// propagate under it (asyncWrite) so an override downstream can tell a
|
|
260
|
+
// stale answer from its own.
|
|
261
|
+
const o = origin;
|
|
262
|
+
let s;
|
|
226
263
|
// Settle-time transition re-entry. The loading rail is invisible to
|
|
227
264
|
// transactions (#2933): a boundary-caught first load never registers as an
|
|
228
265
|
// async reporter, so its settle — the boundary's fallback -> content
|
|
@@ -234,27 +271,41 @@ function handleAsync(e, n, t) {
|
|
|
234
271
|
// keeps transition scheduling; initialized (value-holding) pending settles
|
|
235
272
|
// are the transaction's reveal machinery and always re-enter.
|
|
236
273
|
const settleTransition = () => {
|
|
237
|
-
|
|
238
|
-
|
|
274
|
+
let n = resolveTransition(e);
|
|
275
|
+
// A lane-routed node's landing is revealed by its lane, ahead of the
|
|
276
|
+
// transaction that owns the lane (whose own commit is only the override's
|
|
277
|
+
// confirm/revert). Entering that owner here would fold every transaction
|
|
278
|
+
// waiting on this flight into it at the landing — a reveal that
|
|
279
|
+
// discovered the flight (#3305) would then wait on the owner's action
|
|
280
|
+
// instead of on the flight (#3334). Enter the waiter: the transaction
|
|
281
|
+
// whose blocker this landing clears. Then every transaction waiting on
|
|
282
|
+
// the flight folds in (enterWaiting): a reveal that discovered it
|
|
283
|
+
// completes at its landing (A15) — a stampless node's fresh batch
|
|
284
|
+
// included (its flight started under a batch that committed beneath it,
|
|
285
|
+
// #3305). The fold used to happen as each stamped reader recomputed,
|
|
286
|
+
// which effects no longer do (#3407).
|
|
287
|
+
if (e.o?.Ue) n = waitingTransition(e) ?? n;
|
|
288
|
+
if (n && e.S & STATUS_UNINITIALIZED && !currentTransition(n).oe.has(e)) {
|
|
239
289
|
// Drop the stale stamp too: the plain settle write (setSignal) and the
|
|
240
290
|
// stash-path restamp both re-enter the transaction through it.
|
|
241
|
-
e.
|
|
291
|
+
e.Ge = null;
|
|
242
292
|
return;
|
|
243
293
|
}
|
|
244
294
|
globalQueue.initTransition(n);
|
|
295
|
+
enterWaiting(e);
|
|
245
296
|
};
|
|
246
297
|
const handleError = t => {
|
|
247
|
-
if (e.o?.
|
|
298
|
+
if (e.o?.Re !== n) return;
|
|
248
299
|
// NotReadyError from rejected promises should be treated as pending, not error
|
|
249
300
|
let r = t instanceof NotReadyError;
|
|
250
|
-
if (r && e.
|
|
301
|
+
if (r && e.ge) {
|
|
251
302
|
// Loading window: the flight died waiting on an unready source. Keep
|
|
252
303
|
// serving commit #0 — same parking as recompute's catch for sync
|
|
253
304
|
// dependency throws. The dead flight is released so the clock-gated
|
|
254
305
|
// error-retry pull (updateIfNecessary) can also re-ask.
|
|
255
|
-
if (e.o !== null) e.o.
|
|
306
|
+
if (e.o !== null) e.o.Re = null;
|
|
256
307
|
parkLoadingWindow(e, t);
|
|
257
|
-
e.
|
|
308
|
+
e.Pe = clock;
|
|
258
309
|
return;
|
|
259
310
|
}
|
|
260
311
|
settleTransition();
|
|
@@ -263,32 +314,34 @@ function handleAsync(e, n, t) {
|
|
|
263
314
|
// rejected flight will never settle its self entry, so transfer ownership
|
|
264
315
|
// after notifyStatus has propagated the replacement source.
|
|
265
316
|
if (r) settlePendingSource(e);
|
|
266
|
-
e.
|
|
317
|
+
e.Pe = clock;
|
|
267
318
|
// A real error settles derivatively-pending dependents (notifyStatus
|
|
268
319
|
// cleared their pending sources), so stranded lazy ones release here —
|
|
269
320
|
// the error twin of settlePendingSource's release (#2934).
|
|
270
321
|
if (!r) releaseSettledDependents(e);
|
|
271
322
|
};
|
|
272
|
-
const asyncWrite = (r,
|
|
273
|
-
if (e.o?.
|
|
323
|
+
const asyncWrite = (r, i) => {
|
|
324
|
+
if (e.o?.Re !== n) return;
|
|
274
325
|
// If the node was dirtied by a newer write (optimistic override or regular),
|
|
275
326
|
// skip this stale async result — the upcoming flush will recompute the node
|
|
276
327
|
// with the new value, creating a fresh Promise that supersedes this one.
|
|
277
|
-
if (e.
|
|
328
|
+
if (e.ue & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY)) return;
|
|
329
|
+
// The landing propagates under the flight's provenance (#3331) — through
|
|
330
|
+
// the flush below, which clears it.
|
|
331
|
+
setOrigin(o);
|
|
278
332
|
settleTransition();
|
|
279
|
-
const
|
|
333
|
+
const s = !!(e.S & STATUS_UNINITIALIZED);
|
|
280
334
|
// Captured before clearStatus wipes it: a quiet re-ask's landing may be
|
|
281
335
|
// transition-held below, and the displayed value keeps answering the same
|
|
282
336
|
// question until the hold commits — the classification must survive to
|
|
283
337
|
// that reveal or companion synchronization briefly classifies the held
|
|
284
338
|
// old value as pending, a one-frame pulse to direct observers (#3178).
|
|
285
339
|
// A truthy capture implies `_x` exists, so the restore writes it directly.
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
if (s) s.Oe.delete(e);
|
|
340
|
+
const u = e.o?.be;
|
|
341
|
+
landStatus(e);
|
|
342
|
+
if (u) e.o.be = true;
|
|
343
|
+
const l = resolveLane(e);
|
|
344
|
+
if (l) l.ye.delete(e);
|
|
292
345
|
if (t) {
|
|
293
346
|
try {
|
|
294
347
|
t(r);
|
|
@@ -296,8 +349,12 @@ function handleAsync(e, n, t) {
|
|
|
296
349
|
handleError(e);
|
|
297
350
|
return;
|
|
298
351
|
}
|
|
299
|
-
if (
|
|
300
|
-
} else if (e.o?.
|
|
352
|
+
if (s) landStatus(e, true);
|
|
353
|
+
} else if (e.o?.Ce !== undefined && !(l && e.T & CONFIG_DERIVED_OVERRIDE)) {
|
|
354
|
+
// A derived override's landing UNDER its lane is the lane's own work
|
|
355
|
+
// (the branch below); demoted — its source superseded (A18) — the
|
|
356
|
+
// landing is the truth the correction asked for, and holds and
|
|
357
|
+
// supersedes here like the sync twin (recompute). Otherwise:
|
|
301
358
|
// Optimistic node — resting OR covered by an active override — holds
|
|
302
359
|
// through the shared pending-node path, exactly like a plain async memo,
|
|
303
360
|
// so the commit clears STATUS_UNINITIALIZED (#2806) and elevation to
|
|
@@ -307,8 +364,8 @@ function handleAsync(e, n, t) {
|
|
|
307
364
|
// (A17 — every reader sees the override); the revert reveals whatever
|
|
308
365
|
// has committed by then, so corrections reveal atomically with their
|
|
309
366
|
// transition rather than escaping it.
|
|
310
|
-
if (e.
|
|
311
|
-
e.
|
|
367
|
+
if (e.ve === NOT_PENDING) queuePendingNode(e);
|
|
368
|
+
e.ve = r;
|
|
312
369
|
// The hold is a companion-visible write like any other (A13/A19): the
|
|
313
370
|
// clearStatus() above computed its verdict before the hold existed, so
|
|
314
371
|
// isPending must re-derive (the value is not final until commit — V1)
|
|
@@ -316,35 +373,37 @@ function handleAsync(e, n, t) {
|
|
|
316
373
|
// only notified when the hold is visible to them: under an active
|
|
317
374
|
// override every reader sees the override (A17), so waking subs would
|
|
318
375
|
// re-show an unchanged view — the revert is the notification point.
|
|
319
|
-
|
|
376
|
+
// Under an override the landing is handed to the engine's
|
|
377
|
+
// supersedeOverride (A18 supersession, #3331): own-source truth that
|
|
378
|
+
// differs from the override ends the optimism for the graph now (plain
|
|
379
|
+
// channel, lane demoted); a matching arrival is silent except to an
|
|
380
|
+
// authoritative-view reader (until()'s predicate) waiting on exactly
|
|
381
|
+
// this staged truth (#3164 — without the wake the hold deadlocks: the
|
|
382
|
+
// landing waits on the transaction, the transaction on the action, the
|
|
383
|
+
// action on an until() never re-notified). The hook is installed with
|
|
384
|
+
// the engine, which an active override implies. The propagation runs
|
|
385
|
+
// under this flight's provenance (setOrigin above).
|
|
386
|
+
GlobalQueue.me?.(e, r);
|
|
320
387
|
if (!hasActiveOverride(e)) {
|
|
321
388
|
insertSubs(e);
|
|
322
|
-
} else
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
// and is waiting for exactly this staged truth. Without the wake the
|
|
326
|
-
// hold deadlocks: the landing waits on the transaction, the
|
|
327
|
-
// transaction on the action, the action on an until() that was never
|
|
328
|
-
// re-notified (#3164). Same selective wake as the equal-landing
|
|
329
|
-
// branch in recompute(). Optional call: the bit implies the
|
|
330
|
-
// optimistic engine WAS consulted, but the hook only installs with
|
|
331
|
-
// it — a bare-core build must not crash here.
|
|
332
|
-
GlobalQueue.he?.(e);
|
|
333
|
-
}
|
|
334
|
-
e.Te = clock;
|
|
335
|
-
} else if (s) {
|
|
389
|
+
} else GlobalQueue.we(e, r);
|
|
390
|
+
e.Pe = clock;
|
|
391
|
+
} else if (l) {
|
|
336
392
|
// Route through lane's effect queue for independent flushing
|
|
337
|
-
const n = e.
|
|
338
|
-
const t = e.
|
|
339
|
-
const
|
|
393
|
+
const n = e.Le;
|
|
394
|
+
const t = hasActiveOverride(e) ? unwrapOverride(e.o.Ce) : e.Qe;
|
|
395
|
+
const i = e.Fe;
|
|
340
396
|
try {
|
|
341
|
-
if (!n &&
|
|
342
|
-
|
|
343
|
-
|
|
397
|
+
if (!n && s || !i || !i(r, t)) {
|
|
398
|
+
// Lanes stage (#3479): a memo's landing under its lane is a derived
|
|
399
|
+
// override, as its sync pass's result is (recompute) — `_value`
|
|
400
|
+
// stays the committed truth for readers off the lane.
|
|
401
|
+
if (n) e.Qe = r; else GlobalQueue.Ve(e, r, l);
|
|
402
|
+
e.Pe = clock;
|
|
344
403
|
// The latest() shadow write gives latest() effects independent lanes; the
|
|
345
404
|
// _pendingSignal update is a no-op repeat of the clearStatus() call above
|
|
346
405
|
// (computePendingState doesn't read _value).
|
|
347
|
-
GlobalQueue.
|
|
406
|
+
GlobalQueue.me?.(e, r);
|
|
348
407
|
insertSubs(e, true);
|
|
349
408
|
}
|
|
350
409
|
} catch (n) {
|
|
@@ -370,14 +429,23 @@ function handleAsync(e, n, t) {
|
|
|
370
429
|
// window when the hold commits, so no one-frame isPending pulse can leak
|
|
371
430
|
// to live observers between the landing and its commit (#2990). The
|
|
372
431
|
// quiet re-ask classification follows the same schedule (#3178).
|
|
373
|
-
if (e.
|
|
374
|
-
e.
|
|
375
|
-
if (
|
|
432
|
+
if (e.ve === NOT_PENDING) {
|
|
433
|
+
e.ge = false;
|
|
434
|
+
if (u) e.o.be = false;
|
|
435
|
+
// The landing published: the dependency tail the flight's pass left
|
|
436
|
+
// linked goes now (A30, #3410). A transition-held landing has not
|
|
437
|
+
// replaced the committed frame — the committed value still derives
|
|
438
|
+
// from the previous pass's inputs, and a mainline write to one of them
|
|
439
|
+
// must reach this node and join its hold (its stamp) instead of
|
|
440
|
+
// publishing beside the stale derivation (#3461: `b() ? b() : a()`
|
|
441
|
+
// held on `b` dropped `a` at its landing, and `A: 1` then committed
|
|
442
|
+
// beside `Selected: 0`). `commitPendingNode` trims a held landing.
|
|
443
|
+
trimStaleDeps(e);
|
|
376
444
|
}
|
|
377
445
|
settlePendingSource(e);
|
|
378
446
|
schedule();
|
|
379
447
|
flush();
|
|
380
|
-
|
|
448
|
+
i?.();
|
|
381
449
|
};
|
|
382
450
|
// A pending node's in-flight promise is an observer: `unlinkSubs` skips
|
|
383
451
|
// autodispose while STATUS_PENDING so subscriber churn can't orphan the
|
|
@@ -405,15 +473,15 @@ function handleAsync(e, n, t) {
|
|
|
405
473
|
// Returns whether a sync answer landed (first yield or empty completion) —
|
|
406
474
|
// meaningful only in the live posture.
|
|
407
475
|
const consumeIterator = (t, r) => {
|
|
408
|
-
const
|
|
409
|
-
let
|
|
410
|
-
let
|
|
411
|
-
let
|
|
476
|
+
const i = t[Symbol.asyncIterator]();
|
|
477
|
+
let o = false;
|
|
478
|
+
let u = false;
|
|
479
|
+
let l = !r;
|
|
412
480
|
const close = () => {
|
|
413
|
-
if (
|
|
414
|
-
|
|
481
|
+
if (u) return;
|
|
482
|
+
u = true;
|
|
415
483
|
try {
|
|
416
|
-
const e =
|
|
484
|
+
const e = i.return?.();
|
|
417
485
|
if (isThenable(e)) e.then(undefined, () => {});
|
|
418
486
|
} catch {}
|
|
419
487
|
};
|
|
@@ -422,7 +490,7 @@ function handleAsync(e, n, t) {
|
|
|
422
490
|
// owner-death backstop, but its disposal list can be zombie-deferred
|
|
423
491
|
// until the SUPERSEDING flight settles. The teardown slot fires at the
|
|
424
492
|
// _inFlight release sites so supersede stops this stream immediately.
|
|
425
|
-
ext(e).
|
|
493
|
+
ext(e).Ae = close;
|
|
426
494
|
// Release check before each next pull: an unobserved lazy node must tear
|
|
427
495
|
// down (its close above runs via disposal, closing the iterator) instead
|
|
428
496
|
// of pumping the stream forever with zero subscribers (#2935).
|
|
@@ -436,7 +504,7 @@ function handleAsync(e, n, t) {
|
|
|
436
504
|
// use the bare form as a promise-free fast path when a value is already
|
|
437
505
|
// buffered (seroval's deserialized streams do), so a bare result is
|
|
438
506
|
// assimilated as an already-settled step instead of crashing on `.then`.
|
|
439
|
-
const S =
|
|
507
|
+
const S = i.next();
|
|
440
508
|
const d = isThenable(S) ? S : {
|
|
441
509
|
then: e => void e(S)
|
|
442
510
|
};
|
|
@@ -448,18 +516,18 @@ function handleAsync(e, n, t) {
|
|
|
448
516
|
// stash: it must write through the async path or the value is
|
|
449
517
|
// silently dropped. (The deferred posture never has a caller, so
|
|
450
518
|
// initialRead starts false there and everything writes through.)
|
|
451
|
-
if (c &&
|
|
519
|
+
if (c && l) {
|
|
452
520
|
t = r;
|
|
453
521
|
f = true;
|
|
454
|
-
if (r.done)
|
|
455
|
-
} else if (e.o?.
|
|
522
|
+
if (r.done) u = true;
|
|
523
|
+
} else if (e.o?.Re !== n) {
|
|
456
524
|
return;
|
|
457
525
|
} else if (!r.done) {
|
|
458
|
-
|
|
526
|
+
o = true;
|
|
459
527
|
asyncWrite(r.value, iterateOrRelease);
|
|
460
528
|
} else {
|
|
461
|
-
|
|
462
|
-
if (
|
|
529
|
+
u = true;
|
|
530
|
+
if (o) {
|
|
463
531
|
schedule();
|
|
464
532
|
flush();
|
|
465
533
|
} else {
|
|
@@ -469,11 +537,11 @@ function handleAsync(e, n, t) {
|
|
|
469
537
|
settleAutodispose();
|
|
470
538
|
}
|
|
471
539
|
}, t => {
|
|
472
|
-
if (c &&
|
|
540
|
+
if (c && l) {
|
|
473
541
|
r = t;
|
|
474
542
|
a = true;
|
|
475
|
-
} else if (e.o?.
|
|
476
|
-
|
|
543
|
+
} else if (e.o?.Re === n) {
|
|
544
|
+
u = true;
|
|
477
545
|
handleError(t);
|
|
478
546
|
settleAutodispose();
|
|
479
547
|
}
|
|
@@ -481,27 +549,27 @@ function handleAsync(e, n, t) {
|
|
|
481
549
|
c = false;
|
|
482
550
|
if (a) {
|
|
483
551
|
// Match the promise branch, but only rethrow during the initial read.
|
|
484
|
-
|
|
552
|
+
u = true;
|
|
485
553
|
handleError(r);
|
|
486
|
-
if (
|
|
554
|
+
if (l) throw r;
|
|
487
555
|
return true;
|
|
488
556
|
}
|
|
489
557
|
if (f && !t.done) {
|
|
490
|
-
|
|
491
|
-
|
|
558
|
+
s = t.value;
|
|
559
|
+
o = true;
|
|
492
560
|
return iterate();
|
|
493
561
|
}
|
|
494
562
|
return f && t.done;
|
|
495
563
|
};
|
|
496
564
|
const f = iterate();
|
|
497
565
|
// Later iterate() calls run from asyncWrite, where rethrowing would be unhandled.
|
|
498
|
-
|
|
499
|
-
return
|
|
566
|
+
l = false;
|
|
567
|
+
return o || f;
|
|
500
568
|
};
|
|
501
569
|
// Landed-synchronously verdict for a LIVE iterator drain; null when no live
|
|
502
570
|
// drain ran (plain promise flight, or a deferred flatten). Drives the
|
|
503
571
|
// shared NotReady/loading tail below.
|
|
504
|
-
let
|
|
572
|
+
let u = null;
|
|
505
573
|
// Flatten one async level: a thenable that RESOLVES to an AsyncIterable —
|
|
506
574
|
// the shape every async stub returning a stream produces — consumes as the
|
|
507
575
|
// stream itself rather than settling on the iterable object. One level
|
|
@@ -516,11 +584,11 @@ function handleAsync(e, n, t) {
|
|
|
516
584
|
}
|
|
517
585
|
if (!t) return false;
|
|
518
586
|
const r = consumeIterator(e, n);
|
|
519
|
-
if (!n)
|
|
587
|
+
if (!n) u = r;
|
|
520
588
|
return true;
|
|
521
589
|
};
|
|
522
|
-
if (
|
|
523
|
-
let t = false, r = false,
|
|
590
|
+
if (i) {
|
|
591
|
+
let t = false, r = false, i, o = true;
|
|
524
592
|
// Close registration for the flattening path. Consumption starts in a
|
|
525
593
|
// microtask where the ambient owner is gone (or worse, someone else's),
|
|
526
594
|
// so `cleanup()` can't be used — the close targets el's disposal list
|
|
@@ -532,107 +600,154 @@ function handleAsync(e, n, t) {
|
|
|
532
600
|
// becomes disposal-bearing, which is exactly the class a directly
|
|
533
601
|
// returned iterable already occupies.
|
|
534
602
|
const registerDeferredClose = n => {
|
|
535
|
-
if (!e.
|
|
603
|
+
if (!e.ke) e.ke = n; else if (Array.isArray(e.ke)) e.ke.push(n); else e.ke = [ e.ke, n ];
|
|
536
604
|
};
|
|
537
605
|
n.then(r => {
|
|
538
|
-
if (
|
|
539
|
-
|
|
606
|
+
if (o) {
|
|
607
|
+
s = r;
|
|
540
608
|
t = true;
|
|
541
|
-
} else if (e.o?.
|
|
609
|
+
} else if (e.o?.Re === n && !(e.ue & REACTIVE_DISPOSED) && flattenIfIterable(r, registerDeferredClose)) ; else {
|
|
542
610
|
asyncWrite(r);
|
|
543
611
|
settleAutodispose();
|
|
544
612
|
}
|
|
545
613
|
}, e => {
|
|
546
|
-
if (
|
|
547
|
-
|
|
614
|
+
if (o) {
|
|
615
|
+
i = e;
|
|
548
616
|
r = true;
|
|
549
617
|
} else {
|
|
550
618
|
handleError(e);
|
|
551
619
|
settleAutodispose();
|
|
552
620
|
}
|
|
553
621
|
});
|
|
554
|
-
|
|
622
|
+
o = false;
|
|
555
623
|
if (r) {
|
|
556
624
|
// Settle through the same status path an async rejection uses, then
|
|
557
625
|
// unwind the in-progress synchronous read so the errored node isn't
|
|
558
626
|
// momentarily read as `undefined`.
|
|
559
|
-
handleError(
|
|
560
|
-
throw
|
|
627
|
+
handleError(i);
|
|
628
|
+
throw i;
|
|
561
629
|
} else if (!t) {
|
|
562
630
|
// Loading window: serve commit #0 instead of suspending. No transition
|
|
563
631
|
// is opened — first-flight work on a loadingValue node is loading-class
|
|
564
632
|
// (invisible to boundaries and transitions); the flight itself is
|
|
565
633
|
// already registered in _inFlight and lands through asyncWrite.
|
|
566
|
-
if (e.
|
|
634
|
+
if (e.ge) return e.Qe;
|
|
567
635
|
globalQueue.initTransition(resolveTransition(e));
|
|
568
636
|
throw new NotReadyError(context);
|
|
569
|
-
} else if (!flattenIfIterable(
|
|
637
|
+
} else if (!flattenIfIterable(s)) {
|
|
570
638
|
// Synchronously-resolved promise: the first real answer landed.
|
|
571
|
-
e.
|
|
639
|
+
e.ge = false;
|
|
572
640
|
}
|
|
573
641
|
// A sync-resolved promise holding an AsyncIterable flattened LIVE (we
|
|
574
642
|
// are still inside the synchronous read): full initial-drain semantics
|
|
575
643
|
// apply and the shared tail below settles the verdict.
|
|
576
644
|
}
|
|
577
645
|
if (r) flattenIfIterable(n);
|
|
578
|
-
if (
|
|
579
|
-
if (!
|
|
646
|
+
if (u !== null) {
|
|
647
|
+
if (!u) {
|
|
580
648
|
// Loading window: serve commit #0 (see the promise branch above).
|
|
581
|
-
if (e.
|
|
649
|
+
if (e.ge) return e.Qe;
|
|
582
650
|
globalQueue.initTransition(resolveTransition(e));
|
|
583
651
|
throw new NotReadyError(context);
|
|
584
652
|
}
|
|
585
653
|
// A sync first yield (or immediate empty completion) is the first real
|
|
586
654
|
// answer; async yields clear inside asyncWrite.
|
|
587
|
-
e.
|
|
655
|
+
e.ge = false;
|
|
588
656
|
}
|
|
589
|
-
return
|
|
657
|
+
return s;
|
|
590
658
|
}
|
|
591
659
|
|
|
592
660
|
function clearStatus(e, n = false) {
|
|
593
|
-
if (e.o?.
|
|
594
|
-
if (e.o?.
|
|
661
|
+
if (e.o?.ae) clearPendingSources(e);
|
|
662
|
+
if (e.o?.Ie) if (e.o !== null) e.o.Ie = false;
|
|
595
663
|
// The pending window is over; its quiet classification dies with it.
|
|
596
664
|
// (Unconditional: _reask is baked into the node literals, so this is a
|
|
597
665
|
// plain store to an existing slot — no shape change.)
|
|
598
|
-
if (e.o !== null) e.o.
|
|
666
|
+
if (e.o !== null) e.o.be = false;
|
|
599
667
|
e.S = n ? 0 : e.S & STATUS_UNINITIALIZED;
|
|
600
668
|
if (e.o?._) setPendingError(e);
|
|
601
669
|
// Update pending signal for isPending() reactivity (companions only exist
|
|
602
670
|
// once the verdict layer created them, which installs the hooks).
|
|
603
|
-
if (e.o?.
|
|
604
|
-
if (e.o?.i && e.T & CONFIG_CHILD_COMPANIONS && GlobalQueue.
|
|
671
|
+
if (e.o?.je || e.o?.xe) GlobalQueue.Oe(e);
|
|
672
|
+
if (e.o?.i && e.T & CONFIG_CHILD_COMPANIONS && GlobalQueue.Me !== null) GlobalQueue.Me(e);
|
|
605
673
|
const t = statusNotifierOf(e);
|
|
606
674
|
if (t) t.call(e);
|
|
607
675
|
}
|
|
608
676
|
|
|
609
|
-
|
|
677
|
+
/**
|
|
678
|
+
* Status clear for a flight LANDING (asyncWrite). A landing answers the
|
|
679
|
+
* node's OWN question — it retires the node's self entry, not the pending
|
|
680
|
+
* state its sources propagated onto it. An input re-asked while this flight
|
|
681
|
+
* was up (a second write to the signal feeding `a` while `b`'s first flight
|
|
682
|
+
* is in the air, #3373) marks `b` pending on `a` by propagation, with `b`'s
|
|
683
|
+
* flight still current: nothing superseded it (the re-ask only changed `a`'s
|
|
684
|
+
* status, not yet its value), so the landing arrives, and a full clear made
|
|
685
|
+
* `b` answer with the stale value — the transaction's reporter for `a` found
|
|
686
|
+
* nothing pending below it and committed the newer signal beside the older
|
|
687
|
+
* derived value (`2 / 1`); `isPending(b)` read false for the gap (#3376).
|
|
688
|
+
* With another source still pending the node stays derivatively pending on
|
|
689
|
+
* it; the landed value is written below (the staged answer is still the
|
|
690
|
+
* answer for the inputs it was asked with) and the input's own settle
|
|
691
|
+
* releases it, or its value change recomputes the node into a fresh flight.
|
|
692
|
+
* `_blocked` clears like a full clear: a landing that passed the `_inFlight`
|
|
693
|
+
* guard was not superseded by a re-run (recompute nulls `_inFlight` first),
|
|
694
|
+
* so the flag is the flight's own registration throw — the input settling
|
|
695
|
+
* unchanged must not re-run the node (an extra flight for the same inputs).
|
|
696
|
+
* The node is already STATUS_PENDING in that branch (only notifyStatus fills
|
|
697
|
+
* the set, with status; a loading-window park cannot coexist with a live
|
|
698
|
+
* flight since registration drops the set), so the flags only change when
|
|
699
|
+
* the first landing retires UNINITIALIZED. `_error` must move off self: a
|
|
700
|
+
* reader thrown NotReady(self) would park on a retired entry. Companions
|
|
701
|
+
* keep their verdict (pending before and after; the write re-syncs them).
|
|
702
|
+
*/ function landStatus(e, n = false) {
|
|
703
|
+
const t = e.o?.ae;
|
|
704
|
+
// (The full clear below drops the set whether or not self was retired first.)
|
|
705
|
+
if (t && (t.delete(e), t.size)) {
|
|
706
|
+
e.o.Ie = false;
|
|
707
|
+
if (n) e.S = STATUS_PENDING;
|
|
708
|
+
setPendingError(e, t.values().next().value);
|
|
709
|
+
} else clearStatus(e, n);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
function notifyStatus(e, n, t, r, i) {
|
|
610
713
|
// Wrap regular errors to track source node
|
|
611
714
|
if (n === STATUS_ERROR && !(t instanceof StatusError) && !(t instanceof NotReadyError)) t = new StatusError(e, t);
|
|
612
|
-
const
|
|
613
|
-
const
|
|
614
|
-
|
|
615
|
-
|
|
715
|
+
const o = n === STATUS_PENDING && t instanceof NotReadyError ? t.source : undefined;
|
|
716
|
+
const s = o === e;
|
|
717
|
+
// An optimistic node (a WRITTEN override slot) pending derivatively is a
|
|
718
|
+
// boundary: its override is the answer, pending stops here (A17). A
|
|
719
|
+
// derived override (#3479) is a previous speculative answer on a plain
|
|
720
|
+
// member — pending flows through it as through any memo.
|
|
721
|
+
const u = n === STATUS_PENDING && e.o?.Ce !== undefined && !(e.T & CONFIG_DERIVED_OVERRIDE) && !s;
|
|
722
|
+
const l = u && hasActiveOverride(e);
|
|
616
723
|
if (!r) {
|
|
617
|
-
|
|
618
|
-
|
|
724
|
+
// Lane before companions: the companion pokes below may create the
|
|
725
|
+
// node's pending-signal lane, whose parent is read from the node's lane
|
|
726
|
+
// at creation. Assigned after them (as it was), a node made pending by
|
|
727
|
+
// propagation before it rode the lane got a parentless companion lane,
|
|
728
|
+
// and the isPending reader that also depends on the node merged it into
|
|
729
|
+
// the held lane — the verdict then waited on the async it reports (#3379).
|
|
730
|
+
if (i) assignOrMergeLane(e, i);
|
|
731
|
+
if (n === STATUS_PENDING && o) {
|
|
732
|
+
addPendingSource(e, o);
|
|
733
|
+
// A fresh flight from a settled state starts with its inputs unpublished
|
|
734
|
+
// (a replacement flight while still pending keeps the mark: the first
|
|
735
|
+
// flight's committed inputs are still the frame).
|
|
736
|
+
if (!(e.S & STATUS_PENDING)) e.T &= ~CONFIG_INPUTS_PUBLISHED;
|
|
619
737
|
e.S = STATUS_PENDING | e.S & STATUS_UNINITIALIZED;
|
|
620
738
|
// Preserve the current source on this propagation so render-effect notification
|
|
621
739
|
// can register every distinct pending source with the transition.
|
|
622
|
-
setPendingError(e,
|
|
740
|
+
setPendingError(e, o, t);
|
|
623
741
|
} else {
|
|
624
742
|
clearPendingSources(e);
|
|
625
743
|
e.S = n | (n !== STATUS_ERROR ? e.S & STATUS_UNINITIALIZED : 0);
|
|
626
744
|
ext(e)._ = t;
|
|
627
745
|
}
|
|
628
|
-
GlobalQueue.
|
|
629
|
-
if (e.o?.i && e.T & CONFIG_CHILD_COMPANIONS && GlobalQueue.
|
|
630
|
-
}
|
|
631
|
-
if (o && !r) {
|
|
632
|
-
assignOrMergeLane(e, o);
|
|
746
|
+
GlobalQueue.Oe?.(e);
|
|
747
|
+
if (e.o?.i && e.T & CONFIG_CHILD_COMPANIONS && GlobalQueue.Me !== null) GlobalQueue.Me(e);
|
|
633
748
|
}
|
|
634
|
-
const f = r ||
|
|
635
|
-
const a = r ||
|
|
749
|
+
const f = r || l;
|
|
750
|
+
const a = r || u ? undefined : i;
|
|
636
751
|
const c = statusNotifierOf(e);
|
|
637
752
|
if (c) {
|
|
638
753
|
if (r && n === STATUS_PENDING) {
|
|
@@ -646,19 +761,56 @@ function notifyStatus(e, n, t, r, o) {
|
|
|
646
761
|
return;
|
|
647
762
|
}
|
|
648
763
|
forEachDependent(e, (e, r) => {
|
|
649
|
-
e.
|
|
650
|
-
|
|
764
|
+
e.Pe = clock;
|
|
765
|
+
// A pending mark on a kept-tail link re-derives the subscriber instead of
|
|
766
|
+
// marking it (A30, #3494 review; fuzzer latest-1 #2141; #3519 review).
|
|
767
|
+
// Past `_depsTail` lie the committed frame's deps, kept by A30 because
|
|
768
|
+
// that frame still derives from them while the pass that dropped them is
|
|
769
|
+
// held (staged, or unchanged and parked). A source going pending there is
|
|
770
|
+
// a question for the node's NEXT pass, not a fact about its current one:
|
|
771
|
+
// marked, the node was registered as the flight's reporter and its holder
|
|
772
|
+
// entangled with the flight (the A15 arm below) on a dep the held frame
|
|
773
|
+
// never reads — an orphaned fetch held the truth (a hide joined to a
|
|
774
|
+
// parked action, A34), and a manual flight nobody awaited held a gated
|
|
775
|
+
// reader hidden forever (fuzzer branches-1 #1105). Skipped, the committed
|
|
776
|
+
// frame published stale beside its new inputs (`query=1` beside a
|
|
777
|
+
// `selected` derived from `remote(0)`). Re-derived, the pass decides: it
|
|
778
|
+
// reads the dep and registers through its own read, or reads a held input
|
|
779
|
+
// and enters that transaction (A29), or reads neither and is done. Clears
|
|
780
|
+
// and errors still ride every link. A link inside the prefix carries the
|
|
781
|
+
// pass's generation (`link()`), so the test is O(1); mid-pass the prefix
|
|
782
|
+
// is what the pass has read so far, and the heap refuses a recomputing
|
|
783
|
+
// node — a dep it has yet to reach registers through its own read.
|
|
784
|
+
if (n === STATUS_PENDING && r.qe !== e.Ze) {
|
|
785
|
+
enqueueSub(e);
|
|
786
|
+
schedule();
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
if (n === STATUS_PENDING && o && !e.o?.ae?.has(o) || n !== STATUS_PENDING && (e.o?._ !== t || e.o?.ae)) {
|
|
651
790
|
// A pending-observer link is the subscription an `isPending` read created.
|
|
652
791
|
// It exists so the observer re-runs when the source settles, but it must
|
|
653
792
|
// not carry a real (non-NotReadyError) error — the synchronous `isPending`
|
|
654
793
|
// read swallows those, and the async path must match. Re-run the observer
|
|
655
794
|
// so `isPending` re-evaluates (to not-pending) instead of forwarding.
|
|
656
|
-
if (r.
|
|
795
|
+
if (r.He && n !== STATUS_PENDING && !(t instanceof NotReadyError)) {
|
|
657
796
|
enqueueSub(e);
|
|
658
797
|
schedule();
|
|
659
798
|
return;
|
|
660
799
|
}
|
|
661
|
-
|
|
800
|
+
// A memo another live transaction HOLDS — pending on its work, or
|
|
801
|
+
// staged by it — made pending by THIS flight cannot reveal before the
|
|
802
|
+
// flight lands: the two settle as one unit (A15, a shared derivation of
|
|
803
|
+
// both — #3443). Propagation marks the held memo without recomputing it
|
|
804
|
+
// (its inputs' values are unchanged), so this is the one moment the
|
|
805
|
+
// entanglement is known; the memo's stamped re-entry at its next pass
|
|
806
|
+
// came too late — the holder's own flight landed first and revealed the
|
|
807
|
+
// inputs beside the stale sum. The stamp alone decides nothing (#3334):
|
|
808
|
+
// a node the transaction once queued but holds nothing of — a switch's
|
|
809
|
+
// shared output whose first flight the second write superseded — must
|
|
810
|
+
// not drag the older flight into the newer reveal. Effects entangle
|
|
811
|
+
// nothing (A15 shared-hole corollary): their reader registers with the
|
|
812
|
+
// flight's transaction at queue notification.
|
|
813
|
+
if (!f) e.Ge ? o && !e.Le && (e.S & STATUS_PENDING || e.ve !== NOT_PENDING) && globalQueue.initTransition(e.Ge) : queuePendingNode(e);
|
|
662
814
|
notifyStatus(e, n, t, f, a);
|
|
663
815
|
}
|
|
664
816
|
});
|