@solidjs/signals 2.0.0-beta.30 → 2.0.0-beta.32
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.js +47 -9
- package/dist/node.cjs +136 -97
- package/dist/prod/core/async.js +49 -34
- package/dist/prod/core/core.js +22 -22
- package/dist/prod/core/effect.js +3 -3
- package/dist/prod/core/lanes.js +1 -1
- package/dist/prod/core/optimistic.js +23 -11
- package/dist/prod/core/owner.js +1 -1
- package/dist/prod/core/scheduler.js +25 -13
- package/dist/prod/core/verdict.js +21 -21
- package/dist/prod/store/optimistic.js +4 -4
- package/dist/prod/store/store.js +1 -1
- package/package.json +1 -1
package/dist/prod/core/async.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { STATUS_UNINITIALIZED, STATUS_ERROR, STATUS_PENDING,
|
|
1
|
+
import { STATUS_UNINITIALIZED, STATUS_ERROR, STATUS_PENDING, REACTIVE_DIRTY, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE } from "./constants.js";
|
|
2
2
|
|
|
3
3
|
import { untrack, context, setSignal } from "./core.js";
|
|
4
4
|
|
|
@@ -6,7 +6,7 @@ import "./invariants.js";
|
|
|
6
6
|
|
|
7
7
|
import { NotReadyError, StatusError } from "./error.js";
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { trimStaleDeps, unobserved } from "./graph.js";
|
|
10
10
|
|
|
11
11
|
import { enqueueSub } from "./heap.js";
|
|
12
12
|
|
|
@@ -14,7 +14,7 @@ import { resolveTransition, hasActiveOverride, assignOrMergeLane, resolveLane }
|
|
|
14
14
|
|
|
15
15
|
import { cleanup } from "./owner.js";
|
|
16
16
|
|
|
17
|
-
import { globalQueue, GlobalQueue,
|
|
17
|
+
import { globalQueue, GlobalQueue, schedule, flush, queuePendingNode, insertSubs, clock, currentTransition } 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 —
|
|
@@ -133,9 +133,9 @@ function settlePendingSource(e) {
|
|
|
133
133
|
if (r.has(u) || !removePendingSource(u, e)) return;
|
|
134
134
|
r.add(u);
|
|
135
135
|
u.Se = clock;
|
|
136
|
-
const
|
|
137
|
-
if (
|
|
138
|
-
setPendingError(u,
|
|
136
|
+
const s = u.oe?.values().next().value;
|
|
137
|
+
if (s) {
|
|
138
|
+
setPendingError(u, s);
|
|
139
139
|
o !== null && o(u);
|
|
140
140
|
} else {
|
|
141
141
|
u.S &= ~STATUS_PENDING;
|
|
@@ -222,8 +222,8 @@ function handleAsync(e, n, t) {
|
|
|
222
222
|
const u = !!(e.S & STATUS_UNINITIALIZED);
|
|
223
223
|
trimStaleDeps(e);
|
|
224
224
|
clearStatus(e);
|
|
225
|
-
const
|
|
226
|
-
if (
|
|
225
|
+
const s = resolveLane(e);
|
|
226
|
+
if (s) s.Ne.delete(e);
|
|
227
227
|
if (t) {
|
|
228
228
|
t(r);
|
|
229
229
|
if (u) clearStatus(e, true);
|
|
@@ -249,14 +249,14 @@ function handleAsync(e, n, t) {
|
|
|
249
249
|
GlobalQueue._e !== null && GlobalQueue._e(e, r);
|
|
250
250
|
if (!hasActiveOverride(e)) insertSubs(e);
|
|
251
251
|
e.Se = clock;
|
|
252
|
-
} else if (
|
|
252
|
+
} else if (s) {
|
|
253
253
|
// Route through lane's effect queue for independent flushing
|
|
254
254
|
const n = e.Pe;
|
|
255
|
-
const t = e.
|
|
256
|
-
const o = e.
|
|
255
|
+
const t = e.be;
|
|
256
|
+
const o = e.he;
|
|
257
257
|
try {
|
|
258
258
|
if (!n && u || !o || !o(r, t)) {
|
|
259
|
-
e.
|
|
259
|
+
e.be = r;
|
|
260
260
|
e.Se = clock;
|
|
261
261
|
// The latest() shadow write gives latest() effects independent lanes; the
|
|
262
262
|
// _pendingSignal update is a no-op repeat of the clearStatus() call above
|
|
@@ -300,9 +300,9 @@ function handleAsync(e, n, t) {
|
|
|
300
300
|
return false;
|
|
301
301
|
};
|
|
302
302
|
if (o) {
|
|
303
|
-
let t = false, r = false, o,
|
|
303
|
+
let t = false, r = false, o, s = true;
|
|
304
304
|
n.then(e => {
|
|
305
|
-
if (
|
|
305
|
+
if (s) {
|
|
306
306
|
u = e;
|
|
307
307
|
t = true;
|
|
308
308
|
} else {
|
|
@@ -310,7 +310,7 @@ function handleAsync(e, n, t) {
|
|
|
310
310
|
settleAutodispose();
|
|
311
311
|
}
|
|
312
312
|
}, e => {
|
|
313
|
-
if (
|
|
313
|
+
if (s) {
|
|
314
314
|
o = e;
|
|
315
315
|
r = true;
|
|
316
316
|
} else {
|
|
@@ -318,7 +318,7 @@ function handleAsync(e, n, t) {
|
|
|
318
318
|
settleAutodispose();
|
|
319
319
|
}
|
|
320
320
|
});
|
|
321
|
-
|
|
321
|
+
s = false;
|
|
322
322
|
if (r) {
|
|
323
323
|
// Settle through the same status path an async rejection uses, then
|
|
324
324
|
// unwind the in-progress synchronous read so the errored node isn't
|
|
@@ -334,7 +334,7 @@ function handleAsync(e, n, t) {
|
|
|
334
334
|
const t = n[Symbol.asyncIterator]();
|
|
335
335
|
let r = false;
|
|
336
336
|
let o = false;
|
|
337
|
-
let
|
|
337
|
+
let s = true;
|
|
338
338
|
cleanup(() => {
|
|
339
339
|
if (o) return;
|
|
340
340
|
o = true;
|
|
@@ -350,10 +350,25 @@ function handleAsync(e, n, t) {
|
|
|
350
350
|
if (!settleAutodispose()) iterate();
|
|
351
351
|
};
|
|
352
352
|
const iterate = () => {
|
|
353
|
-
let
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
353
|
+
let l, i, f = false, a = false, c = true;
|
|
354
|
+
// Protocol tolerance, matching `for await`: `await` unwraps whatever
|
|
355
|
+
// next() returns — a thenable OR a bare IteratorResult. Real producers
|
|
356
|
+
// use the bare form as a promise-free fast path when a value is already
|
|
357
|
+
// buffered (seroval's deserialized streams do), so a bare result is
|
|
358
|
+
// assimilated as an already-settled step instead of crashing on `.then`.
|
|
359
|
+
const S = t.next();
|
|
360
|
+
const d = isThenable(S) ? S : {
|
|
361
|
+
then: e => void e(S)
|
|
362
|
+
};
|
|
363
|
+
d.then(t => {
|
|
364
|
+
// The sync stash only serves the INITIAL drain (handleAsync's caller
|
|
365
|
+
// consumes syncValue / throws NotReady from it). A sync-settled step
|
|
366
|
+
// after an async gap — seroval buffering values between pulls, a
|
|
367
|
+
// sync-thenable producer mid-stream — has no caller reading the
|
|
368
|
+
// stash: it must write through the async path or the value is
|
|
369
|
+
// silently dropped.
|
|
370
|
+
if (c && s) {
|
|
371
|
+
l = t;
|
|
357
372
|
f = true;
|
|
358
373
|
if (t.done) o = true;
|
|
359
374
|
} else if (e.Te !== n) {
|
|
@@ -387,20 +402,20 @@ function handleAsync(e, n, t) {
|
|
|
387
402
|
// Match the promise branch, but only rethrow during the initial read.
|
|
388
403
|
o = true;
|
|
389
404
|
handleError(i);
|
|
390
|
-
if (
|
|
405
|
+
if (s) throw i;
|
|
391
406
|
return true;
|
|
392
407
|
}
|
|
393
|
-
if (f && !
|
|
394
|
-
u =
|
|
408
|
+
if (f && !l.done) {
|
|
409
|
+
u = l.value;
|
|
395
410
|
r = true;
|
|
396
411
|
return iterate();
|
|
397
412
|
}
|
|
398
|
-
return f &&
|
|
413
|
+
return f && l.done;
|
|
399
414
|
};
|
|
400
|
-
const
|
|
415
|
+
const l = iterate();
|
|
401
416
|
// Later iterate() calls run from asyncWrite, where rethrowing would be unhandled.
|
|
402
|
-
|
|
403
|
-
if (!r && !
|
|
417
|
+
s = false;
|
|
418
|
+
if (!r && !l) {
|
|
404
419
|
globalQueue.initTransition(resolveTransition(e));
|
|
405
420
|
throw new NotReadyError(context);
|
|
406
421
|
}
|
|
@@ -414,12 +429,12 @@ function clearStatus(e, n = false) {
|
|
|
414
429
|
// The pending window is over; its quiet classification dies with it.
|
|
415
430
|
// (Unconditional: _reask is baked into the node literals, so this is a
|
|
416
431
|
// plain store to an existing slot — no shape change.)
|
|
417
|
-
e.
|
|
432
|
+
e.Ue = false;
|
|
418
433
|
e.S = n ? 0 : e.S & STATUS_UNINITIALIZED;
|
|
419
434
|
if (e._) setPendingError(e);
|
|
420
435
|
// Update pending signal for isPending() reactivity (companions only exist
|
|
421
436
|
// once the verdict layer created them, which installs the hooks).
|
|
422
|
-
if (e.
|
|
437
|
+
if (e.Re || e.pe) GlobalQueue.ce(e);
|
|
423
438
|
if (e.u && GlobalQueue.Ge !== null) GlobalQueue.Ge(e);
|
|
424
439
|
if (e.i) e.i();
|
|
425
440
|
}
|
|
@@ -428,9 +443,9 @@ function notifyStatus(e, n, t, r, o) {
|
|
|
428
443
|
// Wrap regular errors to track source node
|
|
429
444
|
if (n === STATUS_ERROR && !(t instanceof StatusError) && !(t instanceof NotReadyError)) t = new StatusError(e, t);
|
|
430
445
|
const u = n === STATUS_PENDING && t instanceof NotReadyError ? t.source : undefined;
|
|
431
|
-
const
|
|
432
|
-
const
|
|
433
|
-
const i =
|
|
446
|
+
const s = u === e;
|
|
447
|
+
const l = n === STATUS_PENDING && e.Ae !== undefined && !s;
|
|
448
|
+
const i = l && hasActiveOverride(e);
|
|
434
449
|
if (!r) {
|
|
435
450
|
if (n === STATUS_PENDING && u) {
|
|
436
451
|
addPendingSource(e, u);
|
|
@@ -450,7 +465,7 @@ function notifyStatus(e, n, t, r, o) {
|
|
|
450
465
|
assignOrMergeLane(e, o);
|
|
451
466
|
}
|
|
452
467
|
const f = r || i;
|
|
453
|
-
const a = r ||
|
|
468
|
+
const a = r || l ? undefined : o;
|
|
454
469
|
if (e.i) {
|
|
455
470
|
if (r && n === STATUS_PENDING) {
|
|
456
471
|
return;
|
package/dist/prod/core/core.js
CHANGED
|
@@ -137,7 +137,7 @@ function recompute(e, t = false) {
|
|
|
137
137
|
e.Ye++;
|
|
138
138
|
e.se = REACTIVE_RECOMPUTING_DEPS;
|
|
139
139
|
e.Se = clock;
|
|
140
|
-
let r = e.De === NOT_PENDING ? e.
|
|
140
|
+
let r = e.De === NOT_PENDING ? e.be : e.De;
|
|
141
141
|
let c = e.Ve;
|
|
142
142
|
let _ = tracking;
|
|
143
143
|
let f = currentOptimisticLane;
|
|
@@ -189,7 +189,7 @@ function recompute(e, t = false) {
|
|
|
189
189
|
// On a status-free node clearStatus is a guaranteed no-op: every branch
|
|
190
190
|
// in its body is gated on one of these fields, and with _statusFlags === 0
|
|
191
191
|
// the flags write (create or not) stores the 0 already there.
|
|
192
|
-
if (e.S !== 0 || e.i !== undefined || e._ || e.
|
|
192
|
+
if (e.S !== 0 || e.i !== undefined || e._ || e.Ue || e.de || e.oe !== undefined || e.Re !== undefined || e.pe !== undefined || e.u !== null) clearStatus(e, t);
|
|
193
193
|
// _optimisticLane is only ever assigned by engine paths.
|
|
194
194
|
if (e.Me) GlobalQueue.Ke(e);
|
|
195
195
|
} catch (t) {
|
|
@@ -212,10 +212,10 @@ function recompute(e, t = false) {
|
|
|
212
212
|
}
|
|
213
213
|
if (!e._) {
|
|
214
214
|
trimStaleDeps(e);
|
|
215
|
-
const s = u ? unwrapOverride(e.Ae) : e.De === NOT_PENDING ? e.
|
|
215
|
+
const s = u ? unwrapOverride(e.Ae) : e.De === NOT_PENDING ? e.be : e.De;
|
|
216
216
|
let a = false;
|
|
217
217
|
try {
|
|
218
|
-
a = !n && l || !e.
|
|
218
|
+
a = !n && l || !e.he || !e.he(s, r);
|
|
219
219
|
} catch (t) {
|
|
220
220
|
// A throwing user comparator is an error of this node's computation.
|
|
221
221
|
// Route it through the same status path as a compute-phase throw so
|
|
@@ -242,7 +242,7 @@ function recompute(e, t = false) {
|
|
|
242
242
|
// commitPendingNodes) exists to sequence transition reveals, and
|
|
243
243
|
// paying it per effect on the plain path is pure overhead.
|
|
244
244
|
n && (activeTransition !== e.Ie || activeTransition === null) || i) {
|
|
245
|
-
e.
|
|
245
|
+
e.be = r;
|
|
246
246
|
// Lane-propagated correction: upstream data is fresh, correct the
|
|
247
247
|
// override unconditionally. The direct _value commit is the lane's
|
|
248
248
|
// own reveal schedule; drop any superseded older hold so its queued
|
|
@@ -316,14 +316,14 @@ function computed(e, t) {
|
|
|
316
316
|
const i = {
|
|
317
317
|
id: inheritId(t, n, context),
|
|
318
318
|
T: (n ? CONFIG_TRANSPARENT : 0) | (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (!context || t?.lazy ? CONFIG_AUTO_DISPOSE : 0) | (t?.sync ? CONFIG_SYNC : 0) | (t?.V ? CONFIG_NO_SNAPSHOT : 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
|
|
319
|
-
|
|
319
|
+
he: t?.equals != null ? t.equals : isEqual,
|
|
320
320
|
ut: t?.unobserved,
|
|
321
321
|
Le: null,
|
|
322
322
|
C: context?.C ?? globalQueue,
|
|
323
323
|
we: context?.we ?? defaultContext,
|
|
324
324
|
qe: 0,
|
|
325
325
|
ae: e,
|
|
326
|
-
|
|
326
|
+
be: undefined,
|
|
327
327
|
Ve: 0,
|
|
328
328
|
u: null,
|
|
329
329
|
lt: undefined,
|
|
@@ -345,7 +345,7 @@ function computed(e, t) {
|
|
|
345
345
|
Qe: null,
|
|
346
346
|
Te: null,
|
|
347
347
|
Ie: null,
|
|
348
|
-
|
|
348
|
+
Ue: false
|
|
349
349
|
};
|
|
350
350
|
setupComputedNode(i, t);
|
|
351
351
|
return i;
|
|
@@ -361,14 +361,14 @@ function computed(e, t) {
|
|
|
361
361
|
const s = {
|
|
362
362
|
id: inheritId(l, o, context),
|
|
363
363
|
T: (o ? CONFIG_TRANSPARENT : 0) | (l?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (l?.sync ? CONFIG_SYNC : 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
|
|
364
|
-
|
|
364
|
+
he: false,
|
|
365
365
|
ut: l?.unobserved,
|
|
366
366
|
Le: null,
|
|
367
367
|
C: context?.C ?? globalQueue,
|
|
368
368
|
we: context?.we ?? defaultContext,
|
|
369
369
|
qe: 0,
|
|
370
370
|
ae: e,
|
|
371
|
-
|
|
371
|
+
be: undefined,
|
|
372
372
|
Ve: 0,
|
|
373
373
|
u: null,
|
|
374
374
|
lt: undefined,
|
|
@@ -390,7 +390,7 @@ function computed(e, t) {
|
|
|
390
390
|
Qe: null,
|
|
391
391
|
Te: null,
|
|
392
392
|
Ie: null,
|
|
393
|
-
|
|
393
|
+
Ue: false,
|
|
394
394
|
Be: false,
|
|
395
395
|
ct: undefined,
|
|
396
396
|
_t: t,
|
|
@@ -425,7 +425,7 @@ function setupComputedNode(e, t) {
|
|
|
425
425
|
!t?.lazy && recompute(e, true);
|
|
426
426
|
if (snapshotCaptureActive && !t?.lazy) {
|
|
427
427
|
if (!(e.S & STATUS_PENDING) && !(e.T & CONFIG_NO_SNAPSHOT)) {
|
|
428
|
-
e.xe = e.
|
|
428
|
+
e.xe = e.be === undefined ? NO_SNAPSHOT : e.be;
|
|
429
429
|
snapshotSources.add(e);
|
|
430
430
|
}
|
|
431
431
|
}
|
|
@@ -433,10 +433,10 @@ function setupComputedNode(e, t) {
|
|
|
433
433
|
|
|
434
434
|
function signal(e, t, n = null) {
|
|
435
435
|
const i = {
|
|
436
|
-
|
|
436
|
+
he: t?.equals != null ? t.equals : isEqual,
|
|
437
437
|
T: (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (t?.V ? CONFIG_NO_SNAPSHOT : 0),
|
|
438
438
|
ut: t?.unobserved,
|
|
439
|
-
|
|
439
|
+
be: e,
|
|
440
440
|
o: null,
|
|
441
441
|
st: null,
|
|
442
442
|
Se: clock,
|
|
@@ -534,7 +534,7 @@ function isEqual(e, t) {
|
|
|
534
534
|
let t = context;
|
|
535
535
|
if (t?.Nt) t = t.Tt;
|
|
536
536
|
if (t && tracking) link(e, t);
|
|
537
|
-
return !t || e.De === NOT_PENDING ? e.
|
|
537
|
+
return !t || e.De === NOT_PENDING ? e.be : e.De;
|
|
538
538
|
}
|
|
539
539
|
|
|
540
540
|
function read(e) {
|
|
@@ -558,7 +558,7 @@ function read(e) {
|
|
|
558
558
|
}
|
|
559
559
|
if (!n.ae && u === e && e.Ae === undefined && e.xe === undefined && activeTransition === null && currentOptimisticLane === null && !snapshotCaptureActive && true) {
|
|
560
560
|
if (t && tracking) link(e, t);
|
|
561
|
-
return !t || e.De === NOT_PENDING ? e.
|
|
561
|
+
return !t || e.De === NOT_PENDING ? e.be : e.De;
|
|
562
562
|
}
|
|
563
563
|
if (t && tracking) {
|
|
564
564
|
link(e, t, pendingCheckActive);
|
|
@@ -610,7 +610,7 @@ function read(e) {
|
|
|
610
610
|
const n = e.xe;
|
|
611
611
|
if (n !== undefined) {
|
|
612
612
|
const i = n === NO_SNAPSHOT ? undefined : n;
|
|
613
|
-
const u = e.De !== NOT_PENDING ? e.De : e.
|
|
613
|
+
const u = e.De !== NOT_PENDING ? e.De : e.be;
|
|
614
614
|
if (u !== i) t.se |= REACTIVE_SNAPSHOT_STALE;
|
|
615
615
|
return i;
|
|
616
616
|
}
|
|
@@ -627,13 +627,13 @@ function read(e) {
|
|
|
627
627
|
// so it re-runs with the new committed view. (Gate details live with the
|
|
628
628
|
// engine — a non-null lane implies it is installed.)
|
|
629
629
|
if (currentOptimisticLane !== null && activeTransition !== null && t !== null && GlobalQueue.Ot(e, u, t)) {
|
|
630
|
-
return e.
|
|
630
|
+
return e.be;
|
|
631
631
|
}
|
|
632
632
|
// In optimistic lane context, return _value for optimistic/lane-assigned signals
|
|
633
633
|
// and for regular signals in stale mode (render effects). Non-stale readers (user
|
|
634
634
|
// effects) see _pendingValue so that latest() and direct reads stay consistent.
|
|
635
635
|
// (The lane-context clause lives with the engine.)
|
|
636
|
-
const l = !t || currentOptimisticLane !== null && GlobalQueue.Rt(e, u, t) || e.De === NOT_PENDING || stale && e.Ie && activeTransition !== e.Ie ? e.
|
|
636
|
+
const l = !t || currentOptimisticLane !== null && GlobalQueue.Rt(e, u, t) || e.De === NOT_PENDING || stale && e.Ie && activeTransition !== e.Ie ? e.be : e.De;
|
|
637
637
|
// Record that this isPending() probe observed the fresh pending value, so
|
|
638
638
|
// the probe doesn't pair "pending" with the new value (#2831).
|
|
639
639
|
if (pendingCheckActive) GlobalQueue.Pt(e, l);
|
|
@@ -650,18 +650,18 @@ function setSignal(e, t) {
|
|
|
650
650
|
// _overrideValue slot, and every module that creates one installs the
|
|
651
651
|
// engine first.
|
|
652
652
|
if (e.Ae !== undefined && !projectionWriteActive) return GlobalQueue.ht(e, t);
|
|
653
|
-
const n = e.De === NOT_PENDING ? e.
|
|
653
|
+
const n = e.De === NOT_PENDING ? e.be : e.De;
|
|
654
654
|
if (typeof t === "function") t = t(n);
|
|
655
655
|
// Uninitialized check first: the first commit has no previous value, so the
|
|
656
656
|
// user comparator must not run against `undefined` (matches recompute).
|
|
657
|
-
const i = !!(e.S & STATUS_UNINITIALIZED) || !e.
|
|
657
|
+
const i = !!(e.S & STATUS_UNINITIALIZED) || !e.he || !e.he(n, t);
|
|
658
658
|
if (!i) return t;
|
|
659
659
|
if (e.De === NOT_PENDING) queuePendingNode(e);
|
|
660
660
|
e.De = t;
|
|
661
661
|
// syncCompanions only pokes _pendingSignal/_latestValueComputed — with
|
|
662
662
|
// neither companion present the call is a guaranteed no-op (companions are
|
|
663
663
|
// only ever created, never removed, and creating one installs the hook).
|
|
664
|
-
(e.
|
|
664
|
+
(e.Re !== undefined || e.pe !== undefined) && GlobalQueue._e !== null && GlobalQueue._e(e, t);
|
|
665
665
|
e.Se = clock;
|
|
666
666
|
insertSubs(e);
|
|
667
667
|
schedule();
|
package/dist/prod/core/effect.js
CHANGED
|
@@ -62,7 +62,7 @@ function runEffect(t) {
|
|
|
62
62
|
// same flush must not be hijacked by a later-arriving error status.
|
|
63
63
|
if (t.S & STATUS_ERROR && t.Pe === EFFECT_USER) {
|
|
64
64
|
const E = unwrapStatusError(t._);
|
|
65
|
-
t.ct = t.
|
|
65
|
+
t.ct = t.be;
|
|
66
66
|
t.Be = false;
|
|
67
67
|
try {
|
|
68
68
|
t.ft ? t.ft(E, () => {
|
|
@@ -82,7 +82,7 @@ function runEffect(t) {
|
|
|
82
82
|
t.Et = undefined;
|
|
83
83
|
try {
|
|
84
84
|
E?.();
|
|
85
|
-
const e = t._t(t.
|
|
85
|
+
const e = t._t(t.be, t.ct);
|
|
86
86
|
if (false && e !== undefined && typeof e !== "function") ;
|
|
87
87
|
// The final cleanup is invoked by disposeChildren at true disposal.
|
|
88
88
|
t.Et = e;
|
|
@@ -94,7 +94,7 @@ function runEffect(t) {
|
|
|
94
94
|
throw E;
|
|
95
95
|
}
|
|
96
96
|
} finally {
|
|
97
|
-
t.ct = t.
|
|
97
|
+
t.ct = t.be;
|
|
98
98
|
t.Be = false;
|
|
99
99
|
}
|
|
100
100
|
}
|
package/dist/prod/core/lanes.js
CHANGED
|
@@ -36,7 +36,7 @@ const activeLanes = new Set;
|
|
|
36
36
|
// parent-child is a property of the nodes, not of write order — otherwise
|
|
37
37
|
// the owner's write merges the companion's subscribers into this lane and
|
|
38
38
|
// their effects wait on its async instead of flushing immediately.
|
|
39
|
-
adoptCompanionLane(n.
|
|
39
|
+
adoptCompanionLane(n.Re, e);
|
|
40
40
|
adoptCompanionLane(n.pe, e);
|
|
41
41
|
return e;
|
|
42
42
|
}
|
|
@@ -25,9 +25,9 @@ import { GlobalQueue, activeTransition, globalQueue, clock, insertSubs, schedule
|
|
|
25
25
|
*/
|
|
26
26
|
/** The optimistic half of setSignal, fired when `_overrideValue !== undefined`. */ function optimisticWrite(e, n) {
|
|
27
27
|
const t = e.Ae !== NOT_PENDING;
|
|
28
|
-
const i = t ? unwrapOverride(e.Ae) : e.
|
|
28
|
+
const i = t ? unwrapOverride(e.Ae) : e.be;
|
|
29
29
|
if (typeof n === "function") n = n(i);
|
|
30
|
-
const u = !!(e.S & STATUS_UNINITIALIZED) || !e.
|
|
30
|
+
const u = !!(e.S & STATUS_UNINITIALIZED) || !e.he || !e.he(i, n);
|
|
31
31
|
if (!u) {
|
|
32
32
|
// Same-value write with an active override still entangles the current
|
|
33
33
|
// action's transition — the hold must outlast all overlapping actions.
|
|
@@ -46,15 +46,15 @@ import { GlobalQueue, activeTransition, globalQueue, clock, insertSubs, schedule
|
|
|
46
46
|
// joint root). resolveTransition prefers this over the lane's _transition,
|
|
47
47
|
// which a shared subscriber can merge across transactions (#2912).
|
|
48
48
|
e.sn = activeTransition;
|
|
49
|
-
const
|
|
50
|
-
e.Me =
|
|
49
|
+
const r = getOrCreateLane(e);
|
|
50
|
+
e.Me = r;
|
|
51
51
|
// Literal undefined must not land raw: the slot doubles as the optimistic
|
|
52
52
|
// brand, and erasing it makes the write invisible and routes follow-up
|
|
53
53
|
// writes off the optimistic path into permanent commits (#2898).
|
|
54
54
|
e.Ae = n === undefined ? OVERRIDE_UNDEFINED : n;
|
|
55
55
|
// syncCompanions only pokes _pendingSignal/_latestValueComputed — with
|
|
56
56
|
// neither companion present the call is a guaranteed no-op.
|
|
57
|
-
(e.
|
|
57
|
+
(e.Re !== undefined || e.pe !== undefined) && GlobalQueue._e !== null && GlobalQueue._e(e, n);
|
|
58
58
|
e.Se = clock;
|
|
59
59
|
insertSubs(e, true);
|
|
60
60
|
schedule();
|
|
@@ -89,7 +89,7 @@ function resolveOptimisticNodes(e) {
|
|
|
89
89
|
if (!(n.S & STATUS_PENDING)) n.S &= ~STATUS_UNINITIALIZED;
|
|
90
90
|
const i = n.Ae;
|
|
91
91
|
n.Ae = NOT_PENDING;
|
|
92
|
-
if (i !== NOT_PENDING && n.
|
|
92
|
+
if (i !== NOT_PENDING && n.be !== unwrapOverride(i)) insertSubs(n, true);
|
|
93
93
|
n.Ie = null;
|
|
94
94
|
n.sn = null;
|
|
95
95
|
}
|
|
@@ -98,9 +98,9 @@ function resolveOptimisticNodes(e) {
|
|
|
98
98
|
// transition that produced them (A19 — pending is a property of the data).
|
|
99
99
|
for (let t = 0; t < n; t++) {
|
|
100
100
|
const n = e[t];
|
|
101
|
-
if (n.
|
|
101
|
+
if (n.Re || n.pe) GlobalQueue.un(n);
|
|
102
102
|
const i = n.nn;
|
|
103
|
-
if (i && (i.
|
|
103
|
+
if (i && (i.Re === n || i.pe === n)) GlobalQueue.un(i);
|
|
104
104
|
}
|
|
105
105
|
e.splice(0, n);
|
|
106
106
|
}
|
|
@@ -164,7 +164,19 @@ function cleanupCompletedLanes(e) {
|
|
|
164
164
|
* read()'s value selection under a lane: return the committed `_value` for
|
|
165
165
|
* optimistic/lane-assigned signals, stale-mode reads, and pending owners.
|
|
166
166
|
*/ function laneReadsCommitted(e, n, t) {
|
|
167
|
-
|
|
167
|
+
if (e.Ae !== undefined || !!e.Me || !!(n.S & STATUS_PENDING)) return true;
|
|
168
|
+
if (n === e && stale && t.nn !== e) {
|
|
169
|
+
// The committed view can hide a same-tick ambient write (a lane member —
|
|
170
|
+
// even just an isPending companion flip — puts the reader "under a lane"
|
|
171
|
+
// against unrelated plain writes). With no transaction the write commits
|
|
172
|
+
// at THIS flush's end with no re-delivery, so record the reader for
|
|
173
|
+
// replay at commit — the same contract gatedRead provides when a
|
|
174
|
+
// transaction is active (#2963). If a transaction forms mid-flush,
|
|
175
|
+
// initTransition carries the recording over to it.
|
|
176
|
+
if (e.De !== NOT_PENDING && activeTransition === null) globalQueue.m.ln.add(t);
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
return false;
|
|
168
180
|
}
|
|
169
181
|
|
|
170
182
|
/**
|
|
@@ -218,8 +230,8 @@ function trackOptimisticStore(e) {
|
|
|
218
230
|
if (GlobalQueue.ht !== null) return;
|
|
219
231
|
GlobalQueue.ht = optimisticWrite;
|
|
220
232
|
GlobalQueue.En = resolveOptimisticNodes;
|
|
221
|
-
GlobalQueue.
|
|
222
|
-
GlobalQueue.
|
|
233
|
+
GlobalQueue.Tn = transitionBlocked;
|
|
234
|
+
GlobalQueue.dn = cleanupCompletedLanes;
|
|
223
235
|
GlobalQueue.In = runLaneEffects;
|
|
224
236
|
GlobalQueue.Ot = gatedRead;
|
|
225
237
|
GlobalQueue.Ct = laneSuspends;
|
package/dist/prod/core/owner.js
CHANGED
|
@@ -51,7 +51,7 @@ function disposeChildren(e, n = false, t) {
|
|
|
51
51
|
// edge). Snap runs after the DISPOSED flag is set so the oracle reads
|
|
52
52
|
// false, and notifies subscribers still watching the companion.
|
|
53
53
|
const n = e;
|
|
54
|
-
if (n.
|
|
54
|
+
if (n.Re || n.pe) GlobalQueue.un(n);
|
|
55
55
|
}
|
|
56
56
|
if (n && e.ae) e.Te = null;
|
|
57
57
|
let l = t ? e.Qe : e.ke;
|
|
@@ -330,8 +330,8 @@ class GlobalQueue extends Queue {
|
|
|
330
330
|
// once the gate holds.
|
|
331
331
|
static ht=null;
|
|
332
332
|
static En=null;
|
|
333
|
-
static dn=null;
|
|
334
333
|
static Tn=null;
|
|
334
|
+
static dn=null;
|
|
335
335
|
static In=null;
|
|
336
336
|
static Ot=null;
|
|
337
337
|
static Ct=null;
|
|
@@ -486,6 +486,12 @@ class GlobalQueue extends Queue {
|
|
|
486
486
|
}
|
|
487
487
|
if (t.A.length) activeTransition.A.push(...t.A);
|
|
488
488
|
for (const e of t.cn) activeTransition.cn.add(e);
|
|
489
|
+
// Gated readers recorded against the ambient batch move with it: their
|
|
490
|
+
// replay-at-commit now happens at the transaction's completion.
|
|
491
|
+
if (t.ln.size) {
|
|
492
|
+
for (const e of t.ln) activeTransition.ln.add(e);
|
|
493
|
+
t.ln.clear();
|
|
494
|
+
}
|
|
489
495
|
currentBatch = this.m = activeTransition;
|
|
490
496
|
}
|
|
491
497
|
for (const e of activeLanes) {
|
|
@@ -537,14 +543,14 @@ function commitPendingNode(e) {
|
|
|
537
543
|
const t = e;
|
|
538
544
|
if (!t.ae) {
|
|
539
545
|
if (e.De !== NOT_PENDING) {
|
|
540
|
-
e.
|
|
546
|
+
e.be = e.De;
|
|
541
547
|
e.De = NOT_PENDING;
|
|
542
548
|
}
|
|
543
|
-
if (e.
|
|
549
|
+
if (e.Re || e.pe) GlobalQueue.un(e);
|
|
544
550
|
return;
|
|
545
551
|
}
|
|
546
552
|
if (e.De !== NOT_PENDING) {
|
|
547
|
-
e.
|
|
553
|
+
e.be = e.De;
|
|
548
554
|
e.De = NOT_PENDING;
|
|
549
555
|
// Set _modified for effects, but not for tracked effects (they handle their own scheduling)
|
|
550
556
|
if (e.Pe && e.Pe !== EFFECT_TRACKED) e.Be = true;
|
|
@@ -552,7 +558,7 @@ function commitPendingNode(e) {
|
|
|
552
558
|
t.se &= ~REACTIVE_MANUAL_WRITE;
|
|
553
559
|
if (!(t.S & STATUS_PENDING)) t.S &= ~STATUS_UNINITIALIZED;
|
|
554
560
|
if (t.Qe !== null || t.ye !== null) GlobalQueue.me(t, false, true);
|
|
555
|
-
if (e.
|
|
561
|
+
if (e.Re || e.pe) GlobalQueue.un(e);
|
|
556
562
|
}
|
|
557
563
|
|
|
558
564
|
function commitPendingNodes() {
|
|
@@ -579,13 +585,19 @@ function finalizePureQueue(e = null, t = false) {
|
|
|
579
585
|
// which installed the engine's hooks.
|
|
580
586
|
if (t.je.length) GlobalQueue.En(t.je);
|
|
581
587
|
// Replay entanglement: subs recorded by the read-time gate get rescheduled
|
|
582
|
-
// so they re-run with the now-committed values visible.
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
if (t.
|
|
586
|
-
|
|
588
|
+
// so they re-run with the now-committed values visible. The ambient batch
|
|
589
|
+
// replays too — laneReadsCommitted records readers whose committed-view
|
|
590
|
+
// read hid a same-tick plain write that just committed above (#2963).
|
|
591
|
+
if (t.ln.size) {
|
|
592
|
+
for (const e of t.ln) {
|
|
593
|
+
if (e.se & REACTIVE_DISPOSED) continue;
|
|
594
|
+
enqueueSub(e);
|
|
587
595
|
}
|
|
588
|
-
|
|
596
|
+
t.ln.clear();
|
|
597
|
+
// A completing transition keeps the outer flush loop alive by itself;
|
|
598
|
+
// the ambient batch needs the re-arm or the replay sits in the heap
|
|
599
|
+
// until the next unrelated write.
|
|
600
|
+
schedule();
|
|
589
601
|
}
|
|
590
602
|
// Declared motion ends with the transaction: settle (or plain flush end
|
|
591
603
|
// for ambient marks) releases each registration's refcount. A non-empty
|
|
@@ -604,7 +616,7 @@ function finalizePureQueue(e = null, t = false) {
|
|
|
604
616
|
if (t.cn.size) GlobalQueue.Dt(t.cn, e);
|
|
605
617
|
sweepTransientStoreNodes();
|
|
606
618
|
// Lanes only enter activeLanes through the engine's getOrCreateLane.
|
|
607
|
-
if (activeLanes.size) GlobalQueue.
|
|
619
|
+
if (activeLanes.size) GlobalQueue.dn(e);
|
|
608
620
|
}
|
|
609
621
|
}
|
|
610
622
|
|
|
@@ -710,7 +722,7 @@ function transitionComplete(e) {
|
|
|
710
722
|
// Override blockage lives with the engine (absent hook = "no optimistic
|
|
711
723
|
// blockage"); the hook's loops over _optimisticNodes/_optimisticStores are
|
|
712
724
|
// no-ops when the transition holds neither, so no pre-check is needed.
|
|
713
|
-
if (t && GlobalQueue.
|
|
725
|
+
if (t && GlobalQueue.Tn?.(e)) t = false;
|
|
714
726
|
t && (e.fn = true);
|
|
715
727
|
return t;
|
|
716
728
|
}
|