@solidjs/signals 2.0.0-rc.0 → 2.0.0-rc.1
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 +2622 -2438
- package/dist/node.cjs +3770 -3571
- package/dist/prod/core/async.js +186 -132
- package/dist/prod/core/core.js +118 -107
- package/dist/prod/core/effect.js +13 -13
- package/dist/prod/core/external.js +1 -1
- package/dist/prod/core/graph.js +8 -8
- package/dist/prod/core/heap.js +24 -24
- package/dist/prod/core/lanes.js +3 -3
- package/dist/prod/core/optimistic.js +38 -21
- package/dist/prod/core/owner.js +21 -21
- package/dist/prod/core/scheduler.js +55 -47
- package/dist/prod/core/verdict.js +40 -40
- package/dist/prod/index.js +7 -7
- package/dist/prod/map.js +81 -79
- package/dist/prod/signals.js +20 -1
- package/dist/prod/store/index.js +36 -0
- package/dist/prod/store/next/optimistic.js +377 -0
- package/dist/prod/store/next/projection.js +172 -0
- package/dist/prod/store/next/reconcile.js +327 -0
- package/dist/prod/store/next/store.js +1232 -0
- package/dist/prod/store/next/target.js +20 -0
- package/dist/prod/store/store.js +123 -879
- package/dist/prod/store/utils.js +59 -186
- package/dist/types/core/core.d.ts +8 -0
- package/dist/types/core/dev.d.ts +1 -1
- package/dist/types/core/scheduler.d.ts +4 -2
- 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 +24 -0
- package/dist/types/store/next/reconcile.d.ts +3 -0
- package/dist/types/store/next/store.d.ts +71 -0
- package/dist/types/store/next/target.d.ts +99 -0
- package/dist/types/store/store.d.ts +26 -101
- package/dist/types/store/utils.d.ts +0 -40
- package/dist/types-cjs/core/core.d.cts +8 -0
- package/dist/types-cjs/core/dev.d.cts +1 -1
- package/dist/types-cjs/core/scheduler.d.cts +4 -2
- 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 +24 -0
- package/dist/types-cjs/store/next/reconcile.d.cts +3 -0
- package/dist/types-cjs/store/next/store.d.cts +71 -0
- package/dist/types-cjs/store/next/target.d.cts +99 -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
package/dist/prod/core/async.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { STATUS_UNINITIALIZED, STATUS_ERROR, STATUS_PENDING, REACTIVE_DIRTY, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE } from "./constants.js";
|
|
1
|
+
import { STATUS_UNINITIALIZED, STATUS_ERROR, STATUS_PENDING, REACTIVE_DIRTY, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE, REACTIVE_DISPOSED } from "./constants.js";
|
|
2
2
|
|
|
3
|
-
import { untrack,
|
|
3
|
+
import { untrack, setSignal, context } from "./core.js";
|
|
4
4
|
|
|
5
5
|
import "./invariants.js";
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { StatusError, NotReadyError } from "./error.js";
|
|
8
8
|
|
|
9
9
|
import { trimStaleDeps, unobserved } from "./graph.js";
|
|
10
10
|
|
|
11
11
|
import { enqueueSub } from "./heap.js";
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import { hasActiveOverride, assignOrMergeLane, resolveLane, resolveTransition } from "./lanes.js";
|
|
14
14
|
|
|
15
15
|
import { cleanup } from "./owner.js";
|
|
16
16
|
|
|
17
|
-
import {
|
|
17
|
+
import { GlobalQueue, schedule, flush, queuePendingNode, insertSubs, clock, currentTransition, globalQueue } 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 —
|
|
@@ -45,7 +45,7 @@ function clearPendingSources(e) {
|
|
|
45
45
|
* with NO read-visible pending status, no downstream propagation, no
|
|
46
46
|
* transition, no lane registration. Commit #0 keeps serving.
|
|
47
47
|
*/ function parkLoadingWindow(e, n) {
|
|
48
|
-
e.
|
|
48
|
+
e.le = true;
|
|
49
49
|
if (n.source) addPendingSource(e, n.source);
|
|
50
50
|
// A settled error is the node's answer ("the error stays the answer until
|
|
51
51
|
// this retry can actually run") — the park must not replace it: reads
|
|
@@ -70,10 +70,10 @@ function setPendingError(e, n, t) {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
function forEachDependent(e, n) {
|
|
73
|
-
for (let t = e.o; t !== null; t = t.
|
|
73
|
+
for (let t = e.o; t !== null; t = t.ue) n(t.fe, t);
|
|
74
74
|
// `?? null`: affects() marks route plain signals (no `_child` slot) through here.
|
|
75
75
|
for (let t = e.u ?? null; t !== null; t = t.ae) {
|
|
76
|
-
for (let e = t.o; e !== null; e = e.
|
|
76
|
+
for (let e = t.o; e !== null; e = e.ue) n(e.fe, e);
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
|
|
@@ -145,35 +145,35 @@ function settlePendingSource(e) {
|
|
|
145
145
|
const r = new Set;
|
|
146
146
|
// Companion updates no-op without the verdict layer (null hook).
|
|
147
147
|
const o = GlobalQueue.Se;
|
|
148
|
-
const settle =
|
|
149
|
-
if (r.has(
|
|
150
|
-
r.add(
|
|
151
|
-
|
|
152
|
-
const
|
|
148
|
+
const settle = l => {
|
|
149
|
+
if (r.has(l) || !removePendingSource(l, e)) return;
|
|
150
|
+
r.add(l);
|
|
151
|
+
l.de = clock;
|
|
152
|
+
const u = l.oe?.values().next().value;
|
|
153
153
|
// STATUS_ERROR + pending sources only coexist via an errored loading
|
|
154
154
|
// window's park (notifyStatus(STATUS_ERROR) clears pending sources
|
|
155
155
|
// otherwise): the settled error stays the answer through the settle —
|
|
156
156
|
// nulling it here would have reads throw `null` until the re-enqueued
|
|
157
157
|
// retry lands, or lose it entirely if that retry parks again (#2989).
|
|
158
|
-
const
|
|
159
|
-
if (
|
|
160
|
-
if (!
|
|
161
|
-
o !== null && o(
|
|
158
|
+
const i = l.S & STATUS_ERROR;
|
|
159
|
+
if (u) {
|
|
160
|
+
if (!i) setPendingError(l, u);
|
|
161
|
+
o !== null && o(l);
|
|
162
162
|
} else {
|
|
163
|
-
|
|
164
|
-
if (!
|
|
165
|
-
o !== null && o(
|
|
166
|
-
if (
|
|
167
|
-
enqueueSub(
|
|
163
|
+
l.S &= ~STATUS_PENDING;
|
|
164
|
+
if (!i) setPendingError(l);
|
|
165
|
+
o !== null && o(l);
|
|
166
|
+
if (l.le) {
|
|
167
|
+
enqueueSub(l);
|
|
168
168
|
n = true;
|
|
169
169
|
}
|
|
170
|
-
|
|
170
|
+
l.le = false;
|
|
171
171
|
// Fully settled with nobody watching: release candidate (#2934). Checked
|
|
172
172
|
// again at release time — deferred so unobserved() can't unlink subs
|
|
173
173
|
// lists this walk is still iterating.
|
|
174
|
-
if (!
|
|
174
|
+
if (!l.o && l.T & CONFIG_AUTO_DISPOSE) (t ??= []).push(l);
|
|
175
175
|
}
|
|
176
|
-
forEachDependent(
|
|
176
|
+
forEachDependent(l, settle);
|
|
177
177
|
};
|
|
178
178
|
forEachDependent(e, settle);
|
|
179
179
|
// Release before the flush schedule below: unobserved() pulls the node back
|
|
@@ -203,7 +203,7 @@ function handleAsync(e, n, t) {
|
|
|
203
203
|
return n;
|
|
204
204
|
}
|
|
205
205
|
e.Te = n;
|
|
206
|
-
let
|
|
206
|
+
let l;
|
|
207
207
|
// Settle-time transition re-entry. The loading rail is invisible to
|
|
208
208
|
// transactions (#2933): a boundary-caught first load never registers as an
|
|
209
209
|
// async reporter, so its settle — the boundary's fallback -> content
|
|
@@ -253,15 +253,15 @@ function handleAsync(e, n, t) {
|
|
|
253
253
|
// with the new value, creating a fresh Promise that supersedes this one.
|
|
254
254
|
if (e.se & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY)) return;
|
|
255
255
|
settleTransition();
|
|
256
|
-
const
|
|
256
|
+
const l = !!(e.S & STATUS_UNINITIALIZED);
|
|
257
257
|
trimStaleDeps(e);
|
|
258
258
|
clearStatus(e);
|
|
259
|
-
const
|
|
260
|
-
if (
|
|
259
|
+
const u = resolveLane(e);
|
|
260
|
+
if (u) u.Ae.delete(e);
|
|
261
261
|
if (t) {
|
|
262
262
|
t(r);
|
|
263
|
-
if (
|
|
264
|
-
} else if (e.
|
|
263
|
+
if (l) clearStatus(e, true);
|
|
264
|
+
} else if (e.De !== undefined) {
|
|
265
265
|
// Optimistic node — resting OR covered by an active override — holds
|
|
266
266
|
// through the shared pending-node path, exactly like a plain async memo,
|
|
267
267
|
// so the commit clears STATUS_UNINITIALIZED (#2806) and elevation to
|
|
@@ -271,8 +271,8 @@ function handleAsync(e, n, t) {
|
|
|
271
271
|
// (A17 — every reader sees the override); the revert reveals whatever
|
|
272
272
|
// has committed by then, so corrections reveal atomically with their
|
|
273
273
|
// transition rather than escaping it.
|
|
274
|
-
if (e.
|
|
275
|
-
e.
|
|
274
|
+
if (e._e === NOT_PENDING) queuePendingNode(e);
|
|
275
|
+
e._e = r;
|
|
276
276
|
// The hold is a companion-visible write like any other (A13/A19): the
|
|
277
277
|
// clearStatus() above computed its verdict before the hold existed, so
|
|
278
278
|
// isPending must re-derive (the value is not final until commit — V1)
|
|
@@ -283,14 +283,14 @@ function handleAsync(e, n, t) {
|
|
|
283
283
|
GlobalQueue.Pe !== null && GlobalQueue.Pe(e, r);
|
|
284
284
|
if (!hasActiveOverride(e)) insertSubs(e);
|
|
285
285
|
e.de = clock;
|
|
286
|
-
} else if (
|
|
286
|
+
} else if (u) {
|
|
287
287
|
// Route through lane's effect queue for independent flushing
|
|
288
288
|
const n = e.Re;
|
|
289
|
-
const t = e.
|
|
290
|
-
const o = e.
|
|
289
|
+
const t = e.be;
|
|
290
|
+
const o = e.Ue;
|
|
291
291
|
try {
|
|
292
|
-
if (!n &&
|
|
293
|
-
e.
|
|
292
|
+
if (!n && l || !o || !o(r, t)) {
|
|
293
|
+
e.be = r;
|
|
294
294
|
e.de = clock;
|
|
295
295
|
// The latest() shadow write gives latest() effects independent lanes; the
|
|
296
296
|
// _pendingSignal update is a no-op repeat of the clearStatus() call above
|
|
@@ -320,7 +320,7 @@ function handleAsync(e, n, t) {
|
|
|
320
320
|
// held-value branch is window-gated, and commitPendingNode closes the
|
|
321
321
|
// window when the hold commits, so no one-frame isPending pulse can leak
|
|
322
322
|
// to live observers between the landing and its commit (#2990).
|
|
323
|
-
if (e.
|
|
323
|
+
if (e._e === NOT_PENDING) e.Ee = false;
|
|
324
324
|
settlePendingSource(e);
|
|
325
325
|
schedule();
|
|
326
326
|
flush();
|
|
@@ -340,94 +340,68 @@ function handleAsync(e, n, t) {
|
|
|
340
340
|
}
|
|
341
341
|
return false;
|
|
342
342
|
};
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
i = false;
|
|
363
|
-
if (r) {
|
|
364
|
-
// Settle through the same status path an async rejection uses, then
|
|
365
|
-
// unwind the in-progress synchronous read so the errored node isn't
|
|
366
|
-
// momentarily read as `undefined`.
|
|
367
|
-
handleError(o);
|
|
368
|
-
throw o;
|
|
369
|
-
} else if (!t) {
|
|
370
|
-
// Loading window: serve commit #0 instead of suspending. No transition
|
|
371
|
-
// is opened — first-flight work on a loadingValue node is loading-class
|
|
372
|
-
// (invisible to boundaries and transitions); the flight itself is
|
|
373
|
-
// already registered in _inFlight and lands through asyncWrite.
|
|
374
|
-
if (e.Ee) return e.Ue;
|
|
375
|
-
globalQueue.initTransition(resolveTransition(e));
|
|
376
|
-
throw new NotReadyError(context);
|
|
377
|
-
} else {
|
|
378
|
-
// Synchronously-resolved promise: the first real answer landed.
|
|
379
|
-
e.Ee = false;
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
if (r) {
|
|
383
|
-
const t = n[Symbol.asyncIterator]();
|
|
384
|
-
let r = false;
|
|
385
|
-
let o = false;
|
|
386
|
-
let i = true;
|
|
387
|
-
cleanup(() => {
|
|
388
|
-
if (o) return;
|
|
389
|
-
o = true;
|
|
343
|
+
// Consumes an AsyncIterable as this flight's value stream. Two postures:
|
|
344
|
+
// LIVE (called synchronously from this read — the compute returned an
|
|
345
|
+
// iterable directly, or a sync-settled thenable held one), where the
|
|
346
|
+
// initial drain may stash a sync first yield for the caller to return and
|
|
347
|
+
// close registration uses the ambient owner; and DEFERRED (the flattening
|
|
348
|
+
// path — a thenable resolved to an iterable in a later microtask), where
|
|
349
|
+
// there is no caller to serve and no ambient owner: sync-settled steps
|
|
350
|
+
// write through asyncWrite, and close registration goes through the slot
|
|
351
|
+
// the thenable branch pre-registered while it still owned the context.
|
|
352
|
+
// Returns whether a sync answer landed (first yield or empty completion) —
|
|
353
|
+
// meaningful only in the live posture.
|
|
354
|
+
const consumeIterator = (t, r) => {
|
|
355
|
+
const o = t[Symbol.asyncIterator]();
|
|
356
|
+
let u = false;
|
|
357
|
+
let i = false;
|
|
358
|
+
let s = !r;
|
|
359
|
+
const close = () => {
|
|
360
|
+
if (i) return;
|
|
361
|
+
i = true;
|
|
390
362
|
try {
|
|
391
|
-
const e =
|
|
363
|
+
const e = o.return?.();
|
|
392
364
|
if (isThenable(e)) e.then(undefined, () => {});
|
|
393
365
|
} catch {}
|
|
394
|
-
}
|
|
366
|
+
};
|
|
367
|
+
r ? r(close) : cleanup(close);
|
|
395
368
|
// Release check before each next pull: an unobserved lazy node must tear
|
|
396
|
-
// down (its
|
|
397
|
-
// stream forever with zero subscribers (#2935).
|
|
369
|
+
// down (its close above runs via disposal, closing the iterator) instead
|
|
370
|
+
// of pumping the stream forever with zero subscribers (#2935).
|
|
398
371
|
const iterateOrRelease = () => {
|
|
399
372
|
if (!settleAutodispose()) iterate();
|
|
400
373
|
};
|
|
401
374
|
const iterate = () => {
|
|
402
|
-
let
|
|
375
|
+
let t, r, f = false, a = false, c = true;
|
|
403
376
|
// Protocol tolerance, matching `for await`: `await` unwraps whatever
|
|
404
377
|
// next() returns — a thenable OR a bare IteratorResult. Real producers
|
|
405
378
|
// use the bare form as a promise-free fast path when a value is already
|
|
406
379
|
// buffered (seroval's deserialized streams do), so a bare result is
|
|
407
380
|
// assimilated as an already-settled step instead of crashing on `.then`.
|
|
408
|
-
const S =
|
|
381
|
+
const S = o.next();
|
|
409
382
|
const d = isThenable(S) ? S : {
|
|
410
383
|
then: e => void e(S)
|
|
411
384
|
};
|
|
412
|
-
d.then(
|
|
385
|
+
d.then(r => {
|
|
413
386
|
// The sync stash only serves the INITIAL drain (handleAsync's caller
|
|
414
387
|
// consumes syncValue / throws NotReady from it). A sync-settled step
|
|
415
388
|
// after an async gap — seroval buffering values between pulls, a
|
|
416
389
|
// sync-thenable producer mid-stream — has no caller reading the
|
|
417
390
|
// stash: it must write through the async path or the value is
|
|
418
|
-
// silently dropped.
|
|
419
|
-
|
|
420
|
-
|
|
391
|
+
// silently dropped. (The deferred posture never has a caller, so
|
|
392
|
+
// initialRead starts false there and everything writes through.)
|
|
393
|
+
if (c && s) {
|
|
394
|
+
t = r;
|
|
421
395
|
f = true;
|
|
422
|
-
if (
|
|
396
|
+
if (r.done) i = true;
|
|
423
397
|
} else if (e.Te !== n) {
|
|
424
398
|
return;
|
|
425
|
-
} else if (!
|
|
426
|
-
|
|
427
|
-
asyncWrite(
|
|
399
|
+
} else if (!r.done) {
|
|
400
|
+
u = true;
|
|
401
|
+
asyncWrite(r.value, iterateOrRelease);
|
|
428
402
|
} else {
|
|
429
|
-
|
|
430
|
-
if (
|
|
403
|
+
i = true;
|
|
404
|
+
if (u) {
|
|
431
405
|
schedule();
|
|
432
406
|
flush();
|
|
433
407
|
} else {
|
|
@@ -437,11 +411,11 @@ function handleAsync(e, n, t) {
|
|
|
437
411
|
settleAutodispose();
|
|
438
412
|
}
|
|
439
413
|
}, t => {
|
|
440
|
-
if (c) {
|
|
441
|
-
|
|
414
|
+
if (c && s) {
|
|
415
|
+
r = t;
|
|
442
416
|
a = true;
|
|
443
417
|
} else if (e.Te === n) {
|
|
444
|
-
|
|
418
|
+
i = true;
|
|
445
419
|
handleError(t);
|
|
446
420
|
settleAutodispose();
|
|
447
421
|
}
|
|
@@ -449,24 +423,104 @@ function handleAsync(e, n, t) {
|
|
|
449
423
|
c = false;
|
|
450
424
|
if (a) {
|
|
451
425
|
// Match the promise branch, but only rethrow during the initial read.
|
|
452
|
-
|
|
453
|
-
handleError(
|
|
454
|
-
if (
|
|
426
|
+
i = true;
|
|
427
|
+
handleError(r);
|
|
428
|
+
if (s) throw r;
|
|
455
429
|
return true;
|
|
456
430
|
}
|
|
457
|
-
if (f && !
|
|
458
|
-
|
|
459
|
-
|
|
431
|
+
if (f && !t.done) {
|
|
432
|
+
l = t.value;
|
|
433
|
+
u = true;
|
|
460
434
|
return iterate();
|
|
461
435
|
}
|
|
462
|
-
return f &&
|
|
436
|
+
return f && t.done;
|
|
463
437
|
};
|
|
464
|
-
const
|
|
438
|
+
const f = iterate();
|
|
465
439
|
// Later iterate() calls run from asyncWrite, where rethrowing would be unhandled.
|
|
466
|
-
|
|
467
|
-
|
|
440
|
+
s = false;
|
|
441
|
+
return u || f;
|
|
442
|
+
};
|
|
443
|
+
// Landed-synchronously verdict for a LIVE iterator drain; null when no live
|
|
444
|
+
// drain ran (plain promise flight, or a deferred flatten). Drives the
|
|
445
|
+
// shared NotReady/loading tail below.
|
|
446
|
+
let u = null;
|
|
447
|
+
// Flatten one async level: a thenable that RESOLVES to an AsyncIterable —
|
|
448
|
+
// the shape every async stub returning a stream produces — consumes as the
|
|
449
|
+
// stream itself rather than settling on the iterable object. One level
|
|
450
|
+
// only: A+ `then` already collapses nested thenables, so the resolved
|
|
451
|
+
// value is never itself a thenable.
|
|
452
|
+
const flattenIfIterable = (e, n) => {
|
|
453
|
+
let t = false;
|
|
454
|
+
if (typeof e === "object" && e !== null) {
|
|
455
|
+
untrack(() => {
|
|
456
|
+
t = e[Symbol.asyncIterator];
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
if (!t) return false;
|
|
460
|
+
const r = consumeIterator(e, n);
|
|
461
|
+
if (!n) u = r;
|
|
462
|
+
return true;
|
|
463
|
+
};
|
|
464
|
+
if (o) {
|
|
465
|
+
let t = false, r = false, o, u = true;
|
|
466
|
+
// Close registration for the flattening path. Consumption starts in a
|
|
467
|
+
// microtask where the ambient owner is gone (or worse, someone else's),
|
|
468
|
+
// so `cleanup()` can't be used — the close targets el's disposal list
|
|
469
|
+
// directly, exactly where a live cleanup() during this recompute would
|
|
470
|
+
// have put it. Deliberately NOT pre-registered at flight start: a
|
|
471
|
+
// non-null `_disposal` reclassifies the node into recompute's deferred
|
|
472
|
+
// (zombie) disposal path, and plain promise flights — the overwhelming
|
|
473
|
+
// majority — must not pay that. Only a flight that actually flattens
|
|
474
|
+
// becomes disposal-bearing, which is exactly the class a directly
|
|
475
|
+
// returned iterable already occupies.
|
|
476
|
+
const registerDeferredClose = n => {
|
|
477
|
+
if (!e.he) e.he = n; else if (Array.isArray(e.he)) e.he.push(n); else e.he = [ e.he, n ];
|
|
478
|
+
};
|
|
479
|
+
n.then(r => {
|
|
480
|
+
if (u) {
|
|
481
|
+
l = r;
|
|
482
|
+
t = true;
|
|
483
|
+
} else if (e.Te === n && !(e.se & REACTIVE_DISPOSED) && flattenIfIterable(r, registerDeferredClose)) ; else {
|
|
484
|
+
asyncWrite(r);
|
|
485
|
+
settleAutodispose();
|
|
486
|
+
}
|
|
487
|
+
}, e => {
|
|
488
|
+
if (u) {
|
|
489
|
+
o = e;
|
|
490
|
+
r = true;
|
|
491
|
+
} else {
|
|
492
|
+
handleError(e);
|
|
493
|
+
settleAutodispose();
|
|
494
|
+
}
|
|
495
|
+
});
|
|
496
|
+
u = false;
|
|
497
|
+
if (r) {
|
|
498
|
+
// Settle through the same status path an async rejection uses, then
|
|
499
|
+
// unwind the in-progress synchronous read so the errored node isn't
|
|
500
|
+
// momentarily read as `undefined`.
|
|
501
|
+
handleError(o);
|
|
502
|
+
throw o;
|
|
503
|
+
} else if (!t) {
|
|
504
|
+
// Loading window: serve commit #0 instead of suspending. No transition
|
|
505
|
+
// is opened — first-flight work on a loadingValue node is loading-class
|
|
506
|
+
// (invisible to boundaries and transitions); the flight itself is
|
|
507
|
+
// already registered in _inFlight and lands through asyncWrite.
|
|
508
|
+
if (e.Ee) return e.be;
|
|
509
|
+
globalQueue.initTransition(resolveTransition(e));
|
|
510
|
+
throw new NotReadyError(context);
|
|
511
|
+
} else if (!flattenIfIterable(l)) {
|
|
512
|
+
// Synchronously-resolved promise: the first real answer landed.
|
|
513
|
+
e.Ee = false;
|
|
514
|
+
}
|
|
515
|
+
// A sync-resolved promise holding an AsyncIterable flattened LIVE (we
|
|
516
|
+
// are still inside the synchronous read): full initial-drain semantics
|
|
517
|
+
// apply and the shared tail below settles the verdict.
|
|
518
|
+
}
|
|
519
|
+
if (r) flattenIfIterable(n);
|
|
520
|
+
if (u !== null) {
|
|
521
|
+
if (!u) {
|
|
468
522
|
// Loading window: serve commit #0 (see the promise branch above).
|
|
469
|
-
if (e.Ee) return e.
|
|
523
|
+
if (e.Ee) return e.be;
|
|
470
524
|
globalQueue.initTransition(resolveTransition(e));
|
|
471
525
|
throw new NotReadyError(context);
|
|
472
526
|
}
|
|
@@ -474,52 +528,52 @@ function handleAsync(e, n, t) {
|
|
|
474
528
|
// answer; async yields clear inside asyncWrite.
|
|
475
529
|
e.Ee = false;
|
|
476
530
|
}
|
|
477
|
-
return
|
|
531
|
+
return l;
|
|
478
532
|
}
|
|
479
533
|
|
|
480
534
|
function clearStatus(e, n = false) {
|
|
481
535
|
if (e.oe) clearPendingSources(e);
|
|
482
|
-
if (e.
|
|
536
|
+
if (e.le) e.le = false;
|
|
483
537
|
// The pending window is over; its quiet classification dies with it.
|
|
484
538
|
// (Unconditional: _reask is baked into the node literals, so this is a
|
|
485
539
|
// plain store to an existing slot — no shape change.)
|
|
486
|
-
e.
|
|
540
|
+
e.pe = false;
|
|
487
541
|
e.S = n ? 0 : e.S & STATUS_UNINITIALIZED;
|
|
488
542
|
if (e._) setPendingError(e);
|
|
489
543
|
// Update pending signal for isPending() reactivity (companions only exist
|
|
490
544
|
// once the verdict layer created them, which installs the hooks).
|
|
491
|
-
if (e.
|
|
492
|
-
if (e.u && GlobalQueue.
|
|
545
|
+
if (e.Oe || e.ge) GlobalQueue.Se(e);
|
|
546
|
+
if (e.u && GlobalQueue.Ge !== null) GlobalQueue.Ge(e);
|
|
493
547
|
if (e.i) e.i();
|
|
494
548
|
}
|
|
495
549
|
|
|
496
550
|
function notifyStatus(e, n, t, r, o) {
|
|
497
551
|
// Wrap regular errors to track source node
|
|
498
552
|
if (n === STATUS_ERROR && !(t instanceof StatusError) && !(t instanceof NotReadyError)) t = new StatusError(e, t);
|
|
499
|
-
const
|
|
500
|
-
const
|
|
501
|
-
const
|
|
502
|
-
const s =
|
|
553
|
+
const l = n === STATUS_PENDING && t instanceof NotReadyError ? t.source : undefined;
|
|
554
|
+
const u = l === e;
|
|
555
|
+
const i = n === STATUS_PENDING && e.De !== undefined && !u;
|
|
556
|
+
const s = i && hasActiveOverride(e);
|
|
503
557
|
if (!r) {
|
|
504
|
-
if (n === STATUS_PENDING &&
|
|
505
|
-
addPendingSource(e,
|
|
558
|
+
if (n === STATUS_PENDING && l) {
|
|
559
|
+
addPendingSource(e, l);
|
|
506
560
|
e.S = STATUS_PENDING | e.S & STATUS_UNINITIALIZED;
|
|
507
561
|
// Preserve the current source on this propagation so render-effect notification
|
|
508
562
|
// can register every distinct pending source with the transition.
|
|
509
|
-
setPendingError(e,
|
|
563
|
+
setPendingError(e, l, t);
|
|
510
564
|
} else {
|
|
511
565
|
clearPendingSources(e);
|
|
512
566
|
e.S = n | (n !== STATUS_ERROR ? e.S & STATUS_UNINITIALIZED : 0);
|
|
513
567
|
e._ = t;
|
|
514
568
|
}
|
|
515
569
|
GlobalQueue.Se !== null && GlobalQueue.Se(e);
|
|
516
|
-
if (e.u && GlobalQueue.
|
|
570
|
+
if (e.u && GlobalQueue.Ge !== null) GlobalQueue.Ge(e);
|
|
517
571
|
}
|
|
518
572
|
if (o && !r) {
|
|
519
573
|
assignOrMergeLane(e, o);
|
|
520
574
|
}
|
|
521
575
|
const f = r || s;
|
|
522
|
-
const a = r ||
|
|
576
|
+
const a = r || i ? undefined : o;
|
|
523
577
|
if (e.i) {
|
|
524
578
|
if (r && n === STATUS_PENDING) {
|
|
525
579
|
return;
|
|
@@ -533,13 +587,13 @@ function notifyStatus(e, n, t, r, o) {
|
|
|
533
587
|
}
|
|
534
588
|
forEachDependent(e, (e, r) => {
|
|
535
589
|
e.de = clock;
|
|
536
|
-
if (n === STATUS_PENDING &&
|
|
590
|
+
if (n === STATUS_PENDING && l && !e.oe?.has(l) || n !== STATUS_PENDING && (e._ !== t || e.oe)) {
|
|
537
591
|
// A pending-observer link is the subscription an `isPending` read created.
|
|
538
592
|
// It exists so the observer re-runs when the source settles, but it must
|
|
539
593
|
// not carry a real (non-NotReadyError) error — the synchronous `isPending`
|
|
540
594
|
// read swallows those, and the async path must match. Re-run the observer
|
|
541
595
|
// so `isPending` re-evaluates (to not-pending) instead of forwarding.
|
|
542
|
-
if (r.
|
|
596
|
+
if (r.ye && n !== STATUS_PENDING && !(t instanceof NotReadyError)) {
|
|
543
597
|
enqueueSub(e);
|
|
544
598
|
schedule();
|
|
545
599
|
return;
|