@solidjs/signals 2.0.0-rc.0 → 2.0.0-rc.2
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/README.md +1 -1
- package/dist/dev.js +4287 -2803
- package/dist/node.cjs +4563 -3756
- package/dist/prod/affects.js +16 -16
- package/dist/prod/boundaries.js +90 -90
- package/dist/prod/core/action.js +3 -3
- package/dist/prod/core/async.js +234 -178
- package/dist/prod/core/constants.js +39 -1
- package/dist/prod/core/core.js +362 -243
- package/dist/prod/core/effect.js +56 -56
- package/dist/prod/core/external.js +4 -4
- package/dist/prod/core/graph.js +65 -54
- package/dist/prod/core/heap.js +47 -39
- package/dist/prod/core/invariants.js +3 -2
- package/dist/prod/core/lanes.js +41 -34
- package/dist/prod/core/optimistic.js +78 -58
- package/dist/prod/core/owner.js +101 -100
- package/dist/prod/core/scheduler.js +249 -192
- package/dist/prod/core/verdict.js +185 -78
- package/dist/prod/index.js +7 -7
- package/dist/prod/map.js +108 -106
- package/dist/prod/signals.js +20 -1
- package/dist/prod/store/index.js +36 -0
- package/dist/prod/store/next/optimistic.js +385 -0
- package/dist/prod/store/next/projection.js +172 -0
- package/dist/prod/store/next/reconcile.js +331 -0
- package/dist/prod/store/next/store.js +1499 -0
- package/dist/prod/store/next/target.js +20 -0
- package/dist/prod/store/store.js +126 -882
- package/dist/prod/store/utils.js +59 -186
- package/dist/types/core/attribution-hooks.d.ts +52 -0
- package/dist/types/core/attribution.d.ts +186 -0
- package/dist/types/core/constants.d.ts +26 -0
- package/dist/types/core/core.d.ts +16 -1
- package/dist/types/core/dev.d.ts +20 -3
- package/dist/types/core/graph.d.ts +1 -0
- package/dist/types/core/lanes.d.ts +4 -16
- package/dist/types/core/scheduler.d.ts +12 -2
- package/dist/types/core/types.d.ts +85 -41
- package/dist/types/signals.d.ts +19 -0
- package/dist/types/store/index.d.ts +15 -5
- package/dist/types/store/next/optimistic.d.ts +20 -0
- package/dist/types/store/next/projection.d.ts +8 -0
- package/dist/types/store/next/reconcile.d.ts +3 -0
- package/dist/types/store/next/store.d.ts +77 -0
- package/dist/types/store/next/target.d.ts +117 -0
- package/dist/types/store/store.d.ts +26 -101
- package/dist/types/store/utils.d.ts +0 -40
- package/dist/types-cjs/core/attribution-hooks.d.cts +52 -0
- package/dist/types-cjs/core/attribution.d.cts +186 -0
- package/dist/types-cjs/core/constants.d.cts +26 -0
- package/dist/types-cjs/core/core.d.cts +16 -1
- package/dist/types-cjs/core/dev.d.cts +20 -3
- package/dist/types-cjs/core/graph.d.cts +1 -0
- package/dist/types-cjs/core/lanes.d.cts +4 -16
- package/dist/types-cjs/core/scheduler.d.cts +12 -2
- package/dist/types-cjs/core/types.d.cts +85 -41
- package/dist/types-cjs/signals.d.cts +19 -0
- package/dist/types-cjs/store/index.d.cts +15 -5
- package/dist/types-cjs/store/next/optimistic.d.cts +20 -0
- package/dist/types-cjs/store/next/projection.d.cts +8 -0
- package/dist/types-cjs/store/next/reconcile.d.cts +3 -0
- package/dist/types-cjs/store/next/store.d.cts +77 -0
- package/dist/types-cjs/store/next/target.d.cts +117 -0
- package/dist/types-cjs/store/store.d.cts +26 -101
- package/dist/types-cjs/store/utils.d.cts +0 -40
- package/package.json +2 -1
- package/dist/prod/store/optimistic.js +0 -217
- package/dist/prod/store/projection.js +0 -231
- package/dist/prod/store/reconcile.js +0 -707
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { NOT_PENDING, REACTIVE_DISPOSED, REACTIVE_DIRTY, REACTIVE_CHECK, unwrapOverride, REACTIVE_ZOMBIE, STATUS_UNINITIALIZED, STATUS_PENDING, REACTIVE_MANUAL_WRITE, STATUS_ERROR, REACTIVE_RECOMPUTING_DEPS } from "./constants.js";
|
|
1
|
+
import { NOT_PENDING, REACTIVE_DISPOSED, REACTIVE_DIRTY, REACTIVE_CHECK, unwrapOverride, REACTIVE_ZOMBIE, STATUS_UNINITIALIZED, STATUS_PENDING, REACTIVE_OPTIMISTIC_DIRTY, REACTIVE_MANUAL_WRITE, CONFIG_HAS_COMPANIONS, CONFIG_CHILD_COMPANIONS, STATUS_ERROR, REACTIVE_RECOMPUTING_DEPS } from "./constants.js";
|
|
2
2
|
|
|
3
|
-
import { setSignal, prepareComputed, read, context, stale, currentOptimisticLane, tracking, setLatestReadActive, setContextInternal, optimisticComputed, setPendingCheckActive, latestReadActive, optimisticSignal, pendingCheckActive } from "./core.js";
|
|
3
|
+
import { setSignal, prepareComputed, read, context, stale, currentOptimisticLane, tracking, ext, setLatestReadActive, setContextInternal, optimisticComputed, setPendingCheckActive, latestReadActive, optimisticSignal, pendingCheckActive } from "./core.js";
|
|
4
4
|
|
|
5
5
|
import { NotReadyError } from "./error.js";
|
|
6
6
|
|
|
7
7
|
import { link } from "./graph.js";
|
|
8
8
|
|
|
9
|
-
import { insertIntoHeap, queueFor, markHeap } from "./heap.js";
|
|
9
|
+
import { insertIntoHeap, queueFor, markHeap, enqueueSub } from "./heap.js";
|
|
10
10
|
|
|
11
11
|
import "./invariants.js";
|
|
12
12
|
|
|
13
|
-
import { findLane, hasActiveOverride } from "./lanes.js";
|
|
13
|
+
import { findLane, hasActiveOverride, assignOrMergeLane } from "./lanes.js";
|
|
14
14
|
|
|
15
15
|
import { installOptimisticEngine } from "./optimistic.js";
|
|
16
16
|
|
|
17
|
-
import { GlobalQueue,
|
|
17
|
+
import { GlobalQueue, insertSubs, schedule, activeTransition, currentTransition, activeAffectsMarks } from "./scheduler.js";
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* The isPending()/latest() verdict layer, moved out of core.ts. Importing this
|
|
@@ -28,19 +28,45 @@ installOptimisticEngine();
|
|
|
28
28
|
|
|
29
29
|
let pendingProbe = null;
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Probes whose verdict was suppressed by the fresh-read pairing rule while
|
|
33
|
+
* the held write's fate was still undecided (see recordFreshRead /
|
|
34
|
+
* wakeSuppressedProbes): held node → the wrapper computeds that probed it.
|
|
35
|
+
* Entries die with the hold — the commit/revert snap clears them.
|
|
36
|
+
*/ const suppressedProbes = new Map;
|
|
37
|
+
|
|
31
38
|
/**
|
|
32
39
|
* Get or create the pending signal for a node (lazy).
|
|
33
40
|
* Used by isPending() to track pending state reactively.
|
|
34
|
-
*/
|
|
35
|
-
|
|
41
|
+
*/
|
|
42
|
+
/** #3038: register a companion-carrying firewall child on its firewall's
|
|
43
|
+
* companion set and arm the post-recompute snap (CONFIG_CHILD_COMPANIONS is
|
|
44
|
+
* the one-load gate at the call sites). The snap then iterates exactly the
|
|
45
|
+
* children someone asked verdicts of — O(companions) — never the full
|
|
46
|
+
* `_child` chain, which carries one node per materialized leaf (the
|
|
47
|
+
* O(all-leaves-ever-read)-per-update pathology). Entries are permanent like
|
|
48
|
+
* the companions themselves; a store with no leaf-level isPending()/latest()
|
|
49
|
+
* reads never allocates the set or pays the walk. */ function markFirewallChildCompanions(e) {
|
|
50
|
+
const t = e.lt;
|
|
51
|
+
if (!t) return;
|
|
52
|
+
t.T |= CONFIG_CHILD_COMPANIONS;
|
|
53
|
+
(ext(t).Nt ??= new Set).add(e);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function getPendingSignal(e) {
|
|
57
|
+
let t = e.o?.Ge;
|
|
58
|
+
if (!t) {
|
|
36
59
|
// Start false, write true if pending - ensures reversion returns to false
|
|
37
|
-
|
|
60
|
+
t = optimisticSignal(false, {
|
|
38
61
|
ownedWrite: true
|
|
39
62
|
});
|
|
40
|
-
e.
|
|
41
|
-
|
|
63
|
+
ext(e).Ge = t;
|
|
64
|
+
e.T |= CONFIG_HAS_COMPANIONS;
|
|
65
|
+
markFirewallChildCompanions(e);
|
|
66
|
+
ext(t).It = e;
|
|
67
|
+
if (computePendingState(e)) setSignal(t, true);
|
|
42
68
|
}
|
|
43
|
-
return
|
|
69
|
+
return t;
|
|
44
70
|
}
|
|
45
71
|
|
|
46
72
|
function collectPendingSources(e) {
|
|
@@ -70,7 +96,7 @@ function collectPendingSources(e) {
|
|
|
70
96
|
* (`_pendingObserver`) are skipped so an `isPending` wrapper memo never
|
|
71
97
|
* inherits the coverage it reports on.
|
|
72
98
|
*/ function markWalk(e, t) {
|
|
73
|
-
if (e.t) return true;
|
|
99
|
+
if (e.o?.t) return true;
|
|
74
100
|
// A real error outranks an inherited mark (A16/A24c): an errored node
|
|
75
101
|
// answers probes with its error, not a coverage verdict, and coverage does
|
|
76
102
|
// not flow through it — matching the rails' behavior, where propagation
|
|
@@ -86,10 +112,10 @@ function collectPendingSources(e) {
|
|
|
86
112
|
// pass's dependency set — walking past it would read dropped deps and
|
|
87
113
|
// latch a stale verdict on the companion.
|
|
88
114
|
const i = e;
|
|
89
|
-
const r = i.
|
|
115
|
+
const r = i.ie & REACTIVE_RECOMPUTING_DEPS ? i.Ye : undefined;
|
|
90
116
|
if (r !== null) {
|
|
91
|
-
for (let e = i.
|
|
92
|
-
if (!e.
|
|
117
|
+
for (let e = i.nt ?? null; e !== null; e = e.it) {
|
|
118
|
+
if (!e.me && markWalk(e.ut, t)) return true;
|
|
93
119
|
if (e === r) break;
|
|
94
120
|
}
|
|
95
121
|
}
|
|
@@ -101,11 +127,11 @@ function collectPendingSources(e) {
|
|
|
101
127
|
}
|
|
102
128
|
|
|
103
129
|
function quietPending(e) {
|
|
104
|
-
if (e.
|
|
105
|
-
for (const t of e.
|
|
130
|
+
if (e.o?.le) {
|
|
131
|
+
for (const t of e.o.le) if (!t.o?.pe) return false;
|
|
106
132
|
return true;
|
|
107
133
|
}
|
|
108
|
-
return e.
|
|
134
|
+
return e.o?.pe ?? false;
|
|
109
135
|
}
|
|
110
136
|
|
|
111
137
|
// NOTE: a loadingValue node's open loading window (_loading) is verdict-quiet
|
|
@@ -121,47 +147,47 @@ function newQuestionInFlight(e) {
|
|
|
121
147
|
|
|
122
148
|
function computePendingState(e) {
|
|
123
149
|
const t = e;
|
|
124
|
-
if (t.
|
|
150
|
+
if (t.ie & REACTIVE_DISPOSED) return false;
|
|
125
151
|
// Mark coverage is transitive by dep-graph reachability: a latest() shadow
|
|
126
152
|
// reaches its owner (and a store leaf its firewall) through its own deps,
|
|
127
153
|
// so the one walk covers direct marks, derivation, and companion chains.
|
|
128
154
|
if (markCovered(e)) return true;
|
|
129
155
|
const n = e.lt;
|
|
130
|
-
if (e.
|
|
131
|
-
const t = e.
|
|
156
|
+
if (e.o?.It) {
|
|
157
|
+
const t = e.o?.It;
|
|
132
158
|
const n = t.lt || t;
|
|
133
159
|
return newQuestionInFlight(n);
|
|
134
160
|
}
|
|
135
|
-
if (n && e.
|
|
136
|
-
return !!(n.
|
|
161
|
+
if (n && e.Pe !== NOT_PENDING && !hasActiveOverride(e)) {
|
|
162
|
+
return !!(n.ie & REACTIVE_MANUAL_WRITE) || !n.o?.Ee && !(n.S & STATUS_PENDING) || !!(n.S & STATUS_PENDING) && quietPending(n);
|
|
137
163
|
}
|
|
138
164
|
// `!comp._loading`: a hold created while the loading window is still open is
|
|
139
165
|
// the window's own landing in flight to its commit — verdict-quiet like the
|
|
140
166
|
// rest of the window (the UNINITIALIZED check suppresses exactly this frame
|
|
141
167
|
// for windowless first loads; born-committed nodes need their own gate, #2990).
|
|
142
|
-
if (e.
|
|
143
|
-
if (hasActiveOverride(e)) return !e.
|
|
168
|
+
if (e.Pe !== NOT_PENDING && !(t.S & STATUS_UNINITIALIZED) && !t.Ie) {
|
|
169
|
+
if (hasActiveOverride(e)) return !e.Ue || !e.Ue(e.Pe, unwrapOverride(e.o?.De));
|
|
144
170
|
return true;
|
|
145
171
|
}
|
|
146
172
|
return newQuestionInFlight(t);
|
|
147
173
|
}
|
|
148
174
|
|
|
149
175
|
function syncCompanions(e, t) {
|
|
150
|
-
if (e.
|
|
151
|
-
if (e.
|
|
176
|
+
if (e.o?.Ge) updatePendingSignal(e);
|
|
177
|
+
if (e.o?.ge) setSignal(e.o?.ge, t);
|
|
152
178
|
}
|
|
153
179
|
|
|
154
180
|
function updatePendingSignal(e) {
|
|
155
|
-
if (e.
|
|
156
|
-
setSignal(e.
|
|
181
|
+
if (e.o?.Ge) {
|
|
182
|
+
setSignal(e.o?.Ge, computePendingState(e));
|
|
157
183
|
}
|
|
158
|
-
if (e.
|
|
184
|
+
if (e.o?.ge) updatePendingSignal(e.o?.ge);
|
|
159
185
|
}
|
|
160
186
|
|
|
161
187
|
function updateChildCompanions(e) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
188
|
+
const t = e.o?.Nt;
|
|
189
|
+
if (t === undefined) return;
|
|
190
|
+
for (const e of t) updatePendingSignal(e);
|
|
165
191
|
}
|
|
166
192
|
|
|
167
193
|
/**
|
|
@@ -178,31 +204,65 @@ function updateChildCompanions(e) {
|
|
|
178
204
|
const visit = e => {
|
|
179
205
|
if (i.has(e)) return;
|
|
180
206
|
i.add(e);
|
|
181
|
-
if (e.
|
|
182
|
-
for (let t = e.
|
|
183
|
-
for (let t = e.
|
|
207
|
+
if (e.o?.Ge || e.o?.ge) n(e);
|
|
208
|
+
for (let t = e.u; t !== null; t = t.fe) visit(t.ae);
|
|
209
|
+
for (let t = e.o?.l ?? null; t !== null; t = t.ce) {
|
|
184
210
|
visit(t);
|
|
185
211
|
}
|
|
186
212
|
};
|
|
187
213
|
visit(e);
|
|
188
214
|
}
|
|
189
215
|
|
|
216
|
+
/**
|
|
217
|
+
* The correction half of the provisional fresh-read suppression (see
|
|
218
|
+
* collectPending): fired from the sanctioned async-registration site
|
|
219
|
+
* (GlobalQueue.notify) when a transaction gains an in-flight async blocker.
|
|
220
|
+
* Every probe that returned "not pending" purely because it read a held
|
|
221
|
+
* value belonging to that transaction re-runs — its re-probe now sees the
|
|
222
|
+
* live blocker through heldAwaitingAsync and lands the true verdict. The
|
|
223
|
+
* wake mirrors a companion write's own notification (optimistic-dirty on the
|
|
224
|
+
* companion's lane) so the corrected verdict commits and flushes immediately
|
|
225
|
+
* instead of being held with the transaction it reports on.
|
|
226
|
+
*/ function wakeSuppressedProbes(e) {
|
|
227
|
+
if (suppressedProbes.size === 0) return;
|
|
228
|
+
let t = false;
|
|
229
|
+
for (const [n, i] of suppressedProbes) {
|
|
230
|
+
const r = n._e;
|
|
231
|
+
const s = r ? currentTransition(r) : null;
|
|
232
|
+
if (!s) {
|
|
233
|
+
suppressedProbes.delete(n);
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
if (s !== e) continue;
|
|
237
|
+
suppressedProbes.delete(n);
|
|
238
|
+
const o = n.o?.Ge?.o?.Be;
|
|
239
|
+
for (const e of i) {
|
|
240
|
+
if (e.ie & REACTIVE_DISPOSED) continue;
|
|
241
|
+
e.ie |= REACTIVE_OPTIMISTIC_DIRTY;
|
|
242
|
+
if (o) assignOrMergeLane(e, o); else if (e.o !== null) e.o.Be = undefined;
|
|
243
|
+
enqueueSub(e);
|
|
244
|
+
t = true;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (t) schedule();
|
|
248
|
+
}
|
|
249
|
+
|
|
190
250
|
function snapCompanionsToState(e) {
|
|
191
|
-
|
|
192
|
-
|
|
251
|
+
suppressedProbes.size !== 0 && suppressedProbes.delete(e);
|
|
252
|
+
const t = e.o?.Ge;
|
|
253
|
+
if (t && (t.o?.De === undefined || t.o?.De === NOT_PENDING)) {
|
|
193
254
|
const n = computePendingState(e);
|
|
194
|
-
if (t.
|
|
195
|
-
t.
|
|
196
|
-
t.
|
|
197
|
-
t.de = clock;
|
|
255
|
+
if (t.be !== n || t.Pe !== NOT_PENDING) {
|
|
256
|
+
t.be = n;
|
|
257
|
+
t.Pe = NOT_PENDING;
|
|
198
258
|
insertSubs(t);
|
|
199
259
|
schedule();
|
|
200
260
|
}
|
|
201
261
|
}
|
|
202
|
-
const n = e.
|
|
203
|
-
if (n && !(n.
|
|
204
|
-
if ((n.
|
|
205
|
-
n.
|
|
262
|
+
const n = e.o?.ge;
|
|
263
|
+
if (n && !(n.ie & REACTIVE_DISPOSED)) {
|
|
264
|
+
if ((n.o?.De === undefined || n.o?.De === NOT_PENDING) && n.Pe === NOT_PENDING && !Object.is(n.be, e.be) && !(n.ie & (REACTIVE_DIRTY | REACTIVE_CHECK))) {
|
|
265
|
+
n.ie |= REACTIVE_DIRTY;
|
|
206
266
|
insertIntoHeap(n, queueFor(n));
|
|
207
267
|
insertSubs(n);
|
|
208
268
|
schedule();
|
|
@@ -212,29 +272,38 @@ function snapCompanionsToState(e) {
|
|
|
212
272
|
}
|
|
213
273
|
|
|
214
274
|
function getLatestValueComputed(e) {
|
|
215
|
-
|
|
216
|
-
|
|
275
|
+
let t = e.o?.ge;
|
|
276
|
+
if (!t) {
|
|
277
|
+
const n = latestReadActive;
|
|
217
278
|
setLatestReadActive(false);
|
|
218
|
-
const
|
|
279
|
+
const i = pendingCheckActive;
|
|
219
280
|
setPendingCheckActive(false);
|
|
220
|
-
const
|
|
281
|
+
const r = context;
|
|
221
282
|
setContextInternal(null);
|
|
222
283
|
// Detach from owner so it isn't disposed with effects
|
|
223
|
-
|
|
224
|
-
e.
|
|
284
|
+
t = optimisticComputed(() => read(e));
|
|
285
|
+
ext(e).ge = t;
|
|
286
|
+
e.T |= CONFIG_HAS_COMPANIONS;
|
|
287
|
+
markFirewallChildCompanions(e);
|
|
288
|
+
ext(t).It = e;
|
|
225
289
|
// Parent-child lane relationship
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
290
|
+
// Backfill an in-flight write (mirrors getPendingSignal): the companion is
|
|
291
|
+
// created lazily, possibly after the write was processed — syncCompanions
|
|
292
|
+
// only pushes into companions that already exist, so the first latest()
|
|
293
|
+
// read inside a held transition showed the committed value (#3041).
|
|
294
|
+
if (e.Pe !== NOT_PENDING && !hasActiveOverride(e)) setSignal(t, e.Pe);
|
|
295
|
+
setContextInternal(r);
|
|
296
|
+
setPendingCheckActive(i);
|
|
297
|
+
setLatestReadActive(n);
|
|
229
298
|
}
|
|
230
|
-
return
|
|
299
|
+
return t;
|
|
231
300
|
}
|
|
232
301
|
|
|
233
302
|
/** The latest()-mode read path, installed as GlobalQueue._latestRead. */ function latestRead(e) {
|
|
234
303
|
const t = getLatestValueComputed(e);
|
|
235
304
|
const n = latestReadActive;
|
|
236
305
|
setLatestReadActive(false);
|
|
237
|
-
const i = e.
|
|
306
|
+
const i = e.o?.De !== undefined && e.o?.De !== NOT_PENDING ? unwrapOverride(e.o?.De) : e.be;
|
|
238
307
|
let r;
|
|
239
308
|
try {
|
|
240
309
|
// An untracked latest() read has no reading context, so read() never
|
|
@@ -243,7 +312,7 @@ function getLatestValueComputed(e) {
|
|
|
243
312
|
// until the flush (#2922). Mirror the tracked-read pull here: mark the
|
|
244
313
|
// queued staleness through the graph, then bring the shadow up to date.
|
|
245
314
|
const e = queueFor(t);
|
|
246
|
-
if (t.
|
|
315
|
+
if (t.Le >= e.xe && !(t.ie & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE))) {
|
|
247
316
|
markHeap(e);
|
|
248
317
|
prepareComputed(t, true);
|
|
249
318
|
}
|
|
@@ -255,8 +324,8 @@ function getLatestValueComputed(e) {
|
|
|
255
324
|
setLatestReadActive(n);
|
|
256
325
|
}
|
|
257
326
|
if (t.S & STATUS_PENDING) return i;
|
|
258
|
-
if (stale && currentOptimisticLane && t.
|
|
259
|
-
const e = findLane(t.
|
|
327
|
+
if (stale && currentOptimisticLane && t.o?.Be) {
|
|
328
|
+
const e = findLane(t.o?.Be);
|
|
260
329
|
const n = findLane(currentOptimisticLane);
|
|
261
330
|
if (e !== n && e.Ae.size > 0) {
|
|
262
331
|
return i;
|
|
@@ -266,33 +335,54 @@ function getLatestValueComputed(e) {
|
|
|
266
335
|
// speculative value in _pendingValue; a contextless read() only surfaces
|
|
267
336
|
// _value. Overrides stay authoritative (A17), and stale readers keep the
|
|
268
337
|
// other transition's committed view, matching read()'s own selection.
|
|
269
|
-
if (t.
|
|
338
|
+
if (t.Pe !== NOT_PENDING && !hasActiveOverride(t) && !(stale && t._e && activeTransition !== t._e)) return t.Pe;
|
|
270
339
|
return r;
|
|
271
340
|
}
|
|
272
341
|
|
|
273
342
|
/** The isPending()-probe read path, installed as GlobalQueue._pendingCheck. */ function pendingCheckRead(e, t, n, i) {
|
|
274
343
|
setPendingCheckActive(false);
|
|
275
|
-
if (typeof e.
|
|
344
|
+
if (typeof e.Se === "function") prepareComputed(e, true);
|
|
276
345
|
const r = n.S;
|
|
277
346
|
if (t && r & STATUS_PENDING && r & STATUS_UNINITIALIZED) {
|
|
278
347
|
if (tracking && e !== t) link(e, t);
|
|
279
348
|
setPendingCheckActive(true);
|
|
280
|
-
throw n._;
|
|
349
|
+
throw n.o?._;
|
|
281
350
|
}
|
|
282
351
|
collectPendingSources(e);
|
|
283
352
|
if (i) collectPendingSources(i);
|
|
284
353
|
setPendingCheckActive(true);
|
|
285
354
|
}
|
|
286
355
|
|
|
356
|
+
/**
|
|
357
|
+
* A held node whose transaction still has an async question in flight. The
|
|
358
|
+
* probe's fresh-read pairing rule (#2831 — "a reader that sees the fresh
|
|
359
|
+
* value must not also be told it is pending") only applies to LANDED answers
|
|
360
|
+
* awaiting reveal; while the answer is still computing, the fresh value the
|
|
361
|
+
* reader saw is an input, and pending remains the truth for every reader
|
|
362
|
+
* (#3028).
|
|
363
|
+
*/ function heldAwaitingAsync(e) {
|
|
364
|
+
const t = e._e;
|
|
365
|
+
const n = t ? currentTransition(t) : null;
|
|
366
|
+
if (!n || n.an) return false;
|
|
367
|
+
for (const [e, t] of n.Ne) {
|
|
368
|
+
if (t.size && e.S & STATUS_PENDING && e.o?._?.source === e) return true;
|
|
369
|
+
}
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
372
|
+
|
|
287
373
|
function recordFreshRead(e, t) {
|
|
288
|
-
if (pendingProbe !== null && e.
|
|
374
|
+
if (pendingProbe !== null && e.Pe !== NOT_PENDING && t === e.Pe) {
|
|
375
|
+
if (heldAwaitingAsync(e)) return;
|
|
376
|
+
pendingProbe.freshReads.add(e);
|
|
377
|
+
}
|
|
289
378
|
}
|
|
290
379
|
|
|
291
380
|
function applyReask(e, t) {
|
|
292
381
|
const n = !!(e.S & STATUS_PENDING);
|
|
293
|
-
const i = t && !(n && !e.
|
|
294
|
-
const r = n && e.
|
|
295
|
-
|
|
382
|
+
const i = t && !(n && !e.o?.pe);
|
|
383
|
+
const r = n && (e.o?.pe ?? false) !== i;
|
|
384
|
+
// Allocation-free for the quiet case: false is the extension default.
|
|
385
|
+
if (i) ext(e).pe = true; else if (e.o !== null) e.o.pe = false;
|
|
296
386
|
return r;
|
|
297
387
|
}
|
|
298
388
|
|
|
@@ -313,17 +403,32 @@ function isPending(e) {
|
|
|
313
403
|
const i = pendingProbe = {
|
|
314
404
|
found: false,
|
|
315
405
|
sources: new Set,
|
|
316
|
-
freshReads: new Set
|
|
406
|
+
freshReads: new Set,
|
|
407
|
+
suppressed: []
|
|
317
408
|
};
|
|
318
409
|
const collectPending = () => {
|
|
319
410
|
setPendingCheckActive(false);
|
|
320
411
|
try {
|
|
321
412
|
i.sources.forEach(e => {
|
|
322
|
-
if (read(getPendingSignal(e))
|
|
413
|
+
if (read(getPendingSignal(e))) {
|
|
414
|
+
if (!i.freshReads.has(e)) i.found = true; else i.suppressed.push(e);
|
|
415
|
+
}
|
|
323
416
|
});
|
|
324
417
|
} finally {
|
|
325
418
|
setPendingCheckActive(true);
|
|
326
419
|
}
|
|
420
|
+
// A "not pending" verdict that exists only because this reader saw the
|
|
421
|
+
// fresh held value is provisional: if the write turns out NOT to commit
|
|
422
|
+
// this flush (a downstream async pends and holds it), the suppression was
|
|
423
|
+
// wrong and the wrapper must re-ask (#3028). Remember who to wake — the
|
|
424
|
+
// async registration (GlobalQueue.notify) triggers wakeSuppressedProbes.
|
|
425
|
+
if (!i.found && i.suppressed.length && context && typeof context.Se === "function") {
|
|
426
|
+
for (const e of i.suppressed) {
|
|
427
|
+
let t = suppressedProbes.get(e);
|
|
428
|
+
if (!t) suppressedProbes.set(e, t = new Set);
|
|
429
|
+
t.add(context);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
327
432
|
};
|
|
328
433
|
try {
|
|
329
434
|
e();
|
|
@@ -346,24 +451,26 @@ function isPending(e) {
|
|
|
346
451
|
// Hook installation (same late-binding pattern as GlobalQueue._update /
|
|
347
452
|
// _propagateAffects): core call sites fire these behind the same guards the
|
|
348
453
|
// direct calls used, so behavior is identical once this module loads.
|
|
349
|
-
GlobalQueue.
|
|
454
|
+
GlobalQueue.Oe = syncCompanions;
|
|
350
455
|
|
|
351
|
-
GlobalQueue.
|
|
456
|
+
GlobalQueue.de = updatePendingSignal;
|
|
352
457
|
|
|
353
|
-
GlobalQueue.
|
|
458
|
+
GlobalQueue.ye = updateChildCompanions;
|
|
354
459
|
|
|
355
460
|
GlobalQueue.un = snapCompanionsToState;
|
|
356
461
|
|
|
357
|
-
GlobalQueue.
|
|
462
|
+
GlobalQueue.Pt = latestRead;
|
|
463
|
+
|
|
464
|
+
GlobalQueue.Dt = pendingCheckRead;
|
|
358
465
|
|
|
359
|
-
GlobalQueue.
|
|
466
|
+
GlobalQueue.Ht = recordFreshRead;
|
|
360
467
|
|
|
361
|
-
GlobalQueue.
|
|
468
|
+
GlobalQueue.Je = applyReask;
|
|
362
469
|
|
|
363
|
-
GlobalQueue.
|
|
470
|
+
GlobalQueue.p = repollDownstreamVerdicts;
|
|
364
471
|
|
|
365
|
-
GlobalQueue.
|
|
472
|
+
GlobalQueue.Bt = witnessAffects;
|
|
366
473
|
|
|
367
|
-
GlobalQueue.
|
|
474
|
+
GlobalQueue.wt = wakeSuppressedProbes;
|
|
368
475
|
|
|
369
476
|
export { isPending, latest };
|
package/dist/prod/index.js
CHANGED
|
@@ -26,19 +26,19 @@ export { affects } from "./affects.js";
|
|
|
26
26
|
|
|
27
27
|
export { mapArray, repeat } from "./map.js";
|
|
28
28
|
|
|
29
|
-
export {
|
|
29
|
+
export { createStore, deep, reconcile, snapshot } from "./store/index.js";
|
|
30
30
|
|
|
31
|
-
export {
|
|
31
|
+
export { createErrorBoundary, createLoadingBoundary, createRevealOrder, flatten } from "./boundaries.js";
|
|
32
32
|
|
|
33
|
-
export {
|
|
33
|
+
export { $PROXY, $TARGET, $TRACK, isWrappable } from "./store/store.js";
|
|
34
34
|
|
|
35
|
-
export {
|
|
35
|
+
export { createOptimisticStoreNext as createOptimisticStore } from "./store/next/optimistic.js";
|
|
36
36
|
|
|
37
|
-
export {
|
|
37
|
+
export { createProjectionNext as createProjection } from "./store/next/projection.js";
|
|
38
38
|
|
|
39
|
-
export {
|
|
39
|
+
export { merge, omit } from "./store/utils.js";
|
|
40
40
|
|
|
41
|
-
export {
|
|
41
|
+
export { storePath } from "./store/storePath.js";
|
|
42
42
|
|
|
43
43
|
const DEV = undefined;
|
|
44
44
|
|