@solidjs/signals 2.0.0-rc.16 → 2.0.0-rc.17
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 +1719 -1603
- package/dist/dev.attribution.js +13 -13
- package/dist/dev.js +182 -45
- package/dist/observe/boundaries.js +2 -2
- package/dist/observe/core/async.js +3 -1
- package/dist/observe/core/context.js +2 -0
- package/dist/observe/core/core.js +26 -10
- package/dist/observe/core/effect.js +2 -0
- package/dist/observe/core/graph.js +10 -8
- package/dist/observe/core/lanes.js +25 -14
- package/dist/observe/core/owner.js +73 -47
- package/dist/observe/core/scheduler.js +189 -173
- package/dist/observe/core/verdict.js +3 -0
- package/dist/observe/map.js +2 -2
- package/dist/observe/signals.js +6 -1
- package/dist/observe/store/affects.js +13 -13
- package/dist/observe/store/optimistic.js +112 -71
- package/dist/observe/store/store.js +25 -16
- package/dist/prod/boundaries.js +2 -2
- package/dist/prod/core/async.js +3 -1
- package/dist/prod/core/context.js +2 -0
- package/dist/prod/core/core.js +31 -15
- package/dist/prod/core/effect.js +2 -0
- package/dist/prod/core/graph.js +10 -8
- package/dist/prod/core/heap.js +1 -1
- package/dist/prod/core/lanes.js +25 -14
- package/dist/prod/core/owner.js +70 -44
- package/dist/prod/core/scheduler.js +43 -27
- package/dist/prod/core/verdict.js +4 -1
- package/dist/prod/map.js +2 -2
- package/dist/prod/signals.js +18 -13
- package/dist/prod/store/optimistic.js +112 -71
- package/dist/prod/store/store.js +18 -7
- package/dist/types/core/core.d.ts +10 -3
- package/dist/types/core/dev.d.ts +9 -1
- package/dist/types/core/error.d.ts +9 -1
- package/dist/types/core/owner.d.ts +4 -0
- package/dist/types/core/scheduler.d.ts +23 -4
- package/package.json +1 -1
package/dist/prod/core/core.js
CHANGED
|
@@ -10,7 +10,7 @@ import { insertIntoHeap, deleteFromHeap, insertIntoHeapHeight, enqueueSub, markN
|
|
|
10
10
|
|
|
11
11
|
import { inheritId, linkChild, disposeChildren, markDisposal } from "./owner.js";
|
|
12
12
|
|
|
13
|
-
import { schedule, externalUntrackHook, clock, txOf, flushTransaction, laneWriteHook, joinFuture, queuePendingNode, globalQueue, notifyEpoch, insertSubs, dirtyQueue, observeFlightHook,
|
|
13
|
+
import { schedule, externalUntrackHook, clock, txOf, flushTransaction, laneWriteHook, joinFuture, queuePendingNode, globalQueue, notifyEpoch, insertSubs, dirtyQueue, observeFlightHook, laneStageHook, passLane, laneReadHook, wireExternalSourceHook, bumpNotifyEpoch, setPassLane, joinPassTx, freshHook, GlobalQueue, laneOutcomeHook, passTx, holdNode, laneDirty, heldTrim, staleReader, laneSeamsHook, stagedReaders, deferZombie } from "./scheduler.js";
|
|
14
14
|
|
|
15
15
|
// The heap's per-node step. A tracked effect's heap visit is its compute
|
|
16
16
|
// phase — empty, like a user effect whose compute reads nothing — and hands
|
|
@@ -210,7 +210,9 @@ function recompute(e, t = false) {
|
|
|
210
210
|
// and own nothing to park.
|
|
211
211
|
if (n === EFFECT_TRACKED || e.l & CONFIG_STAGED) {
|
|
212
212
|
// A lazy node only those children read is released when this pass
|
|
213
|
-
// ends, if the pass doesn't read it again (#4142).
|
|
213
|
+
// ends, if the pass doesn't read it again (#4142). A cleanup or
|
|
214
|
+
// `unobserved` hook that throws here neither stops the pass nor skips
|
|
215
|
+
// that drain: its error is rethrown when the flush ends (owner.ts).
|
|
214
216
|
const t = deferredReleases;
|
|
215
217
|
deferReleases(o = []);
|
|
216
218
|
disposeChildren(e);
|
|
@@ -331,7 +333,10 @@ function recompute(e, t = false) {
|
|
|
331
333
|
// (markNode(c) in read()) marks the running node as part of ordinary
|
|
332
334
|
// bookkeeping, and those marks are correctly discarded here.
|
|
333
335
|
c = (e.R & REACTIVE_MISSED_WAKE) !== 0;
|
|
334
|
-
A
|
|
336
|
+
// A mount's staged read joins it — except a nested derivation left
|
|
337
|
+
// pending: no render effect reaches it yet, so it holds nothing and
|
|
338
|
+
// reaches no boundary (Reader, #4203). A reader that does is caught.
|
|
339
|
+
A = !!(e.R & (i && (E || n || !(e.m & STATUS_PENDING)) ? REACTIVE_JOINED | REACTIVE_STAGED_READ : REACTIVE_JOINED));
|
|
335
340
|
// A verdict reader is one for as long as it probes, or reads a memo
|
|
336
341
|
// that does (`read`): a pass that did neither (REACTIVE_PROBED, the same
|
|
337
342
|
// bit) is an ordinary derivation again.
|
|
@@ -653,8 +658,8 @@ function computed(e, t) {
|
|
|
653
658
|
me: null,
|
|
654
659
|
Re: null,
|
|
655
660
|
Ve: false,
|
|
656
|
-
|
|
657
|
-
|
|
661
|
+
je: NOT_PENDING,
|
|
662
|
+
Be: -1,
|
|
658
663
|
Ee: 0,
|
|
659
664
|
v: NOT_PENDING,
|
|
660
665
|
I: 0,
|
|
@@ -682,6 +687,12 @@ function computed(e, t) {
|
|
|
682
687
|
if (t.m & STATUS_UNINITIALIZED || e.i !== EFFECT_RENDER) joinPassTx(txOf(t), !e.i && e.m & STATUS_UNINITIALIZED || freshHook?.(e));
|
|
683
688
|
}
|
|
684
689
|
|
|
690
|
+
/** `joinPass` into the hold `t` (a folded guess's: store/optimistic.ts
|
|
691
|
+
* `foldedRead`, D-34) — a reader is never a render effect there. */ function joinPassAt(e, t) {
|
|
692
|
+
e.R |= REACTIVE_JOINED;
|
|
693
|
+
joinPassTx(t, !e.i && e.m & STATUS_UNINITIALIZED || freshHook?.(e));
|
|
694
|
+
}
|
|
695
|
+
|
|
685
696
|
/** A15's stale reader (shared-hole and reveal corollaries): a render effect
|
|
686
697
|
* reading a node held by a transaction that is not the flush's. It is served
|
|
687
698
|
* the committed value — a pending one's too — publishes mainline, and is
|
|
@@ -739,7 +750,7 @@ function computed(e, t) {
|
|
|
739
750
|
|
|
740
751
|
/** The computed whose pass a creation under `owner` belongs to (a root's is
|
|
741
752
|
* its nearest computed ancestor's), or null outside every pass. */ function creatorPass(e) {
|
|
742
|
-
return e !== null && e.
|
|
753
|
+
return e !== null && e.Qe ? e.Ye : e;
|
|
743
754
|
}
|
|
744
755
|
|
|
745
756
|
/** L2 — park the previous pass's frame for this node's commit: the children
|
|
@@ -837,7 +848,7 @@ const lazyOptions = {
|
|
|
837
848
|
|
|
838
849
|
function setupComputedNode(e, t) {
|
|
839
850
|
e.We = e;
|
|
840
|
-
const n = context?.
|
|
851
|
+
const n = context?.Qe ? context.Ye : context;
|
|
841
852
|
if (context) linkChild(context, e);
|
|
842
853
|
if (n) e.Fe = n.Fe + 1;
|
|
843
854
|
wireExternalSourceHook?.(e);
|
|
@@ -1058,7 +1069,7 @@ function markUnflushedStaged() {
|
|
|
1058
1069
|
// A held node (L2) carries a staged value between flushes too, but it was
|
|
1059
1070
|
// flushed — and parked. Not an unflushed write — unless rewritten since:
|
|
1060
1071
|
// then the flushed staging is what the world sees until the next flush.
|
|
1061
|
-
if (e.l & CONFIG_HELD) return e.T.
|
|
1072
|
+
if (e.l & CONFIG_HELD) return e.T.Be === clock ? e.T.je : NOT_PENDING;
|
|
1062
1073
|
return e.Ie;
|
|
1063
1074
|
}
|
|
1064
1075
|
|
|
@@ -1066,9 +1077,9 @@ function markUnflushedStaged() {
|
|
|
1066
1077
|
* flush left (`_flushed`) until the flush that carries the rewrite — the
|
|
1067
1078
|
* stash is stamped with the clock, which that flush advances. */ function stashFlushed(e) {
|
|
1068
1079
|
const t = e.T;
|
|
1069
|
-
if (t.
|
|
1070
|
-
t.
|
|
1071
|
-
t.
|
|
1080
|
+
if (t.Be !== clock) {
|
|
1081
|
+
t.je = e.se;
|
|
1082
|
+
t.Be = clock;
|
|
1072
1083
|
unflushedStaged = true;
|
|
1073
1084
|
}
|
|
1074
1085
|
}
|
|
@@ -1115,7 +1126,7 @@ function resyncUnflushedCompanions() {
|
|
|
1115
1126
|
|
|
1116
1127
|
function read(e) {
|
|
1117
1128
|
let t = context;
|
|
1118
|
-
if (t?.
|
|
1129
|
+
if (t?.Qe) t = t.Ye;
|
|
1119
1130
|
const n = e;
|
|
1120
1131
|
// CARVE 1: `owner` was `el._firewall || el` — a store leaf's status,
|
|
1121
1132
|
// height and error were read off its projection computed. (Typed loosely
|
|
@@ -1149,7 +1160,9 @@ function read(e) {
|
|
|
1149
1160
|
// Committed visibility for untracked and children-forbidden readers.
|
|
1150
1161
|
if (!t || e.se === NOT_PENDING || t.l & CONFIG_CHILDREN_FORBIDDEN) return e.Ie;
|
|
1151
1162
|
// (A plain signal is never uninitialized — the carve-out is `serve`'s.)
|
|
1152
|
-
|
|
1163
|
+
// Lane work over a folded lane (D-34), once the frame has a
|
|
1164
|
+
// transaction, reads the screen too.
|
|
1165
|
+
if ((observeFlightHook && t.l & CONFIG_VERDICT || laneStageHook && passLane?.folded) && stagedScreen(t)) return e.Ie;
|
|
1153
1166
|
stagedRead(t);
|
|
1154
1167
|
return e.se;
|
|
1155
1168
|
}
|
|
@@ -1290,7 +1303,10 @@ function read(e) {
|
|
|
1290
1303
|
|
|
1291
1304
|
/** A pass read a staging of this flush: it derives from what the flush may
|
|
1292
1305
|
* yet hold (REACTIVE_STAGED_READ — the seam decides). Lane work too (plan sec. 28, a
|
|
1293
|
-
* lane sees the screen plus its own guesses)
|
|
1306
|
+
* lane sees the screen plus its own guesses) — a folded lane's only before
|
|
1307
|
+
* the frame has a transaction (after, it reads the screen like a verdict
|
|
1308
|
+
* reader, `stagedScreen` — a list pass never pairs a held length with the
|
|
1309
|
+
* guess's rows, D-34): the staging is the screen if
|
|
1294
1310
|
* the frame commits — one pass, the common case — and a held write if it
|
|
1295
1311
|
* parks, which the seam repairs (`stagedReaders`): the pass re-derives on
|
|
1296
1312
|
* the committed world and its lane's runs wait that round, so the held
|
|
@@ -1527,4 +1543,4 @@ function staleValues(e, t = true) {
|
|
|
1527
1543
|
}
|
|
1528
1544
|
}
|
|
1529
1545
|
|
|
1530
|
-
export { bornInto, captureWriteSnapshot, clearSnapshots, computed, context, createEffectNode, effectStatusNotify, ext, isEqual, isSame, joinPass, markLateLinker, markRefresh, markSnapshotScope, markUnflushedStaged, notePromotedWrite, prepareComputed, pullComputed, read, readerSeesCommitted, recompute, releaseSnapshotScope, resyncUnflushedCompanions, runWithOwner, serve, setEffectStatusNotify, setMemo, setSignal, setSnapshotCapture, setVerdict, setWriteHook, signal, slotSignal, snapshotCaptureActive, snapshotSources, spectating, stagedRead, stagedScreen, staleValues, stashFlushed, statusNotifierOf, tracking, unflushedStaged, unflushedValue, untrack, verdict, writeHook };
|
|
1546
|
+
export { bornInto, captureWriteSnapshot, clearSnapshots, computed, context, createEffectNode, effectStatusNotify, ext, isEqual, isSame, joinPass, joinPassAt, markLateLinker, markRefresh, markSnapshotScope, markUnflushedStaged, notePromotedWrite, prepareComputed, pullComputed, read, readerSeesCommitted, recompute, releaseSnapshotScope, resyncUnflushedCompanions, runWithOwner, serve, setEffectStatusNotify, setMemo, setSignal, setSnapshotCapture, setVerdict, setWriteHook, signal, slotSignal, snapshotCaptureActive, snapshotSources, spectating, stagedRead, stagedScreen, staleValues, stashFlushed, statusNotifierOf, tracking, unflushedStaged, unflushedValue, untrack, verdict, writeHook };
|
package/dist/prod/core/effect.js
CHANGED
|
@@ -46,6 +46,8 @@ function notifyEffectStatus(t, e) {
|
|
|
46
46
|
return;
|
|
47
47
|
}
|
|
48
48
|
if (!globalQueue.notify(this, STATUS_ERROR, STATUS_ERROR)) {
|
|
49
|
+
// Dev locates the halt at the node that threw: the StatusError's
|
|
50
|
+
// source, upstream of this effect when a computed failed.
|
|
49
51
|
haltReactivity(unwrapStatusError(u));
|
|
50
52
|
throw u;
|
|
51
53
|
}
|
package/dist/prod/core/graph.js
CHANGED
|
@@ -2,7 +2,7 @@ import { REACTIVE_RECOMPUTING_DEPS, CONFIG_AUTO_DISPOSE, STATUS_PENDING, REACTIV
|
|
|
2
2
|
|
|
3
3
|
import { deleteFromHeap } from "./heap.js";
|
|
4
4
|
|
|
5
|
-
import { disposeChildren } from "./owner.js";
|
|
5
|
+
import { disposeChildren, runCleanup } from "./owner.js";
|
|
6
6
|
|
|
7
7
|
import { bumpNotifyEpoch, dirtyQueue } from "./scheduler.js";
|
|
8
8
|
|
|
@@ -27,7 +27,9 @@ function unlinkSubs(e) {
|
|
|
27
27
|
if (o === null) {
|
|
28
28
|
// Store slot nodes dispatch to the ONE shared hook — no per-node
|
|
29
29
|
// unobserved closure, no NodeExtension to hold it.
|
|
30
|
-
|
|
30
|
+
// A user hook that throws is collected like a cleanup (owner.ts): the
|
|
31
|
+
// unlink and the teardown around it carry on.
|
|
32
|
+
if (n.l & CONFIG_SLOT_NODE) slotUnobservedHook(n); else if (n.T?.we) runCleanup(n.T.we);
|
|
31
33
|
// No more subscribers; only tear down if CONFIG_AUTO_DISPOSE is set.
|
|
32
34
|
// A pending node is exempt: its in-flight async work is an observer —
|
|
33
35
|
// tearing down would orphan the work and re-execute it on the next
|
|
@@ -141,18 +143,18 @@ function link(e, n) {
|
|
|
141
143
|
// [deps.._depsTail] prefix — the O(1) equivalent of scanning the dep list
|
|
142
144
|
// (the old alien-signals `isValidLink` walk, O(n²) when a computation
|
|
143
145
|
// re-reads earlier deps non-consecutively, e.g. store leaf reads).
|
|
144
|
-
const
|
|
145
|
-
if (
|
|
146
|
-
const
|
|
146
|
+
const r = e.ve;
|
|
147
|
+
if (r !== null && r.u === n && (!s || r.Te === n.Ne)) return;
|
|
148
|
+
const t = n._ = e.ve = {
|
|
147
149
|
S: e,
|
|
148
150
|
u: n,
|
|
149
151
|
G: o,
|
|
150
|
-
Xe:
|
|
152
|
+
Xe: r,
|
|
151
153
|
t: null,
|
|
152
154
|
Te: n.Ne
|
|
153
155
|
};
|
|
154
|
-
if (l !== null) l.G =
|
|
155
|
-
if (
|
|
156
|
+
if (l !== null) l.G = t; else n.C = t;
|
|
157
|
+
if (r !== null) r.t = t; else e.o = t;
|
|
156
158
|
// New subscriber edge: staged-rewrite skips (§12d) must not miss it.
|
|
157
159
|
bumpNotifyEpoch();
|
|
158
160
|
}
|
package/dist/prod/core/heap.js
CHANGED
|
@@ -16,7 +16,7 @@ import { dirtyQueue } from "./scheduler.js";
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
function actualInsertIntoHeap(e, t) {
|
|
19
|
-
const E = (e._parent?.
|
|
19
|
+
const E = (e._parent?.Qe ? e._parent.Ye?.Fe : e._parent?.Fe) ?? -1;
|
|
20
20
|
if (E >= e.Fe) e.Fe = E + 1;
|
|
21
21
|
const n = e.Fe;
|
|
22
22
|
const I = t.$e[n];
|
package/dist/prod/core/lanes.js
CHANGED
|
@@ -435,12 +435,21 @@ function supersede(e, n, s) {
|
|
|
435
435
|
* lane's latest. NOT_PENDING falls through: lane work's read of a pending
|
|
436
436
|
* member throws like any. */ function laneRead(e, n) {
|
|
437
437
|
const s = n.l & CONFIG_GUESS;
|
|
438
|
+
const t = txOf(n);
|
|
439
|
+
// A lane a correction folded under (D-34): store/optimistic.ts reads it.
|
|
440
|
+
// Before the authoritative arm: an `until` outside the lane reads the
|
|
441
|
+
// same truth there, and joins the hold it folded into — its posture over
|
|
442
|
+
// a foreign hold (D-31), which the truth read alone would not take.
|
|
443
|
+
const l = e !== null && t.folded;
|
|
444
|
+
if (l) {
|
|
445
|
+
const s = l(e, n);
|
|
446
|
+
if (s !== NOT_PENDING) return s;
|
|
447
|
+
}
|
|
438
448
|
if (e !== null && e.l & CONFIG_AUTHORITATIVE) return GlobalQueue.rn(n);
|
|
439
|
-
const
|
|
449
|
+
const u = n.m;
|
|
440
450
|
// An errored member's lane value predates its error; the error is the answer (#4106).
|
|
441
|
-
if (!s &&
|
|
451
|
+
if (!s && u & STATUS_ERROR) return NOT_PENDING;
|
|
442
452
|
if (e === null) return laneValueOf(n);
|
|
443
|
-
const l = txOf(n);
|
|
444
453
|
if (!tracking || e.l & CONFIG_CHILDREN_FORBIDDEN) {
|
|
445
454
|
// An untracked read inside lane work derives from the lane like a
|
|
446
455
|
// tracked one, minus the subscription — `untrack` is about
|
|
@@ -449,33 +458,33 @@ function supersede(e, n, s) {
|
|
|
449
458
|
// lanes read by one pass link, as for tracked reads). Outside lane
|
|
450
459
|
// work, the screen.
|
|
451
460
|
if (passLane === null || e.l & CONFIG_CHILDREN_FORBIDDEN) return display(n);
|
|
452
|
-
enterLane(
|
|
453
|
-
if (!s &&
|
|
461
|
+
enterLane(t, e);
|
|
462
|
+
if (!s && u & STATUS_PENDING) return NOT_PENDING;
|
|
454
463
|
return laneValueOf(n);
|
|
455
464
|
}
|
|
456
|
-
if (e.i === EFFECT_RENDER && (passLane === null || !sameLane(passLane,
|
|
465
|
+
if (e.i === EFFECT_RENDER && (passLane === null || !sameLane(passLane, t))) {
|
|
457
466
|
if (!s) {
|
|
458
467
|
// A flight whose value has never shown: nothing to show — the leaf
|
|
459
468
|
// waits on it, and its own frame holds (A15, #3334), not the lane's
|
|
460
469
|
// parent. The landing lands the frame: its own pass (the flush
|
|
461
470
|
// joined its transaction) reads the answer it waited on — the lane's
|
|
462
471
|
// staging, revealed at this seam.
|
|
463
|
-
if (
|
|
472
|
+
if (u & STATUS_PENDING && n.T.v === NOT_PENDING) return NOT_PENDING;
|
|
464
473
|
if (n.se !== NOT_PENDING && e.l & CONFIG_HELD && flushTransaction !== null && resolveTx(flushTransaction) === txOf(e)) return n.se;
|
|
465
474
|
}
|
|
466
|
-
if (
|
|
467
|
-
if (!s && !
|
|
475
|
+
if (t.ln || !s && u & STATUS_PENDING) {
|
|
476
|
+
if (!s && !t.ke && u & STATUS_UNINITIALIZED) throw new NotReadyError(null);
|
|
468
477
|
// Re-derived at the reveal; once per pass (the pass may also be a stale
|
|
469
478
|
// reader of a transaction, REACTIVE_FRAME_READ — both reruns apply).
|
|
470
479
|
if (!(e.R & REACTIVE_SCREEN_READ)) {
|
|
471
480
|
e.R |= REACTIVE_FRAME_READ | REACTIVE_SCREEN_READ;
|
|
472
|
-
|
|
481
|
+
t.En.push(e);
|
|
473
482
|
}
|
|
474
483
|
return display(n);
|
|
475
484
|
}
|
|
476
485
|
}
|
|
477
|
-
enterLane(
|
|
478
|
-
if (!s &&
|
|
486
|
+
enterLane(t, e);
|
|
487
|
+
if (!s && u & STATUS_PENDING) return NOT_PENDING;
|
|
479
488
|
return laneValueOf(n);
|
|
480
489
|
}
|
|
481
490
|
|
|
@@ -598,10 +607,12 @@ function laneSeam(e, n) {
|
|
|
598
607
|
// question is the one being asked (`question`: a slice's, a derive's
|
|
599
608
|
// continuation's — the flight's); unstamped writes and mainline are
|
|
600
609
|
// current.
|
|
601
|
-
|
|
610
|
+
// A store re-bases a keyed arrangement guess over an older truth, and
|
|
611
|
+
// folds a current one beneath it (D-34): true.
|
|
612
|
+
const s = stale(e, question);
|
|
613
|
+
if (GlobalQueue.an?.(e, n, s) || s) {
|
|
602
614
|
e.se = n;
|
|
603
615
|
e.l |= CONFIG_HELD;
|
|
604
|
-
GlobalQueue.an?.(e, n);
|
|
605
616
|
schedule();
|
|
606
617
|
return n;
|
|
607
618
|
}
|
package/dist/prod/core/owner.js
CHANGED
|
@@ -6,7 +6,7 @@ import { unobserved, clearDeps } from "./graph.js";
|
|
|
6
6
|
|
|
7
7
|
import { deleteFromHeap } from "./heap.js";
|
|
8
8
|
|
|
9
|
-
import { schedule, dirtyQueue } from "./scheduler.js";
|
|
9
|
+
import { globalQueue, schedule, dirtyQueue } from "./scheduler.js";
|
|
10
10
|
|
|
11
11
|
// L2 (step 2): the parked frame. A pass parks the previous frame's children
|
|
12
12
|
// and `_disposal` list on `_x._pendingFirstChild` / `_x._pendingDisposal`
|
|
@@ -35,17 +35,41 @@ function dispose(e) {
|
|
|
35
35
|
unobserved(e);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
// The first error a cleanup threw, boxed so a thrown `undefined` still
|
|
39
|
+
// counts. A throwing cleanup stops neither its siblings nor the rest of the
|
|
40
|
+
// tree: every cleanup runs. The error is rethrown once the teardown is over:
|
|
41
|
+
// by a root's dispose call outside a flush (`disposeRootSelf`), otherwise when
|
|
42
|
+
// the flush ends (`flush`; `runCleanup` schedules one), so a flush is never
|
|
43
|
+
// left half-applied. A halt drops it: the error that halted is the one
|
|
44
|
+
// reported, and a stale one must not surface from a later, unrelated dispose.
|
|
45
|
+
let cleanupError;
|
|
46
|
+
|
|
47
|
+
/** Runs a cleanup or `unobserved` hook; a throw is collected, not raised. @internal */ function runCleanup(e) {
|
|
48
|
+
try {
|
|
49
|
+
e();
|
|
50
|
+
} catch (e) {
|
|
51
|
+
cleanupError ||= [ e ];
|
|
52
|
+
schedule();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Rethrows the error `runCleanup` collected, once; `drop` discards it. @internal */ function rethrowCleanupError(e) {
|
|
57
|
+
const n = cleanupError;
|
|
58
|
+
cleanupError = undefined;
|
|
59
|
+
if (n && !e) throw n[0];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function disposeChildren(e, n = false, r) {
|
|
63
|
+
const t = e.R;
|
|
64
|
+
if (t & REACTIVE_DISPOSED) return;
|
|
41
65
|
// A parked frame dies with its owner (#3024): the commit that would retire
|
|
42
66
|
// it never comes once the node is DISPOSED, so it drains here or its
|
|
43
67
|
// cleanups never run and it stays subscribed, rerunning in a torn-down
|
|
44
68
|
// tree (#3561). Death only (`self`): a re-pass leaves the frame reacting
|
|
45
69
|
// until the commit.
|
|
46
|
-
if (n && !
|
|
70
|
+
if (n && !r && e.T !== null && (e.T.Ue !== null || e.T.me !== null)) disposeChildren(e, false, true);
|
|
47
71
|
if (n) {
|
|
48
|
-
e.R =
|
|
72
|
+
e.R = t | REACTIVE_DISPOSED;
|
|
49
73
|
}
|
|
50
74
|
if (n && e.X) {
|
|
51
75
|
if (e.T !== null) e.T.Se = null;
|
|
@@ -53,10 +77,10 @@ function disposeChildren(e, n = false, t) {
|
|
|
53
77
|
// flight's — a transaction's or a lane's (its `_x._transaction`) — or a
|
|
54
78
|
// stale reader's (REACTIVE_FRAME_READ — a frame that derived from one).
|
|
55
79
|
// Nothing else re-examines a dead node, so make sure a seam comes.
|
|
56
|
-
if (e.m & STATUS_PENDING && e.T?.Re != null ||
|
|
80
|
+
if (e.m & STATUS_PENDING && e.T?.Re != null || t & REACTIVE_FRAME_READ) schedule();
|
|
57
81
|
}
|
|
58
|
-
let l =
|
|
59
|
-
if (
|
|
82
|
+
let l = r ? e.T.Ue : e.De;
|
|
83
|
+
if (r) e.T.Ue = null; else e.De = null;
|
|
60
84
|
while (l) {
|
|
61
85
|
const e = l;
|
|
62
86
|
// Owner teardown is death regardless of the child's own lifecycle
|
|
@@ -88,34 +112,34 @@ function disposeChildren(e, n = false, t) {
|
|
|
88
112
|
// would still be holding.
|
|
89
113
|
l = l.pe;
|
|
90
114
|
}
|
|
91
|
-
if (!
|
|
115
|
+
if (!r) e.ye = 0;
|
|
92
116
|
// O(1) splice out of parent's chain on individual dispose. Skipped during
|
|
93
117
|
// batch dispose (parent already disposed) and for a zombie (it sits on the
|
|
94
118
|
// parent's parked frame, drained whole by the commit). We leave
|
|
95
119
|
// node._nextSibling intact so outer walks that already advanced past us
|
|
96
120
|
// still reach later siblings.
|
|
97
|
-
if (n && !
|
|
121
|
+
if (n && !r && !(t & REACTIVE_ZOMBIE) && e._parent !== null && !(e._parent.R & REACTIVE_DISPOSED)) {
|
|
98
122
|
const n = e.Ze;
|
|
99
|
-
const
|
|
100
|
-
if (n !== null) n.pe =
|
|
101
|
-
if (
|
|
123
|
+
const r = e.pe;
|
|
124
|
+
if (n !== null) n.pe = r; else e._parent.De = r;
|
|
125
|
+
if (r !== null) r.Ze = n;
|
|
102
126
|
e.Ze = null;
|
|
103
127
|
}
|
|
104
|
-
runDisposal(e,
|
|
128
|
+
runDisposal(e, r);
|
|
105
129
|
// Final effect-returned cleanup fires at true disposal, after `_disposal`
|
|
106
130
|
// to mirror rerun ordering (compute-phase teardown first, cleanup last).
|
|
107
131
|
if (n && e.Je) {
|
|
108
132
|
const n = e.Je;
|
|
109
133
|
e.Je = undefined;
|
|
110
|
-
n
|
|
134
|
+
runCleanup(n);
|
|
111
135
|
}
|
|
112
136
|
}
|
|
113
137
|
|
|
114
138
|
function linkChild(e, n) {
|
|
115
|
-
const
|
|
139
|
+
const r = e.De;
|
|
116
140
|
n.Ze = null;
|
|
117
|
-
n.pe =
|
|
118
|
-
if (
|
|
141
|
+
n.pe = r;
|
|
142
|
+
if (r !== null) r.Ze = n;
|
|
119
143
|
e.De = n;
|
|
120
144
|
}
|
|
121
145
|
|
|
@@ -126,27 +150,26 @@ function runDisposal(e, n) {
|
|
|
126
150
|
// walk ran every entry a second time. Detached, the re-entrant drain finds
|
|
127
151
|
// nothing. The same shape latched a throwing cleanup: the list survived the
|
|
128
152
|
// throw and every later drain re-ran and re-threw it.
|
|
129
|
-
const
|
|
130
|
-
if (!
|
|
153
|
+
const r = n ? e.T.me : e.He;
|
|
154
|
+
if (!r) return;
|
|
131
155
|
if (n) e.T.me = null; else e.He = null;
|
|
132
|
-
if (Array.isArray(
|
|
156
|
+
if (Array.isArray(r)) {
|
|
133
157
|
// Unwind order (#3572, restores 1.x #1562): later registrations run
|
|
134
158
|
// before earlier ones. Children have already been disposed by the caller,
|
|
135
159
|
// so with LIFO a body that registers cleanup before creating its children
|
|
136
160
|
// tears down after them — the same order a per-component owner gives.
|
|
137
|
-
for (let e =
|
|
138
|
-
|
|
139
|
-
n.call(n);
|
|
161
|
+
for (let e = r.length - 1; e >= 0; e--) {
|
|
162
|
+
runCleanup(r[e]);
|
|
140
163
|
}
|
|
141
164
|
} else {
|
|
142
|
-
|
|
165
|
+
runCleanup(r);
|
|
143
166
|
}
|
|
144
167
|
}
|
|
145
168
|
|
|
146
169
|
function childId(e, n) {
|
|
147
|
-
let
|
|
148
|
-
while (
|
|
149
|
-
if (
|
|
170
|
+
let r = e;
|
|
171
|
+
while (r.l & CONFIG_TRANSPARENT && r._parent) r = r._parent;
|
|
172
|
+
if (r.id != null) return formatId(r.id, n ? r.ye++ : r.ye);
|
|
150
173
|
throw new Error("");
|
|
151
174
|
}
|
|
152
175
|
|
|
@@ -163,8 +186,8 @@ function childId(e, n) {
|
|
|
163
186
|
* The id a freshly-created node inherits: an explicit `options.id` wins;
|
|
164
187
|
* transparent nodes share their parent's id; otherwise the parent's next
|
|
165
188
|
* child id is consumed (or `undefined` outside an id-carrying tree).
|
|
166
|
-
*/ function inheritId(e, n,
|
|
167
|
-
return e?.id ?? (n ?
|
|
189
|
+
*/ function inheritId(e, n, r) {
|
|
190
|
+
return e?.id ?? (n ? r?.id : r?.id != null ? getNextChildId(r) : undefined);
|
|
168
191
|
}
|
|
169
192
|
|
|
170
193
|
/**
|
|
@@ -177,8 +200,8 @@ function childId(e, n) {
|
|
|
177
200
|
}
|
|
178
201
|
|
|
179
202
|
function formatId(e, n) {
|
|
180
|
-
const
|
|
181
|
-
return e + (
|
|
203
|
+
const r = n.toString(36), t = r.length - 1;
|
|
204
|
+
return e + (t ? String.fromCharCode(64 + t) : "") + r;
|
|
182
205
|
}
|
|
183
206
|
|
|
184
207
|
/**
|
|
@@ -253,6 +276,9 @@ function formatId(e, n) {
|
|
|
253
276
|
|
|
254
277
|
function disposeRootSelf(e = true) {
|
|
255
278
|
disposeChildren(this, e);
|
|
279
|
+
// Outside a flush the dispose call rethrows; inside one, the flush does.
|
|
280
|
+
// Kept out of `disposeChildren`, whose hot path it slows.
|
|
281
|
+
if (cleanupError && !globalQueue.q) rethrowCleanupError();
|
|
256
282
|
}
|
|
257
283
|
|
|
258
284
|
/**
|
|
@@ -264,22 +290,22 @@ function disposeRootSelf(e = true) {
|
|
|
264
290
|
* @internal
|
|
265
291
|
*/ function createOwner(e) {
|
|
266
292
|
const n = context;
|
|
267
|
-
const
|
|
293
|
+
const r = e?.transparent ?? false;
|
|
268
294
|
// Prod and observe boilerplates (see core.ts computed()). The observe
|
|
269
295
|
// literal carries the `_name` and `_loc` slots the rendering layer fills
|
|
270
296
|
// with the component label and JSX site (`owner._name = "<App>"`,
|
|
271
297
|
// `owner._loc = "src/App.tsx:3:5"`) — slots, so labelling a root is a
|
|
272
298
|
// plain store rather than a shape fork between labelled and plain roots.
|
|
273
|
-
const
|
|
274
|
-
id: inheritId(e,
|
|
275
|
-
l:
|
|
299
|
+
const t = {
|
|
300
|
+
id: inheritId(e, r, n),
|
|
301
|
+
l: r ? CONFIG_TRANSPARENT : 0,
|
|
276
302
|
// Present from birth: disposeChildren marks death here on roots too, and
|
|
277
303
|
// a first store of a missing property is a shape transition plus a
|
|
278
304
|
// properties-store allocation per root — on a 100×4 table teardown that
|
|
279
305
|
// was 12 KB and most of disposal's time (2026-10-17).
|
|
280
306
|
R: REACTIVE_NONE,
|
|
281
|
-
|
|
282
|
-
Ye: n?.
|
|
307
|
+
Qe: true,
|
|
308
|
+
Ye: n?.Qe ? n.Ye : n,
|
|
283
309
|
De: null,
|
|
284
310
|
pe: null,
|
|
285
311
|
Ze: null,
|
|
@@ -290,8 +316,8 @@ function disposeRootSelf(e = true) {
|
|
|
290
316
|
_parent: n,
|
|
291
317
|
dispose: disposeRootSelf
|
|
292
318
|
};
|
|
293
|
-
if (n) linkChild(n,
|
|
294
|
-
return
|
|
319
|
+
if (n) linkChild(n, t);
|
|
320
|
+
return t;
|
|
295
321
|
}
|
|
296
322
|
|
|
297
323
|
/**
|
|
@@ -329,11 +355,11 @@ function disposeRootSelf(e = true) {
|
|
|
329
355
|
*
|
|
330
356
|
* @description https://v2.solidjs.com/reference/solid-js/advanced/owner-introspection/create-root
|
|
331
357
|
*/ function createRoot(e, n) {
|
|
332
|
-
const
|
|
358
|
+
const r = createOwner(n);
|
|
333
359
|
// A bound function, not an arrow: an arrow would share this scope, which
|
|
334
360
|
// holds `init`, so keeping `dispose` would keep `init` and all it closes
|
|
335
361
|
// over alive (#4095).
|
|
336
|
-
return runWithOwner(
|
|
362
|
+
return runWithOwner(r, () => e(disposeRootSelf.bind(r, true)));
|
|
337
363
|
}
|
|
338
364
|
|
|
339
|
-
export { cleanup, createOwner, createRoot, dispose, disposeChildren, getNextChildId, getObserver, getOwner, inheritId, isDisposed, linkChild, markDisposal, peekNextChildId };
|
|
365
|
+
export { cleanup, createOwner, createRoot, dispose, disposeChildren, getNextChildId, getObserver, getOwner, inheritId, isDisposed, linkChild, markDisposal, peekNextChildId, rethrowCleanupError, runCleanup };
|