@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
|
@@ -0,0 +1,669 @@
|
|
|
1
|
+
import { NOT_PENDING, REACTIVE_DISPOSED, REACTIVE_DIRTY, REACTIVE_CHECK, unwrapOverride, REACTIVE_RECOMPUTING_DEPS, STATUS_PENDING, STATUS_UNINITIALIZED, REACTIVE_OPTIMISTIC_DIRTY, REACTIVE_MANUAL_WRITE, CONFIG_OVERRIDE_SUPERSEDED, CONFIG_HAS_COMPANIONS, CONFIG_ADOPTED_UNFLUSHED, CONFIG_CHILD_COMPANIONS, STATUS_ERROR } from "./constants.js";
|
|
2
|
+
|
|
3
|
+
import { setSignal, visibleOverride, unflushedValue, context, markLateLinker, read, stale, hasActiveOverride, prepareComputed, tracking, ext, setLatestReadActive, setContextInternal, optimisticComputed, unflushed, unflushedCompanions, setPendingCheckActive, latestReadActive, optimisticSignal, pendingCheckActive } from "./core.js";
|
|
4
|
+
|
|
5
|
+
import { NotReadyError } from "./error.js";
|
|
6
|
+
|
|
7
|
+
import { link } from "./graph.js";
|
|
8
|
+
|
|
9
|
+
import { dispose } from "./owner.js";
|
|
10
|
+
|
|
11
|
+
import { insertIntoHeap, queueFor, enqueueSub } from "./heap.js";
|
|
12
|
+
|
|
13
|
+
import "./invariants.js";
|
|
14
|
+
|
|
15
|
+
import { readsHeldCommitted, assignOrMergeLane } from "./lanes.js";
|
|
16
|
+
|
|
17
|
+
import { installOptimisticEngine } from "./optimistic.js";
|
|
18
|
+
|
|
19
|
+
import { GlobalQueue, insertSubs, schedule, activeTransition, currentTransition, runAsTransitionBatch, activeAffectsMarks } from "./scheduler.js";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The isPending()/latest() verdict layer, moved out of core.ts. Importing this
|
|
23
|
+
* module installs the companion-maintenance hooks on GlobalQueue; apps that
|
|
24
|
+
* never import isPending/latest never pay for any of it.
|
|
25
|
+
*/
|
|
26
|
+
// Companions (pending signals / latest shadows) are optimistic nodes: their
|
|
27
|
+
// writes go through the optimistic write path and their reversion rides the
|
|
28
|
+
// same lanes, so the verdict layer brings the engine with it.
|
|
29
|
+
installOptimisticEngine();
|
|
30
|
+
|
|
31
|
+
let pendingProbe = null;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Probes whose verdict was suppressed by the fresh-read pairing rule while
|
|
35
|
+
* the held write's fate was still undecided (see recordFreshRead /
|
|
36
|
+
* wakeSuppressedProbes): held node → the wrapper computeds that probed it.
|
|
37
|
+
* Entries die with the hold — the commit/revert snap clears them.
|
|
38
|
+
*/ const suppressedProbes = new Map;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Get or create the pending signal for a node (lazy).
|
|
42
|
+
* Used by isPending() to track pending state reactively.
|
|
43
|
+
*/
|
|
44
|
+
/** #3038: register a companion-carrying firewall child on its firewall's
|
|
45
|
+
* companion set and arm the post-recompute snap (CONFIG_CHILD_COMPANIONS is
|
|
46
|
+
* the one-load gate at the call sites). The snap then iterates exactly the
|
|
47
|
+
* children someone asked verdicts of — O(companions) — never the full
|
|
48
|
+
* `_child` chain, which carries one node per materialized leaf (the
|
|
49
|
+
* O(all-leaves-ever-read)-per-update pathology). Entries live as long as the
|
|
50
|
+
* store addresses the leaf — the unobserved sweep's `unlinkFirewallChild`
|
|
51
|
+
* drops them (#3503); a store with no leaf-level isPending()/latest() reads
|
|
52
|
+
* never allocates the set or pays the walk. */ function markFirewallChildCompanions(e) {
|
|
53
|
+
const t = e.ne;
|
|
54
|
+
if (!t) return;
|
|
55
|
+
t.C |= CONFIG_CHILD_COMPANIONS;
|
|
56
|
+
(ext(t).Jt ??= new Set).add(e);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function getPendingSignal(e) {
|
|
60
|
+
let t = e.o?.he;
|
|
61
|
+
if (!t) {
|
|
62
|
+
// Start false, write true if pending - ensures reversion returns to false
|
|
63
|
+
t = optimisticSignal(false, {
|
|
64
|
+
ownedWrite: true
|
|
65
|
+
});
|
|
66
|
+
ext(e).he = t;
|
|
67
|
+
e.C |= CONFIG_HAS_COMPANIONS;
|
|
68
|
+
markFirewallChildCompanions(e);
|
|
69
|
+
ext(t).nn = e;
|
|
70
|
+
if (computePendingState(e)) backfillCompanion(e, t, true);
|
|
71
|
+
joinUnflushedResync(e);
|
|
72
|
+
}
|
|
73
|
+
return t;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* A lazily created companion's first write mirrors state the owner already
|
|
78
|
+
* carries — a held write, a pending verdict. The companion is created wherever
|
|
79
|
+
* the first latest()/isPending() read happens to run, but the write belongs
|
|
80
|
+
* to whatever HOLDS that state: written in the ambient window, the override
|
|
81
|
+
* would register in the ambient batch and revert when that flush's round ends
|
|
82
|
+
* — the shadow re-derived from the committed view, the verdict flipped false —
|
|
83
|
+
* while the owner's hold was still on (#3336: A and B differing only in
|
|
84
|
+
* whether a companion existed before the hold). A companion created lazily
|
|
85
|
+
* answers as if it had always existed: its backfill is registered with the
|
|
86
|
+
* owner's transaction and lives and reverts with it. A hold with no
|
|
87
|
+
* transaction (a pending async, a same-flush staged write) is ambient and the
|
|
88
|
+
* write stays ambient. (A28 (3), lifted from #3337.)
|
|
89
|
+
*/ function backfillCompanion(e, t, n) {
|
|
90
|
+
const i = e.Te;
|
|
91
|
+
if (i) runAsTransitionBatch(i, () => setSignal(t, n)); else setSignal(t, n);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** A28: a companion created while its source carries an UNFLUSHED write joins
|
|
95
|
+
* the flush-start re-sync like a companion that existed at the write —
|
|
96
|
+
* syncCompanions only reaches companions that exist at write time. Without
|
|
97
|
+
* this the flush brings it current by a plain recompute instead of the
|
|
98
|
+
* optimistic write: its readers are then staged under whatever transaction
|
|
99
|
+
* the round entered (a memo over latest() of a held source was held with the
|
|
100
|
+
* source, and its untracked reads answered the previous value until the hold
|
|
101
|
+
* committed) rather than direct-committed as the optimistic view they are. */ function joinUnflushedResync(e) {
|
|
102
|
+
if (unflushed(e)) unflushedCompanions.push(e);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** The staged value the verdict channels answer for (A28): while a node
|
|
106
|
+
* carries an unflushed write its `_pendingValue` is not yet part of any
|
|
107
|
+
* flushed world, so the channels answer for the value the last flush left
|
|
108
|
+
* staged (a held node's stash) or for nothing (NOT_PENDING). */ function flushedStaged(e) {
|
|
109
|
+
if (!unflushed(e)) return e._e;
|
|
110
|
+
// Ambient, or adopted before any flush (CONFIG_ADOPTED_UNFLUSHED): nothing
|
|
111
|
+
// a flush carried is staged for it. A held rewrite: the stash.
|
|
112
|
+
return e.Te === null || e.C & CONFIG_ADOPTED_UNFLUSHED ? NOT_PENDING : e.o.Bt;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function collectPendingSources(e) {
|
|
116
|
+
if (!pendingProbe) return;
|
|
117
|
+
pendingProbe.sources.add(e);
|
|
118
|
+
const t = e.ne || e;
|
|
119
|
+
if (t !== e) pendingProbe.sources.add(t);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Adds a node to the active isPending() probe without reading it. The store's
|
|
124
|
+
* untracked-probe fallback (`witnessAffectsMark`) reaches this through
|
|
125
|
+
* `GlobalQueue._witnessAffects` — its callers guard on `pendingCheckActive`,
|
|
126
|
+
* which only flips inside `isPending()`, so the hook is always installed by
|
|
127
|
+
* the time it can fire.
|
|
128
|
+
*/ function witnessAffects(e) {
|
|
129
|
+
pendingProbe?.sources.add(e);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* The affects() coverage walk — the read half of the dedicated mark channel.
|
|
134
|
+
* A node is covered by a live mark iff it carries one (`_affectsCount`) or
|
|
135
|
+
* derives, through its CURRENT deps (hopping store firewalls), from a node
|
|
136
|
+
* that does. Pull-based coverage means graph rewires, mid-window recomputes,
|
|
137
|
+
* and probe-triggered recomputes can never strand or strip a mark — there is
|
|
138
|
+
* nothing stored downstream to corrupt. Probe-created links
|
|
139
|
+
* (`_pendingObserver`) are skipped so an `isPending` wrapper memo never
|
|
140
|
+
* inherits the coverage it reports on.
|
|
141
|
+
*/ function markWalk(e, t) {
|
|
142
|
+
if (e.o?.t) return true;
|
|
143
|
+
// A real error outranks an inherited mark (A16/A24c): an errored node
|
|
144
|
+
// answers probes with its error, not a coverage verdict, and coverage does
|
|
145
|
+
// not flow through it — matching the rails' behavior, where propagation
|
|
146
|
+
// stopped at errored nodes. A DIRECT mark on an errored node still reads
|
|
147
|
+
// pending (the count check above), also matching.
|
|
148
|
+
if (e.S & STATUS_ERROR) return false;
|
|
149
|
+
if (t.has(e)) return false;
|
|
150
|
+
t.add(e);
|
|
151
|
+
const n = e.ne;
|
|
152
|
+
if (n && markWalk(n, t)) return true;
|
|
153
|
+
// Mid-recompute (the clearStatus companion poke runs before
|
|
154
|
+
// trimStaleDeps), only the validated prefix [_deps.._depsTail] is this
|
|
155
|
+
// pass's dependency set — walking past it would read dropped deps and
|
|
156
|
+
// latch a stale verdict on the companion.
|
|
157
|
+
const i = e;
|
|
158
|
+
const r = i.ft & REACTIVE_RECOMPUTING_DEPS ? i.Fe : undefined;
|
|
159
|
+
if (r !== null) {
|
|
160
|
+
for (let e = i.ee ?? null; e !== null; e = e.te) {
|
|
161
|
+
if (!e.Ce && markWalk(e.re, t)) return true;
|
|
162
|
+
if (e === r) break;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function quietPending(e) {
|
|
169
|
+
if (e.o?.ut) {
|
|
170
|
+
for (const t of e.o.ut) if (!t.o?.Ee) return false;
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
return e.o?.Ee ?? false;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// NOTE: a loadingValue node's open loading window (_loading) is verdict-quiet
|
|
177
|
+
// on purpose: commit #0 answers the question by declaration, so the window
|
|
178
|
+
// reads NOT pending — first-load affordances live in the value channel
|
|
179
|
+
// (null / skeleton provenance the author encoded), and isPending stays what
|
|
180
|
+
// it always was: refetch truth for an answered question. This keeps the
|
|
181
|
+
// verdict fully correlated with transition-class machinery and keeps server
|
|
182
|
+
// (always false) and client hydration trivially consistent.
|
|
183
|
+
function newQuestionInFlight(e) {
|
|
184
|
+
return !!(e.S & STATUS_PENDING) && !(e.S & STATUS_UNINITIALIZED) && !quietPending(e);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function computePendingState(e) {
|
|
188
|
+
const t = e;
|
|
189
|
+
if (t.ft & REACTIVE_DISPOSED) return false;
|
|
190
|
+
// Mark coverage is transitive by dep-graph reachability: a latest() shadow
|
|
191
|
+
// reaches its owner (and a store leaf its firewall) through its own deps,
|
|
192
|
+
// so the one walk covers direct marks, derivation, and companion chains.
|
|
193
|
+
// Gated: apps with no live mark pay one integer compare.
|
|
194
|
+
if (activeAffectsMarks !== 0 && markWalk(e, new Set)) return true;
|
|
195
|
+
const n = e.ne;
|
|
196
|
+
if (e.o?.nn) {
|
|
197
|
+
const t = e.o?.nn;
|
|
198
|
+
const n = t.ne || t;
|
|
199
|
+
return newQuestionInFlight(n);
|
|
200
|
+
}
|
|
201
|
+
const i = flushedStaged(e);
|
|
202
|
+
if (n && i !== NOT_PENDING && !hasActiveOverride(e)) {
|
|
203
|
+
return !!(n.ft & REACTIVE_MANUAL_WRITE) || !n.o?.ce && !(n.S & STATUS_PENDING) || !!(n.S & STATUS_PENDING) && quietPending(n);
|
|
204
|
+
}
|
|
205
|
+
// `!comp._loading`: a hold created while the loading window is still open is
|
|
206
|
+
// the window's own landing in flight to its commit — verdict-quiet like the
|
|
207
|
+
// rest of the window (the UNINITIALIZED check suppresses exactly this frame
|
|
208
|
+
// for windowless first loads; born-committed nodes need their own gate, #2990).
|
|
209
|
+
// A18 (d) for a body-end supersession (#3427): the truth at hand is the
|
|
210
|
+
// COMMITTED value — nothing staged — yet the display still shows the
|
|
211
|
+
// override; pending iff they differ, as for a staged arrival below.
|
|
212
|
+
if (e.C & CONFIG_OVERRIDE_SUPERSEDED && e._e === NOT_PENDING && visibleOverride(e)) return !e.Re || !e.Re(e.Ae, unwrapOverride(e.o?.Ne));
|
|
213
|
+
// A28 (2): an unflushed write is not yet observable — the verdict answers
|
|
214
|
+
// for the flushed staged value.
|
|
215
|
+
if (i !== NOT_PENDING && !t.Se) {
|
|
216
|
+
// A18 (d): under a displayed override the observable value is the
|
|
217
|
+
// override, so the verdict is "the arrived truth differs from it" —
|
|
218
|
+
// even before the node's first commit. The UNINITIALIZED suppression
|
|
219
|
+
// below is A19 exception (1), "no observable value exists to be
|
|
220
|
+
// non-final"; an override is one (a node whose first landing was held
|
|
221
|
+
// by a reveal it never got to commit, then superseded under its
|
|
222
|
+
// override, read false here).
|
|
223
|
+
if (visibleOverride(e)) return !e.Re || !e.Re(i, unwrapOverride(e.o?.Ne));
|
|
224
|
+
// A quiet re-ask's held landing still answers the same question: the
|
|
225
|
+
// classification survives the landing (asyncWrite) and dies with the
|
|
226
|
+
// commit (commitPendingNode) — verdict-quiet through the reveal, like
|
|
227
|
+
// the loading window above (#3178).
|
|
228
|
+
// A staged value equal to the committed one is no proposal (A34, #3494): the
|
|
229
|
+
// observable value IS final (A19). The coalesced `setShow(false);
|
|
230
|
+
// setShow(true)` read pending through the flush that carried it — and,
|
|
231
|
+
// stamped into a hold that flush opened, until the hold settled.
|
|
232
|
+
if (!(t.S & STATUS_UNINITIALIZED) && !t.o?.Ee && (!e.Re || !e.Re(e.Ae, i))) return true;
|
|
233
|
+
}
|
|
234
|
+
return newQuestionInFlight(t);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function syncCompanions(e, t) {
|
|
238
|
+
if (e.o?.he) updatePendingSignal(e);
|
|
239
|
+
if (e.o?.Ge) setSignal(e.o?.Ge, t);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function updatePendingSignal(e) {
|
|
243
|
+
if (e.o?.he) {
|
|
244
|
+
setSignal(e.o?.he, computePendingState(e));
|
|
245
|
+
}
|
|
246
|
+
if (e.o?.Ge) updatePendingSignal(e.o?.Ge);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function updateChildCompanions(e) {
|
|
250
|
+
const t = e.o?.Jt;
|
|
251
|
+
if (t === undefined) return;
|
|
252
|
+
for (const e of t) updatePendingSignal(e);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Re-derive every verdict companion downstream of `el` (subs + firewall
|
|
257
|
+
* children, dedup'd). The affects() channel's poke walk: registration and
|
|
258
|
+
* re-ask flips use the live write path (companion setSignal — its own lane
|
|
259
|
+
* lets the wake escape an incomplete transition's effect stash, #2887);
|
|
260
|
+
* mark release passes `snap` because it runs inside queue finalization,
|
|
261
|
+
* where companion writes must land committed (a setSignal there would open
|
|
262
|
+
* a fresh override window that nothing settles).
|
|
263
|
+
*/ function repollDownstreamVerdicts(e, t = false) {
|
|
264
|
+
const n = t ? snapCompanionsToState : updatePendingSignal;
|
|
265
|
+
const i = new Set;
|
|
266
|
+
const visit = e => {
|
|
267
|
+
if (i.has(e)) return;
|
|
268
|
+
i.add(e);
|
|
269
|
+
if (e.o?.he || e.o?.Ge) n(e);
|
|
270
|
+
for (let t = e.u; t !== null; t = t.ie) visit(t.se);
|
|
271
|
+
for (let t = e.o?.i ?? null; t !== null; t = t.ue) {
|
|
272
|
+
visit(t);
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
visit(e);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* The correction half of the provisional fresh-read suppression (see
|
|
280
|
+
* collectPending): fired from the sanctioned async-registration site
|
|
281
|
+
* (GlobalQueue.notify) when a transaction gains an in-flight async blocker.
|
|
282
|
+
* Every probe that returned "not pending" purely because it read a held
|
|
283
|
+
* value belonging to that transaction re-runs — its re-probe now sees the
|
|
284
|
+
* live blocker through heldAwaitingAsync and lands the true verdict. The
|
|
285
|
+
* wake mirrors a companion write's own notification (optimistic-dirty on the
|
|
286
|
+
* companion's lane) so the corrected verdict commits and flushes immediately
|
|
287
|
+
* instead of being held with the transaction it reports on.
|
|
288
|
+
*/ function wakeSuppressedProbes(e) {
|
|
289
|
+
if (suppressedProbes.size === 0) return;
|
|
290
|
+
let t = false;
|
|
291
|
+
for (const [n, i] of suppressedProbes) {
|
|
292
|
+
const r = n.Te;
|
|
293
|
+
const s = r ? currentTransition(r) : null;
|
|
294
|
+
if (!s) {
|
|
295
|
+
suppressedProbes.delete(n);
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
if (s !== e) continue;
|
|
299
|
+
suppressedProbes.delete(n);
|
|
300
|
+
const o = n.o?.he?.o?.de;
|
|
301
|
+
for (const e of i) {
|
|
302
|
+
if (e.ft & REACTIVE_DISPOSED) continue;
|
|
303
|
+
e.ft |= REACTIVE_OPTIMISTIC_DIRTY;
|
|
304
|
+
if (o) assignOrMergeLane(e, o); else if (e.o !== null) e.o.de = undefined;
|
|
305
|
+
enqueueSub(e);
|
|
306
|
+
t = true;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (t) schedule();
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function snapCompanionsToState(e) {
|
|
313
|
+
suppressedProbes.size !== 0 && suppressedProbes.delete(e);
|
|
314
|
+
const t = e.o?.he;
|
|
315
|
+
if (t && (t.o?.Ne === undefined || t.o?.Ne === NOT_PENDING)) {
|
|
316
|
+
const n = computePendingState(e);
|
|
317
|
+
if (t.Ae !== n || t._e !== NOT_PENDING) {
|
|
318
|
+
t.Ae = n;
|
|
319
|
+
t._e = NOT_PENDING;
|
|
320
|
+
insertSubs(t);
|
|
321
|
+
schedule();
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
const n = e.o?.Ge;
|
|
325
|
+
if (n && !(n.ft & REACTIVE_DISPOSED)) {
|
|
326
|
+
// A leaf whose firewall is disposed (the projection's teardown snaps its
|
|
327
|
+
// companion-bearing leaves): the shadow's compute reads through a
|
|
328
|
+
// disposed, possibly still-pending projection and would sit
|
|
329
|
+
// NotReady/uninitialized forever — never derived, its backfilled override
|
|
330
|
+
// dropped at the settle — against a leaf whose committed value differs
|
|
331
|
+
// (INV-4 at the next quiescence; spec O5). It dies with its source;
|
|
332
|
+
// getLatestValueComputed treats a disposed shadow as absent, so a later
|
|
333
|
+
// read recreates it from the committed view.
|
|
334
|
+
if (e.ne?.ft & REACTIVE_DISPOSED) {
|
|
335
|
+
dispose(n);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
if ((n.o?.Ne === undefined || n.o?.Ne === NOT_PENDING) && n._e === NOT_PENDING && !Object.is(n.Ae, e.Ae) && !(n.ft & (REACTIVE_DIRTY | REACTIVE_CHECK))) {
|
|
339
|
+
n.ft |= REACTIVE_DIRTY;
|
|
340
|
+
insertIntoHeap(n, queueFor(n));
|
|
341
|
+
insertSubs(n);
|
|
342
|
+
schedule();
|
|
343
|
+
}
|
|
344
|
+
snapCompanionsToState(n);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function getLatestValueComputed(e) {
|
|
349
|
+
let t = e.o?.Ge;
|
|
350
|
+
// A shadow disposed while unobserved (its gated reader unmounted at a
|
|
351
|
+
// landing) is a corpse: sync writes into it equality-swallow against its
|
|
352
|
+
// frozen _value, and a later read revives it via recompute — clearing
|
|
353
|
+
// DISPOSED and re-deriving from the committed view, so the banner showed
|
|
354
|
+
// the previous transition's target (#3041 follow-up). Treat it as absent;
|
|
355
|
+
// recreation backfills from the in-flight write below.
|
|
356
|
+
if (t && t.ft & REACTIVE_DISPOSED) t = undefined;
|
|
357
|
+
if (!t) {
|
|
358
|
+
const n = latestReadActive;
|
|
359
|
+
setLatestReadActive(false);
|
|
360
|
+
const i = pendingCheckActive;
|
|
361
|
+
setPendingCheckActive(false);
|
|
362
|
+
const r = context;
|
|
363
|
+
setContextInternal(null);
|
|
364
|
+
// Detach from owner so it isn't disposed with effects
|
|
365
|
+
GlobalQueue.On = true;
|
|
366
|
+
try {
|
|
367
|
+
t = optimisticComputed(() => read(e), {
|
|
368
|
+
ownedWrite: true
|
|
369
|
+
});
|
|
370
|
+
} finally {
|
|
371
|
+
GlobalQueue.On = false;
|
|
372
|
+
}
|
|
373
|
+
ext(e).Ge = t;
|
|
374
|
+
e.C |= CONFIG_HAS_COMPANIONS;
|
|
375
|
+
markFirewallChildCompanions(e);
|
|
376
|
+
ext(t).nn = e;
|
|
377
|
+
// Parent-child lane relationship
|
|
378
|
+
// Backfill an in-flight write (mirrors getPendingSignal): the companion is
|
|
379
|
+
// created lazily, possibly after the write was processed — syncCompanions
|
|
380
|
+
// only pushes into companions that already exist, so the first latest()
|
|
381
|
+
// read inside a held transition showed the committed value (#3041).
|
|
382
|
+
const s = flushedStaged(e);
|
|
383
|
+
if (s !== NOT_PENDING && !hasActiveOverride(e)) backfillCompanion(e, t, s);
|
|
384
|
+
joinUnflushedResync(e);
|
|
385
|
+
setContextInternal(r);
|
|
386
|
+
setPendingCheckActive(i);
|
|
387
|
+
setLatestReadActive(n);
|
|
388
|
+
}
|
|
389
|
+
return t;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/** The latest()-mode read path, installed as GlobalQueue._latestRead. */
|
|
393
|
+
/** A7: the source has no visible value yet — judged on the OWNER, as read()
|
|
394
|
+
* does: a store leaf behind a projection's firewall is a plain signal whose
|
|
395
|
+
* `_value` is the seed (A25: a draft, never a value), and read() routes a
|
|
396
|
+
* latest() read here before its own firewall/status logic. An override
|
|
397
|
+
* displays a value even before the first commit (A17). */ function uninitializedSource(e) {
|
|
398
|
+
const t = e.ne || e;
|
|
399
|
+
return !!(t.S & STATUS_UNINITIALIZED) && !hasActiveOverride(e);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function latestRead(e) {
|
|
403
|
+
// A leaf of a DISPOSED projection has no flushed world left to mirror: a
|
|
404
|
+
// shadow created for it now would read through the dead firewall, sit
|
|
405
|
+
// NotReady/uninitialized forever, and no teardown would ever retire it (the
|
|
406
|
+
// firewall's already ran — spec O5). Serve the committed value; create
|
|
407
|
+
// nothing. (A read of a disposed node freezes at its last commit, #3024.)
|
|
408
|
+
if (e.ne?.ft & REACTIVE_DISPOSED) return e.Ae;
|
|
409
|
+
const t = getLatestValueComputed(e);
|
|
410
|
+
const n = latestReadActive;
|
|
411
|
+
setLatestReadActive(false);
|
|
412
|
+
const i = visibleOverride(e) ? unwrapOverride(e.o?.Ne) : e.Ae;
|
|
413
|
+
// A28: an unflushed write is not the staged value latest() serves. The
|
|
414
|
+
// shadow was written at the source's write to mirror it (A8) — consult it
|
|
415
|
+
// only once a flush has carried the write.
|
|
416
|
+
const r = unflushedValue(e);
|
|
417
|
+
if (r !== NOT_PENDING) {
|
|
418
|
+
// The reader derived from the flushed world because of an unflushed
|
|
419
|
+
// write: it runs again in the flush that carries it (late linker) —
|
|
420
|
+
// a pull may have cleared the mark the write's walk set.
|
|
421
|
+
if (context !== null && context.ft & REACTIVE_RECOMPUTING_DEPS) markLateLinker(context);
|
|
422
|
+
// Link the reader to the shadow so the flush that carries the write
|
|
423
|
+
// updates it (the shadow itself already mirrors the write, A8).
|
|
424
|
+
try {
|
|
425
|
+
read(t);
|
|
426
|
+
} catch {
|
|
427
|
+
/* the flushed value answers */} finally {
|
|
428
|
+
setLatestReadActive(n);
|
|
429
|
+
}
|
|
430
|
+
// An ambient write: the visible value (override or committed). A rewrite
|
|
431
|
+
// of a held node: the staged value the last flush left.
|
|
432
|
+
return e.Te === null ? i : r;
|
|
433
|
+
}
|
|
434
|
+
let s;
|
|
435
|
+
try {
|
|
436
|
+
// No mid-tick pull: writes become visible at flush (A28), so before the
|
|
437
|
+
// flush the shadow is exactly as current as the flushed world — the read
|
|
438
|
+
// below serves it as is. (#2922's pull, which brought the shadow current
|
|
439
|
+
// against the unflushed write, is superseded: `flush()` first to read
|
|
440
|
+
// your own write.)
|
|
441
|
+
s = read(t);
|
|
442
|
+
} catch (t) {
|
|
443
|
+
// A NotReady from the shadow of an INITIALIZED source means the shadow
|
|
444
|
+
// is mid-flight: serve the visible (committed / override) value. An
|
|
445
|
+
// uninitialized source has no visible value — latest() throws in every
|
|
446
|
+
// scope rather than fabricate `undefined` for a `T` that excludes it
|
|
447
|
+
// (A7; the unowned scope used to return undefined here).
|
|
448
|
+
if (t instanceof NotReadyError && !uninitializedSource(e)) return i;
|
|
449
|
+
throw t;
|
|
450
|
+
} finally {
|
|
451
|
+
setLatestReadActive(n);
|
|
452
|
+
}
|
|
453
|
+
if (t.S & STATUS_PENDING) {
|
|
454
|
+
if (uninitializedSource(e)) throw new NotReadyError(e);
|
|
455
|
+
return i;
|
|
456
|
+
}
|
|
457
|
+
// A render effect off the shadow's HELD lane sees the committed value and
|
|
458
|
+
// re-runs at the release (#3460; lanes mirror transitions — see
|
|
459
|
+
// readsHeldCommitted). Was: only a reader under ANOTHER lane; a mainline
|
|
460
|
+
// reader, mounted or re-run by a sync write mid-hold, showed the
|
|
461
|
+
// speculative value beside the lane's deferred readers.
|
|
462
|
+
if (stale && context !== null && readsHeldCommitted(t, context)) return e.Ae;
|
|
463
|
+
// A shadow recomputed by the pull above (not at creation) holds its fresh
|
|
464
|
+
// speculative value in _pendingValue; a contextless read() only surfaces
|
|
465
|
+
// _value. Overrides stay authoritative (A17), and stale readers keep the
|
|
466
|
+
// other transition's committed view, matching read()'s own selection.
|
|
467
|
+
if (t._e !== NOT_PENDING && !hasActiveOverride(t) && !(stale && t.Te && activeTransition !== t.Te)) return t._e;
|
|
468
|
+
return s;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* A latest() shadow that is uninitialized only because it was CREATED during
|
|
473
|
+
* an active flight — its parent source already has a committed value, so
|
|
474
|
+
* latest() serves that as the visible value and a tracked reader has
|
|
475
|
+
* something to pair a verdict with (#3166). Same parent resolution as
|
|
476
|
+
* computePendingState. The pending signal companion also carries
|
|
477
|
+
* `_parentSource` but is a plain signal (no `_fn`) that never goes pending,
|
|
478
|
+
* so the `_fn` check is belt-and-braces for this call site.
|
|
479
|
+
*/ function latestShadowWithInitializedParent(e) {
|
|
480
|
+
if (typeof e.ht !== "function") return false;
|
|
481
|
+
const t = e.o?.nn;
|
|
482
|
+
if (t === undefined) return false;
|
|
483
|
+
const n = t.ne || t;
|
|
484
|
+
return !(n.S & STATUS_UNINITIALIZED);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/** The isPending()-probe read path, installed as GlobalQueue._pendingCheck. */ function pendingCheckRead(e, t, n, i) {
|
|
488
|
+
setPendingCheckActive(false);
|
|
489
|
+
if (typeof e.ht === "function") {
|
|
490
|
+
GlobalQueue.On = true;
|
|
491
|
+
try {
|
|
492
|
+
prepareComputed(e, true);
|
|
493
|
+
} finally {
|
|
494
|
+
GlobalQueue.On = false;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
const r = n.S;
|
|
498
|
+
if (t && r & STATUS_PENDING && r & STATUS_UNINITIALIZED &&
|
|
499
|
+
// The suspend-throw is for a genuinely-first-load source: the tracked
|
|
500
|
+
// reader has nothing to pair a verdict with, so it parks on the source.
|
|
501
|
+
// A latest() SHADOW created lazily mid-flight is born uninitialized even
|
|
502
|
+
// though its parent has a committed value latest() will serve — throwing
|
|
503
|
+
// here (swallowed by latestRead's fallback) dropped the shadow from the
|
|
504
|
+
// probe, so a tracked latest(isPending()) probe created during a
|
|
505
|
+
// new-question flight cached `false` for that whole flight (#3166).
|
|
506
|
+
// Defer to the PARENT's initialization state and fall through to normal
|
|
507
|
+
// collection; the plain pending throw downstream still links the reader.
|
|
508
|
+
!latestShadowWithInitializedParent(n)) {
|
|
509
|
+
if (tracking && e !== t) link(e, t);
|
|
510
|
+
setPendingCheckActive(true);
|
|
511
|
+
throw n.o?._;
|
|
512
|
+
}
|
|
513
|
+
collectPendingSources(e);
|
|
514
|
+
if (i) collectPendingSources(i);
|
|
515
|
+
setPendingCheckActive(true);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* A held node whose transaction still has an async question in flight. The
|
|
520
|
+
* probe's fresh-read pairing rule (#2831 — "a reader that sees the fresh
|
|
521
|
+
* value must not also be told it is pending") only applies to LANDED answers
|
|
522
|
+
* awaiting reveal; while the answer is still computing, the fresh value the
|
|
523
|
+
* reader saw is an input, and pending remains the truth for every reader
|
|
524
|
+
* (#3028).
|
|
525
|
+
*/ function heldAwaitingAsync(e) {
|
|
526
|
+
const t = e.Te;
|
|
527
|
+
const n = t ? currentTransition(t) : activeTransition;
|
|
528
|
+
if (!n || n.yt) return false;
|
|
529
|
+
// A plain staged write (a signal/store leaf — no _fn) held while an action
|
|
530
|
+
// is still running is an INPUT to a computation still in flight (#3078):
|
|
531
|
+
// the pairing rule must not suppress the verdict, or a memo recomputing
|
|
532
|
+
// mid-action reads the staged value, gets told "not pending", and
|
|
533
|
+
// disagrees with a direct isPending() probe for the whole action window.
|
|
534
|
+
// A computed's staged value is the opposite case — a LANDED answer
|
|
535
|
+
// awaiting reveal — where the pairing rule stands even inside an open
|
|
536
|
+
// action (#2831: a reader that saw the new value must not also see
|
|
537
|
+
// pending); still-computing answers are covered by the reporter scan.
|
|
538
|
+
if (n.dt.length && !e.ht) return true;
|
|
539
|
+
// The reporter scan runs for an unstamped node too (#3457): a node staged
|
|
540
|
+
// AFTER the transaction opened is pushed straight into the transaction's
|
|
541
|
+
// batch (queuePendingNode, once initTransition adopted it) and only gets
|
|
542
|
+
// its `_transition` stamp when the flush stashes the hold, but its staged
|
|
543
|
+
// value is already the transaction's, and `t` resolved to that very
|
|
544
|
+
// transaction above. Gating on the stamp let a memo whose recompute read
|
|
545
|
+
// a sync memo's fresh staged value mid-flush pair "not pending" with it
|
|
546
|
+
// (A10) while the transaction's async source was still computing, so a
|
|
547
|
+
// memo-wrapped isPending() read false where a direct probe read true.
|
|
548
|
+
for (const [e, t] of n.ot) {
|
|
549
|
+
if (t.size && e.S & STATUS_PENDING && e.o?._?.source === e) return true;
|
|
550
|
+
}
|
|
551
|
+
return false;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
function recordFreshRead(e, t) {
|
|
555
|
+
if (pendingProbe !== null && e._e !== NOT_PENDING && t === e._e) {
|
|
556
|
+
if (heldAwaitingAsync(e)) return;
|
|
557
|
+
pendingProbe.freshReads.add(e);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
function applyReask(e, t) {
|
|
562
|
+
const n = !!(e.S & STATUS_PENDING);
|
|
563
|
+
const i = t && !(n && !e.o?.Ee);
|
|
564
|
+
const r = n && (e.o?.Ee ?? false) !== i;
|
|
565
|
+
// Allocation-free for the quiet case: false is the extension default.
|
|
566
|
+
if (i) ext(e).Ee = true; else if (e.o !== null) e.o.Ee = false;
|
|
567
|
+
return r;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
function latest(e) {
|
|
571
|
+
const t = latestReadActive;
|
|
572
|
+
setLatestReadActive(true);
|
|
573
|
+
try {
|
|
574
|
+
return e();
|
|
575
|
+
} finally {
|
|
576
|
+
setLatestReadActive(t);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
function isPending(e) {
|
|
581
|
+
const t = pendingCheckActive;
|
|
582
|
+
const n = pendingProbe;
|
|
583
|
+
setPendingCheckActive(true);
|
|
584
|
+
const i = pendingProbe = {
|
|
585
|
+
found: false,
|
|
586
|
+
sources: new Set,
|
|
587
|
+
freshReads: new Set,
|
|
588
|
+
suppressed: []
|
|
589
|
+
};
|
|
590
|
+
const collectPending = () => {
|
|
591
|
+
setPendingCheckActive(false);
|
|
592
|
+
// Companion reads are mode-neutral plumbing: under an outer latest()
|
|
593
|
+
// (isPending inside a latest window — #3104's memo shape) leaving latest
|
|
594
|
+
// mode active dispatched these reads through latestRead, which built a
|
|
595
|
+
// SHADOW OF THE PENDING SIGNAL itself. The next updatePendingSignal then
|
|
596
|
+
// wrote that companion-on-companion from inside a recompute
|
|
597
|
+
// (syncCompanions → setSignal on a shadow created without ownedWrite)
|
|
598
|
+
// and halted dev with the owned-scope write guard. The creation paths
|
|
599
|
+
// (getLatestValueComputed / getPendingSignal) already suspend both
|
|
600
|
+
// modes; this read site must too.
|
|
601
|
+
const e = latestReadActive;
|
|
602
|
+
setLatestReadActive(false);
|
|
603
|
+
try {
|
|
604
|
+
i.sources.forEach(e => {
|
|
605
|
+
if (read(getPendingSignal(e))) {
|
|
606
|
+
if (!i.freshReads.has(e)) i.found = true; else i.suppressed.push(e);
|
|
607
|
+
}
|
|
608
|
+
});
|
|
609
|
+
} finally {
|
|
610
|
+
setLatestReadActive(e);
|
|
611
|
+
setPendingCheckActive(true);
|
|
612
|
+
}
|
|
613
|
+
// A "not pending" verdict that exists only because this reader saw the
|
|
614
|
+
// fresh held value is provisional: if the write turns out NOT to commit
|
|
615
|
+
// this flush (a downstream async pends and holds it), the suppression was
|
|
616
|
+
// wrong and the wrapper must re-ask (#3028). Remember who to wake — the
|
|
617
|
+
// async registration (GlobalQueue.notify) triggers wakeSuppressedProbes.
|
|
618
|
+
if (!i.found && i.suppressed.length && context && typeof context.ht === "function") {
|
|
619
|
+
for (const e of i.suppressed) {
|
|
620
|
+
let t = suppressedProbes.get(e);
|
|
621
|
+
if (!t) suppressedProbes.set(e, t = new Set);
|
|
622
|
+
t.add(context);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
};
|
|
626
|
+
try {
|
|
627
|
+
e();
|
|
628
|
+
collectPending();
|
|
629
|
+
return i.found;
|
|
630
|
+
} catch (e) {
|
|
631
|
+
collectPending();
|
|
632
|
+
if (e instanceof NotReadyError) {
|
|
633
|
+
const t = !!(e.source?.S & STATUS_UNINITIALIZED);
|
|
634
|
+
if (i.found && !t) return true;
|
|
635
|
+
if (context && t) throw e;
|
|
636
|
+
}
|
|
637
|
+
return i.found;
|
|
638
|
+
} finally {
|
|
639
|
+
setPendingCheckActive(t);
|
|
640
|
+
pendingProbe = n;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// Hook installation (same late-binding pattern as GlobalQueue._update /
|
|
645
|
+
// _propagateAffects): core call sites fire these behind the same guards the
|
|
646
|
+
// direct calls used, so behavior is identical once this module loads.
|
|
647
|
+
GlobalQueue.De = syncCompanions;
|
|
648
|
+
|
|
649
|
+
GlobalQueue.le = updatePendingSignal;
|
|
650
|
+
|
|
651
|
+
GlobalQueue.be = updateChildCompanions;
|
|
652
|
+
|
|
653
|
+
GlobalQueue.Qn = snapCompanionsToState;
|
|
654
|
+
|
|
655
|
+
GlobalQueue.Cn = latestRead;
|
|
656
|
+
|
|
657
|
+
GlobalQueue.pn = pendingCheckRead;
|
|
658
|
+
|
|
659
|
+
GlobalQueue.Pn = recordFreshRead;
|
|
660
|
+
|
|
661
|
+
GlobalQueue.bt = applyReask;
|
|
662
|
+
|
|
663
|
+
GlobalQueue.k = repollDownstreamVerdicts;
|
|
664
|
+
|
|
665
|
+
GlobalQueue.Kn = witnessAffects;
|
|
666
|
+
|
|
667
|
+
GlobalQueue.Yn = wakeSuppressedProbes;
|
|
668
|
+
|
|
669
|
+
export { isPending, latest };
|