@solidjs/signals 2.0.0-rc.2 → 2.0.0-rc.20
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 +6505 -0
- package/dist/dev.attribution.js +3484 -0
- package/dist/dev.js +10227 -9508
- package/dist/observe/affects-marks.js +86 -0
- package/dist/observe/affects.js +112 -0
- package/dist/observe/attribution.js +7 -0
- package/dist/observe/boundaries.js +696 -0
- package/dist/observe/core/action.js +182 -0
- package/dist/observe/core/async.js +824 -0
- package/dist/observe/core/attribution-costs.js +66 -0
- package/dist/observe/core/attribution-feedback.js +280 -0
- package/dist/observe/core/attribution-hooks.js +83 -0
- package/dist/observe/core/attribution-queries.js +43 -0
- package/dist/observe/core/attribution.js +3079 -0
- package/dist/observe/core/constants.js +269 -0
- package/dist/observe/core/context.js +63 -0
- package/dist/observe/core/copies.js +10 -0
- package/dist/observe/core/core.js +1707 -0
- package/dist/observe/core/dev.js +394 -0
- package/dist/observe/core/effect.js +193 -0
- package/dist/observe/core/error-hooks.js +71 -0
- package/dist/observe/core/error.js +82 -0
- package/dist/observe/core/external.js +97 -0
- package/dist/observe/core/graph.js +162 -0
- package/dist/observe/core/heap.js +124 -0
- package/dist/observe/core/identities.js +62 -0
- package/dist/observe/core/invariants.js +12 -0
- package/dist/observe/core/lanes.js +972 -0
- package/dist/observe/core/owner.js +371 -0
- package/dist/observe/core/scheduler.js +1157 -0
- package/dist/observe/core/truth.js +47 -0
- package/dist/observe/core/verdict.js +470 -0
- package/dist/observe/deferred.js +186 -0
- package/dist/observe/flatten.js +79 -0
- package/dist/observe/from.js +248 -0
- package/dist/observe/index.js +71 -0
- package/dist/observe/map.js +593 -0
- package/dist/observe/reveal.js +212 -0
- package/dist/observe/signals.js +717 -0
- package/dist/observe/store/affects.js +142 -0
- package/dist/observe/store/edges.js +39 -0
- package/dist/observe/store/hooks.js +16 -0
- package/dist/observe/store/index.js +47 -0
- package/dist/observe/store/optimistic.js +713 -0
- package/dist/observe/store/projection.js +345 -0
- package/dist/observe/store/reconcile.js +281 -0
- package/dist/observe/store/store.js +2127 -0
- package/dist/observe/store/storePath.js +103 -0
- package/dist/observe/store/target.js +46 -0
- package/dist/observe/store/types.js +83 -0
- package/dist/observe/store/utils.js +1063 -0
- package/dist/prod/affects-marks.js +86 -0
- package/dist/prod/affects.js +98 -112
- package/dist/prod/attribution.js +45 -0
- package/dist/prod/boundaries.js +578 -468
- package/dist/prod/core/action.js +92 -59
- package/dist/prod/core/async.js +522 -336
- package/dist/prod/core/attribution-hooks.js +3 -0
- package/dist/prod/core/constants.js +168 -44
- package/dist/prod/core/context.js +12 -16
- package/dist/prod/core/copies.js +10 -0
- package/dist/prod/core/core.js +1345 -627
- package/dist/prod/core/dev.js +123 -1
- package/dist/prod/core/effect.js +113 -80
- package/dist/prod/core/error-hooks.js +71 -0
- package/dist/prod/core/error.js +18 -4
- package/dist/prod/core/external.js +6 -7
- package/dist/prod/core/graph.js +119 -72
- package/dist/prod/core/heap.js +85 -109
- package/dist/prod/core/identities.js +62 -0
- package/dist/prod/core/invariants.js +1 -32
- package/dist/prod/core/lanes.js +931 -132
- package/dist/prod/core/owner.js +192 -129
- package/dist/prod/core/scheduler.js +948 -642
- package/dist/prod/core/truth.js +47 -0
- package/dist/prod/core/verdict.js +409 -420
- package/dist/prod/deferred.js +184 -0
- package/dist/prod/flatten.js +77 -0
- package/dist/prod/from.js +248 -0
- package/dist/prod/index.js +40 -16
- package/dist/prod/map.js +481 -211
- package/dist/prod/reveal.js +212 -0
- package/dist/prod/signals.js +401 -84
- package/dist/prod/store/affects.js +142 -0
- package/dist/prod/store/edges.js +39 -0
- package/dist/prod/store/hooks.js +16 -0
- package/dist/prod/store/index.js +27 -18
- package/dist/prod/store/optimistic.js +707 -0
- package/dist/prod/store/projection.js +331 -0
- package/dist/prod/store/reconcile.js +281 -0
- package/dist/prod/store/store.js +1952 -267
- package/dist/prod/store/storePath.js +1 -1
- package/dist/prod/store/target.js +43 -0
- package/dist/prod/store/types.js +83 -0
- package/dist/prod/store/utils.js +997 -138
- package/dist/types/affects-marks.d.ts +14 -0
- package/dist/types/affects.d.ts +12 -30
- package/dist/types/attribution.d.ts +19 -0
- package/dist/types/attribution.prod.d.ts +22 -0
- package/dist/types/boundaries.d.ts +99 -124
- package/dist/types/carved.d.ts +1 -0
- package/dist/types/core/action.d.ts +23 -9
- package/dist/types/core/async.d.ts +34 -6
- package/dist/types/core/attribution-costs.d.ts +35 -0
- package/dist/types/core/attribution-feedback.d.ts +133 -0
- package/dist/types/core/attribution-hooks.d.ts +307 -8
- package/dist/types/core/attribution-queries.d.ts +24 -0
- package/dist/types/core/attribution.d.ts +802 -72
- package/dist/types/core/constants.d.ts +195 -38
- package/dist/types/core/copies.d.ts +54 -0
- package/dist/types/core/core.d.ts +295 -65
- package/dist/types/core/dev.d.ts +509 -22
- package/dist/types/core/effect.d.ts +1 -1
- package/dist/types/core/error-hooks.d.ts +71 -0
- package/dist/types/core/error.d.ts +18 -1
- package/dist/types/core/graph.d.ts +40 -1
- package/dist/types/core/heap.d.ts +5 -5
- package/dist/types/core/identities.d.ts +17 -0
- package/dist/types/core/index.d.ts +11 -7
- package/dist/types/core/invariants.d.ts +1 -26
- package/dist/types/core/lanes.d.ts +74 -43
- package/dist/types/core/owner.d.ts +27 -9
- package/dist/types/core/scheduler.d.ts +353 -102
- package/dist/types/core/truth.d.ts +18 -0
- package/dist/types/core/types.d.ts +97 -71
- package/dist/types/core/verdict.d.ts +35 -0
- package/dist/types/deferred.d.ts +28 -0
- package/dist/types/flatten.d.ts +8 -0
- package/dist/types/from.d.ts +47 -0
- package/dist/types/index.d.ts +33 -7
- package/dist/types/map.d.ts +4 -2
- package/dist/types/reveal.d.ts +19 -0
- package/dist/types/signals.d.ts +196 -45
- package/dist/types/store/affects.d.ts +12 -0
- package/dist/types/store/edges.d.ts +9 -0
- package/dist/types/store/hooks.d.ts +62 -0
- package/dist/types/store/index.d.ts +42 -15
- package/dist/types/store/optimistic.d.ts +24 -45
- package/dist/types/store/projection.d.ts +17 -68
- package/dist/types/store/reconcile.d.ts +3 -46
- package/dist/types/store/store.d.ts +133 -136
- package/dist/types/store/storePath.d.ts +1 -1
- package/dist/types/store/target.d.ts +166 -0
- package/dist/types/store/types.d.ts +105 -0
- package/dist/types/store/utils.d.ts +188 -0
- package/package.json +31 -26
- package/dist/node.cjs +0 -8745
- package/dist/prod/core/optimistic.js +0 -265
- package/dist/prod/store/next/optimistic.js +0 -385
- package/dist/prod/store/next/projection.js +0 -172
- package/dist/prod/store/next/reconcile.js +0 -331
- package/dist/prod/store/next/store.js +0 -1499
- package/dist/prod/store/next/target.js +0 -20
- package/dist/types/core/optimistic.d.ts +0 -6
- package/dist/types/store/next/optimistic.d.ts +0 -20
- package/dist/types/store/next/projection.d.ts +0 -8
- package/dist/types/store/next/reconcile.d.ts +0 -3
- package/dist/types/store/next/store.d.ts +0 -77
- package/dist/types/store/next/target.d.ts +0 -117
- package/dist/types-cjs/affects.d.cts +0 -47
- package/dist/types-cjs/boundaries.d.cts +0 -175
- package/dist/types-cjs/core/action.d.cts +0 -64
- package/dist/types-cjs/core/async.d.cts +0 -21
- package/dist/types-cjs/core/attribution-hooks.d.cts +0 -52
- package/dist/types-cjs/core/attribution.d.cts +0 -186
- package/dist/types-cjs/core/constants.d.cts +0 -97
- package/dist/types-cjs/core/context.d.cts +0 -36
- package/dist/types-cjs/core/core.d.cts +0 -163
- package/dist/types-cjs/core/dev.d.cts +0 -81
- package/dist/types-cjs/core/effect.d.cts +0 -30
- package/dist/types-cjs/core/error.d.cts +0 -47
- package/dist/types-cjs/core/external.d.cts +0 -15
- package/dist/types-cjs/core/graph.d.cts +0 -6
- package/dist/types-cjs/core/heap.d.cts +0 -22
- package/dist/types-cjs/core/index.d.cts +0 -13
- package/dist/types-cjs/core/invariants.d.cts +0 -59
- package/dist/types-cjs/core/lanes.d.cts +0 -44
- package/dist/types-cjs/core/optimistic.d.cts +0 -6
- package/dist/types-cjs/core/owner.d.cts +0 -124
- package/dist/types-cjs/core/scheduler.d.cts +0 -180
- package/dist/types-cjs/core/types.d.cts +0 -192
- package/dist/types-cjs/core/verdict.d.cts +0 -2
- package/dist/types-cjs/index.d.cts +0 -10
- package/dist/types-cjs/map.d.cts +0 -69
- package/dist/types-cjs/package.json +0 -3
- package/dist/types-cjs/signals.d.cts +0 -624
- package/dist/types-cjs/store/index.d.cts +0 -19
- package/dist/types-cjs/store/next/optimistic.d.cts +0 -20
- package/dist/types-cjs/store/next/projection.d.cts +0 -8
- package/dist/types-cjs/store/next/reconcile.d.cts +0 -3
- package/dist/types-cjs/store/next/store.d.cts +0 -77
- package/dist/types-cjs/store/next/target.d.cts +0 -117
- package/dist/types-cjs/store/optimistic.d.cts +0 -45
- package/dist/types-cjs/store/projection.d.cts +0 -70
- package/dist/types-cjs/store/reconcile.d.cts +0 -46
- package/dist/types-cjs/store/store.d.cts +0 -138
- package/dist/types-cjs/store/storePath.d.cts +0 -58
- package/dist/types-cjs/store/utils.d.cts +0 -74
package/dist/prod/core/dev.js
CHANGED
|
@@ -1,3 +1,125 @@
|
|
|
1
|
+
import { context } from "./core.js";
|
|
2
|
+
|
|
3
|
+
const diagnosticListeners = new Set;
|
|
4
|
+
|
|
5
|
+
const diagnosticCaptures = new Set;
|
|
6
|
+
|
|
7
|
+
let diagnosticSequence = 0;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Registers the console footer appended to the first console report of
|
|
11
|
+
* each diagnostic code — a discovery pointer to deeper guidance. Reported
|
|
12
|
+
* events carry it as trailing lines of the same console entry; events that
|
|
13
|
+
* surface as a thrown error instead get it as a follow-up line. Returning
|
|
14
|
+
* undefined for an event suppresses the footer. Passing undefined
|
|
15
|
+
* unregisters and resets the once-per-code memory.
|
|
16
|
+
*
|
|
17
|
+
* @internal A seam for `solid-js`, which owns the repair skill the footer
|
|
18
|
+
* names and installs it from both of its entries; not part of `DEV`. No-op
|
|
19
|
+
* outside dev builds, where nothing reports to the console.
|
|
20
|
+
*/ function setConsoleFooter(e) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Events built for an excluded subject: never delivered, never reported. */ const suppressedEvents = new WeakSet;
|
|
25
|
+
|
|
26
|
+
function isExcluded(e) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
1
30
|
const DEV = undefined;
|
|
2
31
|
|
|
3
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Root-first names of the owners enclosing `subject` (inclusive when the
|
|
34
|
+
* subject is itself a named owner). Signals hop to their registering owner
|
|
35
|
+
* (`_owner`, set by registerGraph). Unnamed owners are skipped so the path
|
|
36
|
+
* reads as the component tree plus the scope: `<App> › <TodoRow> › effect`.
|
|
37
|
+
* Public as `OBSERVE.ownerPath`; the core's own sites import it directly.
|
|
38
|
+
*/ function ownerPath(e) {
|
|
39
|
+
if (!e) return undefined;
|
|
40
|
+
let n = "_parent" in e ? e : e.H ?? null;
|
|
41
|
+
const t = [];
|
|
42
|
+
for (;n !== null; n = n._parent) {
|
|
43
|
+
const e = n._name;
|
|
44
|
+
if (typeof e === "string" && e.length) t.push(e);
|
|
45
|
+
}
|
|
46
|
+
return t.length ? t.reverse() : undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The innermost source location on the subject's chain: the subject's own
|
|
51
|
+
* `_loc` (a signal or computation created with `{ loc }`), else the first
|
|
52
|
+
* `_loc` up the owners — a signal hops to its registering owner, then
|
|
53
|
+
* `_parent` upward (a component root carries its JSX site). The same walk
|
|
54
|
+
* `ownerPath` makes; `undefined` when nothing on the chain was located.
|
|
55
|
+
*/ function locationOf(e) {
|
|
56
|
+
// Owners have a `_parent` key (null on a root); a signal has none and hops
|
|
57
|
+
// to its registering `_owner` instead.
|
|
58
|
+
for (let n = e; n; n = n._parent !== undefined ? n._parent : n.H) if (n._loc) return n._loc;
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Records a diagnostic on the structured channel (listeners, captures) and
|
|
64
|
+
* returns the entry. `subject` locates it: the current reactive `context` by
|
|
65
|
+
* default (right for the synchronous rule checks — they fire inside the
|
|
66
|
+
* scope that misbehaved); pass the node for scheduler-time findings whose
|
|
67
|
+
* ambient context is the flush, or `null` for events that have no location
|
|
68
|
+
* by nature. An `ownerPath` (or `loc`) already on the event wins over the
|
|
69
|
+
* subject walk (hosts whose owners are not signals' owners compute their
|
|
70
|
+
* own). Console output is a separate, dev-tier step — see `reportDiagnostic`.
|
|
71
|
+
*/ function emitDiagnostic(e, n = context) {
|
|
72
|
+
const t = {
|
|
73
|
+
sequence: ++diagnosticSequence,
|
|
74
|
+
...e
|
|
75
|
+
};
|
|
76
|
+
// The observer's own subtree: build the entry (the caller may throw its
|
|
77
|
+
// message) but tell nobody.
|
|
78
|
+
if (isExcluded()) {
|
|
79
|
+
suppressedEvents.add(t);
|
|
80
|
+
return t;
|
|
81
|
+
}
|
|
82
|
+
if (t.ownerPath === undefined) {
|
|
83
|
+
const e = ownerPath(n);
|
|
84
|
+
if (e) t.ownerPath = e;
|
|
85
|
+
}
|
|
86
|
+
if (t.loc === undefined) {
|
|
87
|
+
const e = locationOf(n);
|
|
88
|
+
if (e !== undefined) t.loc = e;
|
|
89
|
+
}
|
|
90
|
+
const o = n ?? undefined;
|
|
91
|
+
if (o !== undefined) eventSubjects.set(t, o);
|
|
92
|
+
for (const e of diagnosticListeners) e(t, o);
|
|
93
|
+
for (const e of diagnosticCaptures) e.push(t);
|
|
94
|
+
return t;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The subject each emitted event was about, for the console step, which
|
|
99
|
+
* runs after `emitDiagnostic` returned and can show what the node knows: a
|
|
100
|
+
* rendering runtime may stamp a binding effect with the DOM element it
|
|
101
|
+
* writes (`_devElement`), and a live element reference beside the message
|
|
102
|
+
* is the most addressable pointer a console can print. Listeners get the
|
|
103
|
+
* subject as their second argument instead; the map exists only to carry
|
|
104
|
+
* it from `emitDiagnostic` to `reportDiagnostic` across the call site's
|
|
105
|
+
* `reportDiagnostic(emitDiagnostic(…))`. Weak, keyed by the entry.
|
|
106
|
+
*/ const eventSubjects = new WeakMap;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* The console face of a diagnostic — ONE entry per finding: the message, the
|
|
110
|
+
* owner path (`in <App> › <TodoRow> › effect`) so a human can locate it, the
|
|
111
|
+
* source location (`at src/App.tsx:18:3` — see `locationLine`) when a node on
|
|
112
|
+
* the chain was compiled with one, the once-per-code footer as trailing
|
|
113
|
+
* lines, and — when the subject is a binding effect the rendering runtime
|
|
114
|
+
* tagged — the element it writes, as a second console argument (hover
|
|
115
|
+
* highlights it, click jumps to Elements).
|
|
116
|
+
* Severity picks the console method. Call sites report the entry
|
|
117
|
+
* `emitDiagnostic` returned so the structured and console channels never
|
|
118
|
+
* disagree. Dev-tier: in an observe build this is a no-op, so wiring paths
|
|
119
|
+
* that both emit and report (graph-size warnings) reach the channel only —
|
|
120
|
+
* production observability never writes to the console.
|
|
121
|
+
*/ function reportDiagnostic(e) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export { DEV, emitDiagnostic, isExcluded, ownerPath, reportDiagnostic, setConsoleFooter };
|
package/dist/prod/core/effect.js
CHANGED
|
@@ -1,55 +1,80 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { NOT_PENDING, EFFECT_USER, CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, EFFECT_TRACKED, EFFECT_RENDER, REACTIVE_DISPOSED, STATUS_ERROR, STATUS_PENDING } from "./constants.js";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { createEffectNode, recompute, computed, staleValues, ext, setEffectStatusNotify } from "./core.js";
|
|
4
4
|
|
|
5
5
|
import { unwrapStatusError, StatusError } from "./error.js";
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { trimStaleDeps } from "./graph.js";
|
|
8
|
+
|
|
9
|
+
import { enqueueSub } from "./heap.js";
|
|
10
|
+
|
|
11
|
+
import { globalQueue, schedule, heldRunHook, haltReactivity, GlobalQueue } from "./scheduler.js";
|
|
8
12
|
|
|
9
13
|
/**
|
|
10
14
|
* Effects are the leaf nodes of our reactive graph. When their sources change, they are
|
|
11
15
|
* automatically added to the queue of effects to re-execute, which will cause them to fetch their
|
|
12
16
|
* sources and recompute
|
|
13
|
-
*/ function effect(t, e, E,
|
|
14
|
-
const r = !!
|
|
15
|
-
const f = createEffectNode(t, e, E, r ? EFFECT_USER : EFFECT_RENDER,
|
|
17
|
+
*/ function effect(t, e, E, u) {
|
|
18
|
+
const r = !!u?.user;
|
|
19
|
+
const f = createEffectNode(t, e, E, r ? EFFECT_USER : EFFECT_RENDER, u);
|
|
16
20
|
recompute(f, true);
|
|
17
|
-
|
|
21
|
+
// A first pass that read a staged value was staged itself (recompute: born
|
|
22
|
+
// staged); the flush's commit replays this effect. Its first run is not this
|
|
23
|
+
// creation's (A29). A lane's node likewise: its run is the lane's, queued
|
|
24
|
+
// by the pass and released at the reveal (lanes.ts).
|
|
25
|
+
!u?.defer && f.ue === NOT_PENDING && !f.T?.Re?.P && (f.i === EFFECT_USER || u?.schedule ? globalQueue.enqueue(f.i, runEffect.bind(null, f)) : // Type 0: the synchronous first render, not a queued run (D-19).
|
|
26
|
+
runEffect(f, 0));
|
|
18
27
|
}
|
|
19
28
|
|
|
20
|
-
function notifyEffectStatus(
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
this.
|
|
26
|
-
if (this.Re === EFFECT_USER) {
|
|
29
|
+
function notifyEffectStatus() {
|
|
30
|
+
const t = this.m;
|
|
31
|
+
const e = this.T?.U;
|
|
32
|
+
if (t & STATUS_ERROR) {
|
|
33
|
+
globalQueue.notify(this, STATUS_PENDING, 0);
|
|
34
|
+
if (this.i === EFFECT_USER) {
|
|
27
35
|
// The error handler is the error arm of the effect phase (#2840 ruling):
|
|
28
36
|
// queue it like the effect function. It runs in the same imperative,
|
|
29
|
-
// writable scope
|
|
30
|
-
// (or optimistic lane) defers it exactly as it defers the success arm.
|
|
31
|
-
// No payload is queued — the node already carries `_statusFlags`/`_error`,
|
|
37
|
+
// writable scope and throws escalate the same way. No payload is queued — the node already carries `_statusFlags`/`_error`,
|
|
32
38
|
// and the runner dispatches on them, so a recovery before the effect
|
|
33
|
-
// phase takes the success arm instead.
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
this.
|
|
38
|
-
this.C.enqueue(this.Re, this.et ??= runEffect.bind(null, this));
|
|
39
|
+
// phase takes the success arm instead. (Re-read: the boundary
|
|
40
|
+
// notify above can run a redraw.)
|
|
41
|
+
if (this.m & STATUS_ERROR) {
|
|
42
|
+
this.Ae = true;
|
|
43
|
+
globalQueue.enqueue(this.i, this.W ??= runEffect.bind(null, this));
|
|
39
44
|
}
|
|
40
45
|
return;
|
|
41
46
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
// Dev locates the halt at the node that threw: the StatusError's
|
|
48
|
+
// source, upstream of this effect when a computed failed. The rethrow is
|
|
49
|
+
// the user's error, not the wrapper: the wrapper's `source` is a node, and
|
|
50
|
+
// a reporter that serializes the throw walks the reactive graph.
|
|
51
|
+
escalate(this, unwrapStatusError(e), this);
|
|
52
|
+
} else if (this.i === EFFECT_RENDER) {
|
|
53
|
+
globalQueue.notify(this, STATUS_PENDING | STATUS_ERROR, t, e);
|
|
48
54
|
}
|
|
49
55
|
}
|
|
50
56
|
|
|
51
|
-
|
|
52
|
-
|
|
57
|
+
/** An effect's error no boundary takes halts the system (boundary-or-halt):
|
|
58
|
+
* halted with `cause` and rethrown; dev locates it at `subject`. */ function escalate(t, e, E = t) {
|
|
59
|
+
if (!globalQueue.notify(t, STATUS_ERROR, STATUS_ERROR)) {
|
|
60
|
+
haltReactivity(e);
|
|
61
|
+
throw e;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function runEffect(t, e) {
|
|
66
|
+
if (!t.Ae || t.R & REACTIVE_DISPOSED) return;
|
|
67
|
+
// A queued run behind a fallback (boundaries.ts) waits for the reveal: a
|
|
68
|
+
// user effect's, which would read a DOM that is not attached, and a render
|
|
69
|
+
// effect's update too (maintainer, 2026-10-02: "we held the queue but let
|
|
70
|
+
// the sync renders through… we definitely shouldn't be showing portals
|
|
71
|
+
// early") — a Portal's or head-tag's render effect writes outside the
|
|
72
|
+
// hidden subtree. The synchronous first render on creation (`type` 0) is
|
|
73
|
+
// not a queued run and goes through, whichever boundary above is on its
|
|
74
|
+
// fallback then — an outer one about to reveal in this flush included
|
|
75
|
+
// (#4343). `_modified` stays set; the boundary re-queues the run
|
|
76
|
+
// (`release`).
|
|
77
|
+
if (e && heldRunHook?.(t)) return;
|
|
53
78
|
// Error arm (#2840), user effects only: a compute-phase error that is still
|
|
54
79
|
// the node's settled state at effect time runs the bundle's error handler in
|
|
55
80
|
// this same imperative, writable scope. Unwrap the StatusError used for
|
|
@@ -60,46 +85,49 @@ function runEffect(t) {
|
|
|
60
85
|
// Render effects bypass: their errors route to boundaries synchronously in
|
|
61
86
|
// notifyEffectStatus, and a runner queued by an earlier valueChanged in the
|
|
62
87
|
// same flush must not be hijacked by a later-arriving error status.
|
|
63
|
-
if (t.
|
|
64
|
-
const e = unwrapStatusError(t.
|
|
65
|
-
t.
|
|
66
|
-
t.
|
|
88
|
+
if (t.m & STATUS_ERROR && t.i === EFFECT_USER) {
|
|
89
|
+
const e = unwrapStatusError(t.T?.U);
|
|
90
|
+
t.ge = t.le;
|
|
91
|
+
t.Ae = false;
|
|
67
92
|
try {
|
|
68
|
-
t.
|
|
69
|
-
const e = t.
|
|
70
|
-
t.
|
|
93
|
+
t.Je ? t.Je(e, () => {
|
|
94
|
+
const e = t.ze;
|
|
95
|
+
t.ze = undefined;
|
|
71
96
|
e?.();
|
|
72
97
|
}) : console.error(e);
|
|
73
98
|
} catch (e) {
|
|
74
|
-
|
|
75
|
-
haltReactivity(e);
|
|
76
|
-
throw e;
|
|
77
|
-
}
|
|
99
|
+
escalate(t, e);
|
|
78
100
|
}
|
|
79
101
|
return;
|
|
80
102
|
}
|
|
81
|
-
|
|
82
|
-
|
|
103
|
+
// Captured before the callback: its own throw errors the node below, but
|
|
104
|
+
// the compute pass that produced `_value` was clean, so its tail still goes.
|
|
105
|
+
const E = t.T?.U == null;
|
|
106
|
+
const u = t.ze;
|
|
107
|
+
t.ze = undefined;
|
|
83
108
|
try {
|
|
84
|
-
|
|
85
|
-
const
|
|
86
|
-
if (false &&
|
|
109
|
+
u?.();
|
|
110
|
+
const e = t.qe(t.le, t.ge);
|
|
111
|
+
if (false && e !== undefined && typeof e !== "function") ;
|
|
87
112
|
// The final cleanup is invoked by disposeChildren at true disposal.
|
|
88
|
-
t.
|
|
113
|
+
t.ze = e;
|
|
89
114
|
} catch (e) {
|
|
90
|
-
ext(t).
|
|
91
|
-
t.
|
|
92
|
-
|
|
93
|
-
haltReactivity(e);
|
|
94
|
-
throw e;
|
|
95
|
-
}
|
|
115
|
+
ext(t).U = new StatusError(t, e);
|
|
116
|
+
t.m |= STATUS_ERROR;
|
|
117
|
+
escalate(t, e);
|
|
96
118
|
} finally {
|
|
97
|
-
t.
|
|
98
|
-
t.
|
|
119
|
+
t.ge = t.le;
|
|
120
|
+
t.Ae = false;
|
|
121
|
+
// The run applied: this is the frame now, so the dependency tail the
|
|
122
|
+
// compute pass left linked goes (A30, #3438 — `recompute` defers an
|
|
123
|
+
// effect's trim while a run is owed; the twin of `commitPendingNode`'s
|
|
124
|
+
// trim for a staged pass). An errored compute kept its full list with
|
|
125
|
+
// `_depsTail` marking where it stopped; leave it, as the commit does.
|
|
126
|
+
if (E) trimStaleDeps(t);
|
|
99
127
|
}
|
|
100
128
|
}
|
|
101
129
|
|
|
102
|
-
GlobalQueue.
|
|
130
|
+
GlobalQueue.q = runEffect;
|
|
103
131
|
|
|
104
132
|
/**
|
|
105
133
|
* Internal tracked effect - bypasses heap, goes directly to effect queue.
|
|
@@ -107,39 +135,44 @@ GlobalQueue.tt = runEffect;
|
|
|
107
135
|
* Uses stale reads.
|
|
108
136
|
*/ function trackedEffect(t, e) {
|
|
109
137
|
const run = () => {
|
|
110
|
-
|
|
138
|
+
// `_modified` is NOT redundant with the heap: the heap dedups within a
|
|
139
|
+
// pass, but several passes in one flush each enqueue `_run` into the same
|
|
140
|
+
// user queue, and this gate is what collapses them into one run.
|
|
141
|
+
if (!E.Ae || E.R & REACTIVE_DISPOSED) return;
|
|
142
|
+
if (heldRunHook?.(E)) return;
|
|
111
143
|
try {
|
|
112
|
-
E.
|
|
144
|
+
E.Ae = false;
|
|
113
145
|
recompute(E);
|
|
114
146
|
} finally {}
|
|
115
147
|
};
|
|
116
148
|
const E = computed(() => {
|
|
117
|
-
const e = E.
|
|
118
|
-
E.
|
|
149
|
+
const e = E.ze;
|
|
150
|
+
E.ze = undefined;
|
|
119
151
|
e?.();
|
|
120
|
-
const
|
|
121
|
-
E.
|
|
152
|
+
const u = staleValues(t);
|
|
153
|
+
E.ze = u;
|
|
122
154
|
}, {
|
|
123
155
|
...e,
|
|
124
156
|
lazy: true
|
|
125
157
|
});
|
|
126
|
-
E.
|
|
127
|
-
E.
|
|
128
|
-
E.
|
|
129
|
-
E.
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
};
|
|
141
|
-
E.Ut = run;
|
|
142
|
-
E.C.enqueue(EFFECT_USER, run);
|
|
158
|
+
E.ze = undefined;
|
|
159
|
+
E.l = E.l & ~CONFIG_AUTO_DISPOSE | CONFIG_CHILDREN_FORBIDDEN;
|
|
160
|
+
E.Ae = true;
|
|
161
|
+
E.i = EFFECT_TRACKED;
|
|
162
|
+
// Status dispatch rides the SHARED notifier (statusNotifierOf keys off
|
|
163
|
+
// _type): its error arm is behavior-identical to the closure that used to
|
|
164
|
+
// live here, without the per-node NodeExtension allocation.
|
|
165
|
+
E.Z = run;
|
|
166
|
+
// The first run rides the heap like every wake (GlobalQueue._update), so a
|
|
167
|
+
// tracked effect created inside a render-effect callback runs after that
|
|
168
|
+
// pass's staged writes commit, not before.
|
|
169
|
+
enqueueSub(E);
|
|
170
|
+
schedule();
|
|
143
171
|
}
|
|
144
172
|
|
|
173
|
+
// Install the shared effect status notifier (statusNotifierOf serves it to
|
|
174
|
+
// every effect node) — module-scope: any bundle that creates effects
|
|
175
|
+
// evaluates this module.
|
|
176
|
+
setEffectStatusNotify(notifyEffectStatus);
|
|
177
|
+
|
|
145
178
|
export { effect, trackedEffect };
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { ROOT_ERROR_HOOK } from "./scheduler.js";
|
|
2
|
+
|
|
3
|
+
let ambientHook;
|
|
4
|
+
|
|
5
|
+
const reported = new WeakSet;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Registers the ambient client error hook — the one call a browser `init()`
|
|
9
|
+
* makes to see every failure a boundary renders a fallback for, in
|
|
10
|
+
* production. (Uncaught errors reach `reportError` / `window.onerror`.)
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* configureClientErrors({
|
|
14
|
+
* onError: (error, { ownerPath }) =>
|
|
15
|
+
* Sentry.captureException(error, {
|
|
16
|
+
* mechanism: { type: "solid.error_boundary", handled: true }
|
|
17
|
+
* })
|
|
18
|
+
* });
|
|
19
|
+
* ```
|
|
20
|
+
*/ function configureClientErrors(e) {
|
|
21
|
+
if (e && e.onError !== undefined && typeof e.onError !== "function") {
|
|
22
|
+
throw new TypeError(`Invalid onError: expected a function, received ${typeof e.onError}.`);
|
|
23
|
+
}
|
|
24
|
+
ambientHook = e ? e.onError : undefined;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** The nearest root's hook above `owner` (parked under `ROOT_ERROR_HOOK`), else the ambient one. */ function hookFor(e) {
|
|
28
|
+
for (let n = e; n; n = n._parent) {
|
|
29
|
+
const e = n[ROOT_ERROR_HOOK];
|
|
30
|
+
if (e !== undefined) return e;
|
|
31
|
+
}
|
|
32
|
+
return ambientHook;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Component labels up the owner chain, root first — `_name` where the runtime keeps it. */ function labels(e) {
|
|
36
|
+
const n = [];
|
|
37
|
+
for (let o = e; o; o = o._parent) {
|
|
38
|
+
const e = o._name;
|
|
39
|
+
if (typeof e === "string" && e.length) n.push(e);
|
|
40
|
+
}
|
|
41
|
+
return n.length ? n.reverse() : undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Tells the client error hook about `error`, caught by the boundary whose
|
|
46
|
+
* owner is `owner`, thrown by `thrower` (the computation the engine's status
|
|
47
|
+
* wrapper named; unknown for a value that never crossed one) — once per
|
|
48
|
+
* error object. A throwing hook is reported on the console and otherwise
|
|
49
|
+
* ignored — a monitor must never take the app down.
|
|
50
|
+
* @internal
|
|
51
|
+
*/ function reportClientError(e, n, o) {
|
|
52
|
+
const r = e !== null && (typeof e === "object" || typeof e === "function");
|
|
53
|
+
if (r) {
|
|
54
|
+
if (reported.has(e)) return;
|
|
55
|
+
reported.add(e);
|
|
56
|
+
}
|
|
57
|
+
const t = hookFor(n);
|
|
58
|
+
if (t === undefined) return;
|
|
59
|
+
const i = {};
|
|
60
|
+
const f = labels(n);
|
|
61
|
+
const c = labels(o) ?? f;
|
|
62
|
+
if (c !== undefined) i.ownerPath = c;
|
|
63
|
+
if (f !== undefined) i.boundaryPath = f;
|
|
64
|
+
try {
|
|
65
|
+
t(e, i);
|
|
66
|
+
} catch (e) {
|
|
67
|
+
console.error(e);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export { configureClientErrors, reportClientError };
|
package/dist/prod/core/error.js
CHANGED
|
@@ -23,7 +23,6 @@
|
|
|
23
23
|
* ```
|
|
24
24
|
*/
|
|
25
25
|
class NotReadyError extends Error {
|
|
26
|
-
source;
|
|
27
26
|
constructor(r) {
|
|
28
27
|
// Control-flow throw: it happens on every read of a pending source, so in
|
|
29
28
|
// production skip V8's eager stack capture (proportional to stack depth —
|
|
@@ -40,9 +39,12 @@ class NotReadyError extends Error {
|
|
|
40
39
|
}
|
|
41
40
|
|
|
42
41
|
class StatusError extends Error {
|
|
43
|
-
source;
|
|
44
42
|
constructor(r, o) {
|
|
45
|
-
|
|
43
|
+
// Best-effort message: formatting must never replace the original (#4112).
|
|
44
|
+
try {
|
|
45
|
+
var t = String(o instanceof Error ? o.message : o);
|
|
46
|
+
} catch {}
|
|
47
|
+
super(t, {
|
|
46
48
|
cause: o
|
|
47
49
|
});
|
|
48
50
|
this.source = r;
|
|
@@ -53,6 +55,18 @@ class StatusError extends Error {
|
|
|
53
55
|
return r instanceof StatusError ? r.cause : r;
|
|
54
56
|
}
|
|
55
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Rejection value of `until(fn, { timeout })` when the predicate does not turn
|
|
60
|
+
* truthy within the window. Inside an `action()`, the rejection is thrown back
|
|
61
|
+
* in at the `yield` point — catchable there, or the action fails and its
|
|
62
|
+
* optimistic state reverts.
|
|
63
|
+
*/ class TimeoutError extends Error {
|
|
64
|
+
constructor(r = "Timed out waiting for condition") {
|
|
65
|
+
super(r);
|
|
66
|
+
this.name = "TimeoutError";
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
56
70
|
class NoOwnerError extends Error {
|
|
57
71
|
constructor() {
|
|
58
72
|
super("");
|
|
@@ -65,4 +79,4 @@ class ContextNotFoundError extends Error {
|
|
|
65
79
|
}
|
|
66
80
|
}
|
|
67
81
|
|
|
68
|
-
export { ContextNotFoundError, NoOwnerError, NotReadyError, StatusError, unwrapStatusError };
|
|
82
|
+
export { ContextNotFoundError, NoOwnerError, NotReadyError, StatusError, TimeoutError, unwrapStatusError };
|
|
@@ -2,7 +2,7 @@ import { signal, setSignal, read } from "./core.js";
|
|
|
2
2
|
|
|
3
3
|
import { cleanup } from "./owner.js";
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { setExternalHooks } from "./scheduler.js";
|
|
6
6
|
|
|
7
7
|
let externalSourceConfig = null;
|
|
8
8
|
|
|
@@ -37,7 +37,7 @@ let externalSourceConfig = null;
|
|
|
37
37
|
* ```
|
|
38
38
|
*/
|
|
39
39
|
// Wires a freshly created computed through the active external-source bridge.
|
|
40
|
-
// Lives here (installed
|
|
40
|
+
// Lives here (installed as a scheduler hook while a config is active) rather than
|
|
41
41
|
// inline in core: esbuild cannot literal-track the mutable config binding the
|
|
42
42
|
// way rollup does, so an inline `if (externalSourceConfig)` block ships in
|
|
43
43
|
// every bundle even though only enableExternalSource() can make it reachable.
|
|
@@ -46,11 +46,11 @@ function wireExternalSource(e) {
|
|
|
46
46
|
equals: false,
|
|
47
47
|
ownedWrite: true
|
|
48
48
|
});
|
|
49
|
-
const r = externalSourceConfig.factory(e.
|
|
49
|
+
const r = externalSourceConfig.factory(e.X, () => {
|
|
50
50
|
setSignal(n, undefined);
|
|
51
51
|
});
|
|
52
52
|
cleanup(() => r.dispose());
|
|
53
|
-
e.
|
|
53
|
+
e.X = e => {
|
|
54
54
|
read(n);
|
|
55
55
|
return r.track(e);
|
|
56
56
|
};
|
|
@@ -61,11 +61,10 @@ function externalUntrack(e) {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
// The hooks mirror the config's liveness exactly (installed on enable,
|
|
64
|
-
// removed on reset) so core's
|
|
64
|
+
// removed on reset) so core's presence checks stay equivalent to the old
|
|
65
65
|
// `externalSourceConfig` truthiness checks.
|
|
66
66
|
function syncExternalHooks() {
|
|
67
|
-
|
|
68
|
-
GlobalQueue.Gt = externalSourceConfig ? externalUntrack : null;
|
|
67
|
+
if (externalSourceConfig) setExternalHooks(wireExternalSource, externalUntrack); else setExternalHooks(undefined, undefined);
|
|
69
68
|
}
|
|
70
69
|
|
|
71
70
|
function enableExternalSource(e) {
|