@solidjs/signals 2.0.0-rc.8 → 2.0.0-rc.9
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 +1399 -285
- package/dist/dev.attribution.js +459 -307
- package/dist/dev.js +1864 -435
- package/dist/observe/affects.js +3 -1
- package/dist/observe/attribution.js +7 -1
- package/dist/observe/boundaries.js +209 -154
- package/dist/observe/core/action.js +18 -8
- package/dist/observe/core/async.js +220 -110
- package/dist/observe/core/attribution-costs.js +66 -0
- package/dist/observe/core/attribution-feedback.js +282 -0
- package/dist/observe/core/attribution-hooks.js +23 -1
- package/dist/observe/core/attribution-queries.js +28 -0
- package/dist/observe/core/attribution.js +262 -485
- package/dist/observe/core/constants.js +34 -1
- package/dist/observe/core/context.js +3 -3
- package/dist/observe/core/core.js +867 -367
- package/dist/observe/core/dev.js +78 -17
- package/dist/observe/core/effect.js +67 -51
- package/dist/observe/core/error-hooks.js +71 -0
- package/dist/observe/core/external.js +4 -4
- package/dist/observe/core/graph.js +37 -37
- package/dist/observe/core/heap.js +45 -45
- package/dist/observe/core/invariants.js +2 -0
- package/dist/observe/core/lanes.js +86 -49
- package/dist/observe/core/optimistic.js +242 -95
- package/dist/observe/core/owner.js +98 -84
- package/dist/observe/core/scheduler.js +543 -305
- package/dist/observe/core/verdict.js +247 -129
- package/dist/observe/index.js +7 -3
- package/dist/observe/map.js +126 -124
- package/dist/observe/signals.js +33 -17
- package/dist/observe/store/index.js +2 -0
- package/dist/observe/store/next/optimistic.js +141 -132
- package/dist/observe/store/next/projection.js +34 -21
- package/dist/observe/store/next/reconcile.js +58 -56
- package/dist/observe/store/next/store.js +260 -146
- package/dist/observe/store/store.js +5 -3
- package/dist/observe/store/utils.js +948 -135
- package/dist/prod/attribution.js +26 -17
- package/dist/prod/boundaries.js +128 -76
- package/dist/prod/core/action.js +16 -8
- package/dist/prod/core/async.js +251 -143
- package/dist/prod/core/constants.js +34 -1
- package/dist/prod/core/context.js +3 -3
- package/dist/prod/core/core.js +843 -347
- package/dist/prod/core/dev.js +17 -1
- package/dist/prod/core/effect.js +48 -34
- package/dist/prod/core/error-hooks.js +71 -0
- package/dist/prod/core/external.js +4 -4
- package/dist/prod/core/graph.js +37 -37
- package/dist/prod/core/heap.js +45 -45
- package/dist/prod/core/lanes.js +90 -53
- package/dist/prod/core/optimistic.js +247 -100
- package/dist/prod/core/owner.js +57 -45
- package/dist/prod/core/scheduler.js +543 -305
- package/dist/prod/core/verdict.js +245 -127
- package/dist/prod/index.js +7 -3
- package/dist/prod/map.js +112 -112
- package/dist/prod/signals.js +28 -12
- package/dist/prod/store/next/optimistic.js +135 -128
- package/dist/prod/store/next/projection.js +31 -20
- package/dist/prod/store/next/store.js +325 -252
- package/dist/prod/store/store.js +2 -2
- package/dist/prod/store/utils.js +946 -135
- package/dist/types/attribution.d.ts +7 -2
- package/dist/types/attribution.prod.d.ts +10 -1
- package/dist/types/boundaries.d.ts +10 -1
- package/dist/types/core/action.d.ts +12 -5
- 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 +28 -3
- package/dist/types/core/attribution-queries.d.ts +10 -0
- package/dist/types/core/attribution.d.ts +51 -172
- package/dist/types/core/constants.d.ts +33 -0
- package/dist/types/core/core.d.ts +186 -5
- package/dist/types/core/dev.d.ts +129 -9
- package/dist/types/core/error-hooks.d.ts +71 -0
- package/dist/types/core/index.d.ts +3 -1
- package/dist/types/core/invariants.d.ts +4 -0
- package/dist/types/core/lanes.d.ts +31 -4
- package/dist/types/core/scheduler.d.ts +95 -2
- package/dist/types/core/types.d.ts +3 -0
- package/dist/types/index.d.ts +2 -2
- package/dist/types/signals.d.ts +8 -0
- package/dist/types/store/index.d.ts +2 -1
- package/dist/types/store/next/optimistic.d.ts +1 -1
- package/dist/types/store/next/store.d.ts +6 -5
- package/dist/types/store/next/target.d.ts +1 -1
- package/dist/types/store/utils.d.ts +177 -6
- package/package.json +1 -1
|
@@ -10,6 +10,11 @@
|
|
|
10
10
|
* `attribution.prod.ts`, an inert engine with the same surface, so app code
|
|
11
11
|
* can import it unconditionally.
|
|
12
12
|
*/
|
|
13
|
-
export { attribution } from "./core/attribution.js";
|
|
14
|
-
export
|
|
13
|
+
export { attribution, formatOrigin, formatRerun } from "./core/attribution.js";
|
|
14
|
+
export { costs } from "./core/attribution-costs.js";
|
|
15
|
+
export { feedback } from "./core/attribution-feedback.js";
|
|
16
|
+
export { subscriptions, why } from "./core/attribution-queries.js";
|
|
17
|
+
export type { Acknowledgement, Attribution, AttributionOptions, AttributionRecords, AttributionRecordType, ChangeKind, ChangeOrigin, ChangeRecord, FlightLink, HeldWrite, HoldEvent, InteractionEvent, NavigationEvent, NavigationHop, RerunEvent, WaterfallRecord } from "./core/attribution.js";
|
|
18
|
+
export type { AttributionCostTables, ScopeCost, WriteCost } from "./core/attribution-costs.js";
|
|
19
|
+
export type { AttributionFeedbackTables, FallbackStats, FeedbackInteraction, FeedbackNavigation, FeedbackSource, FlightStats } from "./core/attribution-feedback.js";
|
|
15
20
|
export type { InteractionRef, NavigationRef, OriginRef } from "./core/attribution-hooks.js";
|
|
@@ -8,6 +8,15 @@
|
|
|
8
8
|
* real engine's interface so the two cannot drift.
|
|
9
9
|
*/
|
|
10
10
|
import type { Attribution } from "./core/attribution.js";
|
|
11
|
+
import type * as Engine from "./attribution.js";
|
|
11
12
|
export declare const attribution: Attribution;
|
|
12
|
-
export
|
|
13
|
+
export declare const costs: typeof Engine.costs;
|
|
14
|
+
export declare const feedback: typeof Engine.feedback;
|
|
15
|
+
export declare const why: typeof Engine.why;
|
|
16
|
+
export declare const subscriptions: typeof Engine.subscriptions;
|
|
17
|
+
export declare const formatRerun: typeof Engine.formatRerun;
|
|
18
|
+
export declare const formatOrigin: typeof Engine.formatOrigin;
|
|
19
|
+
export type { Acknowledgement, Attribution, AttributionOptions, AttributionRecords, AttributionRecordType, ChangeKind, ChangeOrigin, ChangeRecord, FlightLink, HeldWrite, HoldEvent, InteractionEvent, NavigationEvent, NavigationHop, RerunEvent, WaterfallRecord } from "./core/attribution.js";
|
|
20
|
+
export type { AttributionCostTables, ScopeCost, WriteCost } from "./core/attribution-costs.js";
|
|
21
|
+
export type { AttributionFeedbackTables, FallbackStats, FeedbackInteraction, FeedbackNavigation, FeedbackSource, FlightStats } from "./core/attribution-feedback.js";
|
|
13
22
|
export type { InteractionRef, NavigationRef, OriginRef } from "./core/attribution-hooks.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Queue, type Computed, type Effect } from "./core/index.js";
|
|
1
|
+
import { Queue, type Computed, type Effect, type Owner } from "./core/index.js";
|
|
2
2
|
import type { Signal } from "./core/index.js";
|
|
3
3
|
import { type Accessor } from "./signals.js";
|
|
4
4
|
export interface BoundaryComputed<T> extends Computed<T> {
|
|
@@ -45,9 +45,18 @@ export declare class CollectionQueue extends Queue {
|
|
|
45
45
|
_initialized: boolean;
|
|
46
46
|
_onFn: (() => any) | undefined;
|
|
47
47
|
_prevOn: any;
|
|
48
|
+
/** The boundary's owner — where a `caught` report locates itself, set before the children are built (a creation-time throw arrives before `_tree`). */
|
|
49
|
+
_owner?: Owner;
|
|
48
50
|
constructor(type: number);
|
|
49
51
|
run(type: number): void;
|
|
52
|
+
/** The `on` key, or ON_INIT when it throws. Evaluated mid-propagation,
|
|
53
|
+
* inside the pending node's own pass: read as a spectator so the key's
|
|
54
|
+
* sources never become that node's dependencies (#3528). */
|
|
55
|
+
_readOn(): any;
|
|
50
56
|
notify(node: Effect<any>, type: number, flags: number, error?: any): boolean;
|
|
57
|
+
/** Is `reporter` live and routed to this boundary — under it, with no
|
|
58
|
+
* collecting pending-type boundary in between (`reporterBlocksSource`'s test)? */
|
|
59
|
+
_holds(reporter: Computed<any>): boolean;
|
|
51
60
|
_checkSources(): void;
|
|
52
61
|
}
|
|
53
62
|
/**
|
|
@@ -23,15 +23,22 @@
|
|
|
23
23
|
* `yield` is the transaction-safe suspension point: the action waits for a
|
|
24
24
|
* yielded promise and re-enters the transaction before running the code after
|
|
25
25
|
* it. A plain `await` does NOT — the runtime has no hook into an async
|
|
26
|
-
* generator's internal await continuations, so
|
|
27
|
-
*
|
|
28
|
-
* immediately
|
|
29
|
-
*
|
|
26
|
+
* generator's internal await continuations, so code between an `await` and
|
|
27
|
+
* the next `yield` runs OUTSIDE the transaction: writes to fresh signals
|
|
28
|
+
* commit immediately, and anything that creates a reader there — `until()`,
|
|
29
|
+
* `latest()`, a memo or effect, a mount — is created mainline, where a read of
|
|
30
|
+
* this action's held state makes it born held (A29): staged with the
|
|
31
|
+
* transaction and replayed at its commit. For `until()` that commit is the
|
|
32
|
+
* settle its own promise holds open (#3482). `await` is still the ergonomic
|
|
33
|
+
* choice for typed results; just put a bare `yield` before any write or
|
|
34
|
+
* reader creation that follows it — including the expression of the next
|
|
35
|
+
* `yield`, which is evaluated before the step re-enters:
|
|
30
36
|
*
|
|
31
37
|
* ```ts
|
|
32
38
|
* const saved = await api.createTodo(text); // typed result
|
|
33
|
-
* yield; // re-enter the transaction before writing
|
|
39
|
+
* yield; // re-enter the transaction before writing or reading
|
|
34
40
|
* setTodos(t => { ... });
|
|
41
|
+
* yield until(() => todos.some(t => t.id === saved.id));
|
|
35
42
|
* ```
|
|
36
43
|
*
|
|
37
44
|
* (For the same reason, don't call `flush()` inside an action body — it
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export interface ScopeCost {
|
|
2
|
+
name: string;
|
|
3
|
+
kind: "effect" | "memo";
|
|
4
|
+
runs: number;
|
|
5
|
+
selfMs: number;
|
|
6
|
+
/**
|
|
7
|
+
* Self-time of PLAIN, non-held runs that produced an unchanged value —
|
|
8
|
+
* the recoverable number. Overlay runs (optimistic/transition) are never
|
|
9
|
+
* counted here: an optimistic recompute landing back on the committed
|
|
10
|
+
* value is the mechanism working, not waste.
|
|
11
|
+
*/
|
|
12
|
+
wastedMs: number;
|
|
13
|
+
/** Self-time spent in optimistic/transition (overlay) runs. */
|
|
14
|
+
overlayMs: number;
|
|
15
|
+
}
|
|
16
|
+
export interface WriteCost {
|
|
17
|
+
/** Root cause name (a signal write, async landing, or refresh target). */
|
|
18
|
+
name: string;
|
|
19
|
+
/** Number of downstream re-runs this root triggered. */
|
|
20
|
+
runs: number;
|
|
21
|
+
/** Summed self-time of every downstream re-run it caused. */
|
|
22
|
+
downstreamMs: number;
|
|
23
|
+
}
|
|
24
|
+
export interface AttributionCostTables {
|
|
25
|
+
/** Ranked by self-time. */
|
|
26
|
+
scopes: ScopeCost[];
|
|
27
|
+
/** Ranked by the total downstream re-run time each root write caused. */
|
|
28
|
+
writes: WriteCost[];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Aggregated cost tables since `enable()`: `scopes` ranked by self-time
|
|
32
|
+
* (with `wastedMs` = time spent on unchanged-value runs), `writes` ranked by
|
|
33
|
+
* total downstream re-run time each root write caused.
|
|
34
|
+
*/
|
|
35
|
+
export declare function costs(): AttributionCostTables;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
export interface FeedbackSource {
|
|
2
|
+
/** The async nodes the holds waited on; empty when an action alone kept them open. */
|
|
3
|
+
sources: string[];
|
|
4
|
+
holds: number;
|
|
5
|
+
/** Summed wait across the holds (ms). */
|
|
6
|
+
heldMs: number;
|
|
7
|
+
worstMs: number;
|
|
8
|
+
/** Holds with no acknowledgment at all — the SILENT_HOLD signature, at any duration. */
|
|
9
|
+
silent: number;
|
|
10
|
+
silentMs: number;
|
|
11
|
+
/** Holds whose only acknowledgment was a `latest()` shadow: the input showed, nothing said "loading". */
|
|
12
|
+
latestOnly: number;
|
|
13
|
+
/**
|
|
14
|
+
* Holds whose quiescent tail (last write to join → commit) reached
|
|
15
|
+
* `longHolds.infoMs`, acknowledged or not — the LONG_HOLD signature at the
|
|
16
|
+
* table level. The affordance is not the whole answer there: a fallback
|
|
17
|
+
* (`Loading` keyed with `on`), a preload, a cache, or a faster source is.
|
|
18
|
+
* `longMs` sums the tails.
|
|
19
|
+
*/
|
|
20
|
+
long: number;
|
|
21
|
+
longMs: number;
|
|
22
|
+
/** Which affordances answered, and in how many holds — ranked. */
|
|
23
|
+
acknowledgedBy: {
|
|
24
|
+
by: string;
|
|
25
|
+
holds: number;
|
|
26
|
+
}[];
|
|
27
|
+
/** Interactions whose writes were held here, ranked by holds. */
|
|
28
|
+
interactions: {
|
|
29
|
+
interaction: string;
|
|
30
|
+
holds: number;
|
|
31
|
+
}[];
|
|
32
|
+
/** Distinct root writes that were held. */
|
|
33
|
+
writes: string[];
|
|
34
|
+
/** Holds an action opened or joined. */
|
|
35
|
+
actions: number;
|
|
36
|
+
}
|
|
37
|
+
export interface FeedbackInteraction {
|
|
38
|
+
/** `click on button#next "Next →"` — type and target; repeated dispatches fold together. */
|
|
39
|
+
interaction: string;
|
|
40
|
+
/** Distinct dispatches seen (by dispatch time). */
|
|
41
|
+
dispatches: number;
|
|
42
|
+
/** Re-runs traced back to this interaction, and their summed self-time. */
|
|
43
|
+
runs: number;
|
|
44
|
+
selfMs: number;
|
|
45
|
+
/** The most re-run self-time a single dispatch caused — the long-flush hazard. */
|
|
46
|
+
worstDispatchMs: number;
|
|
47
|
+
/** Holds this interaction's writes waited in — the silent-hold hazard. */
|
|
48
|
+
holds: number;
|
|
49
|
+
heldMs: number;
|
|
50
|
+
silentMs: number;
|
|
51
|
+
worstHoldMs: number;
|
|
52
|
+
}
|
|
53
|
+
/** Per async source: how many flights it started, and how many it threw away. */
|
|
54
|
+
export interface FlightStats {
|
|
55
|
+
source: string;
|
|
56
|
+
/** Flights registered (a recompute that produced a new promise/iterable). */
|
|
57
|
+
flights: number;
|
|
58
|
+
/** Flights that landed (whether or not the value changed). */
|
|
59
|
+
landed: number;
|
|
60
|
+
/**
|
|
61
|
+
* Flights superseded by a newer one before landing — the search-as-you-type
|
|
62
|
+
* signature when large: every keystroke asked, most answers were discarded.
|
|
63
|
+
* A debounced/equality-gated derivation between input and fetch is the repair.
|
|
64
|
+
*/
|
|
65
|
+
abandoned: number;
|
|
66
|
+
/** Summed and worst wall time of landed flights (ms). */
|
|
67
|
+
landedMs: number;
|
|
68
|
+
worstMs: number;
|
|
69
|
+
}
|
|
70
|
+
/** Per loading boundary: how long, and how briefly, it showed its fallback. */
|
|
71
|
+
export interface FallbackStats {
|
|
72
|
+
/** The boundary's owner path (`<App> › <Feed>`), or `boundary` when unnamed. */
|
|
73
|
+
boundary: string;
|
|
74
|
+
/** Times the fallback was shown. */
|
|
75
|
+
shows: number;
|
|
76
|
+
/** Summed and worst fallback duration (ms) across completed shows. */
|
|
77
|
+
shownMs: number;
|
|
78
|
+
worstMs: number;
|
|
79
|
+
/**
|
|
80
|
+
* Shows shorter than the flash window (default 150ms): a spinner that
|
|
81
|
+
* appeared and vanished — the other end of the SILENT_HOLD spectrum, too
|
|
82
|
+
* much feedback for too little wait. A preload, a cache, or lifting the
|
|
83
|
+
* fetch above the boundary removes the flash.
|
|
84
|
+
*/
|
|
85
|
+
flashes: number;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Per route: what navigating to it cost, folded from settled
|
|
89
|
+
* `NavigationEvent`s — the route-level view a router integration used to
|
|
90
|
+
* have to build itself, from the runtime's own facts.
|
|
91
|
+
*/
|
|
92
|
+
export interface FeedbackNavigation {
|
|
93
|
+
/** The route pattern (`/users/:id`), or the concrete `to` when the router gave no pattern. */
|
|
94
|
+
name: string;
|
|
95
|
+
navigations: number;
|
|
96
|
+
/** Summed and worst request-to-settle time (ms) across settled navigations. */
|
|
97
|
+
settledMs: number;
|
|
98
|
+
worstMs: number;
|
|
99
|
+
/** Navigations whose writes waited in a hold, and the time they waited. */
|
|
100
|
+
held: number;
|
|
101
|
+
heldMs: number;
|
|
102
|
+
/** Held navigations the screen acknowledged nothing for — the SILENT_HOLD signature. */
|
|
103
|
+
silent: number;
|
|
104
|
+
/** Navigations overwritten by a later one before they landed. */
|
|
105
|
+
superseded: number;
|
|
106
|
+
/** Navigations a redirect sent elsewhere on the way (keyed by where they ended up). */
|
|
107
|
+
redirected: number;
|
|
108
|
+
}
|
|
109
|
+
export interface AttributionFeedbackTables {
|
|
110
|
+
sources: FeedbackSource[];
|
|
111
|
+
interactions: FeedbackInteraction[];
|
|
112
|
+
/** Routes ranked by the time spent held navigating to them, then by total settle time. */
|
|
113
|
+
navigations: FeedbackNavigation[];
|
|
114
|
+
/** Async sources ranked by abandoned flights, then by flights. */
|
|
115
|
+
flights: FlightStats[];
|
|
116
|
+
/** Loading boundaries ranked by flashes, then by time shown. */
|
|
117
|
+
fallbacks: FallbackStats[];
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* What the user waited on, folded from holds and the interaction on each
|
|
121
|
+
* re-run: `sources` ranks async sources by the silent time writes spent held
|
|
122
|
+
* behind them (with which affordances answered, how often, and which
|
|
123
|
+
* interactions were held); `interactions` ranks user events by the total time
|
|
124
|
+
* they cost — re-run work caused (long-flush hazard) beside time held
|
|
125
|
+
* (silent-hold hazard). Facts at every duration; SILENT_HOLD is the
|
|
126
|
+
* thresholded verdict. Three more tables round out the picture: `navigations`
|
|
127
|
+
* ranks routes by the time spent held navigating to them (folded from settled
|
|
128
|
+
* navigations), `flights` counts each async source's flights and how many
|
|
129
|
+
* were abandoned before landing (the re-ask storm), and `fallbacks` measures
|
|
130
|
+
* how long each loading boundary showed its fallback and how often that was a
|
|
131
|
+
* flash.
|
|
132
|
+
*/
|
|
133
|
+
export declare function feedback(): AttributionFeedbackTables;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import type { ChangeOrigin } from "./attribution.js";
|
|
1
2
|
import type { Transition } from "./scheduler.js";
|
|
2
|
-
import type { Computed, Signal } from "./types.js";
|
|
3
|
+
import type { Computed, Owner, Signal } from "./types.js";
|
|
3
4
|
/**
|
|
4
5
|
* Observe-tier hook points for the reactive core.
|
|
5
6
|
*
|
|
@@ -127,9 +128,12 @@ export interface AttributionHooks {
|
|
|
127
128
|
* `prevTotal` leaves in the old one. Containers above 64 leaves are not
|
|
128
129
|
* announced. Fired per written key from the write channel's notify. The
|
|
129
130
|
* engine decides whether the replacement was a spread-copy worth a
|
|
130
|
-
* diagnostic.
|
|
131
|
+
* diagnostic. `owner` is the owner the store root was created under
|
|
132
|
+
* (undefined when unrecorded): the finding is about the store, not about
|
|
133
|
+
* whoever wrote it, so an `OBSERVE.exclude`d panel's store stays silent
|
|
134
|
+
* however its writes arrive.
|
|
131
135
|
*/
|
|
132
|
-
storeReplaced(path: string, isArray: boolean, total: number, unchanged: number, prevTotal: number): void;
|
|
136
|
+
storeReplaced(path: string, isArray: boolean, total: number, unchanged: number, prevTotal: number, owner: Owner | null | undefined): void;
|
|
133
137
|
/**
|
|
134
138
|
* A `mapArray` update both disposed and created rows: `removed` are the
|
|
135
139
|
* items whose rows were disposed, `created` the items that got new rows,
|
|
@@ -147,6 +151,18 @@ export interface AttributionHooks {
|
|
|
147
151
|
* registers / last one clears), not per flush.
|
|
148
152
|
*/
|
|
149
153
|
boundaryFallback(boundary: object, tree: Computed<any> | undefined, shown: boolean): void;
|
|
154
|
+
/**
|
|
155
|
+
* The one query on the surface: the provenance a root write performed at
|
|
156
|
+
* this moment would be stamped with — the innermost open frame (an effect
|
|
157
|
+
* callback, an action step, a navigation), the interaction the handler
|
|
158
|
+
* runs under, or, inside a recompute, the origin of the change that caused
|
|
159
|
+
* it — or `undefined` when none applies (external). For a runtime that
|
|
160
|
+
* records a fact of its own beside the engine's records — `@solidjs/web`'s
|
|
161
|
+
* `"call"` record stamps the server-function call it is about to make —
|
|
162
|
+
* so the fact joins the engine's interaction and navigation records by the
|
|
163
|
+
* identity of the object returned, not by time.
|
|
164
|
+
*/
|
|
165
|
+
currentOrigin(): ChangeOrigin | undefined;
|
|
150
166
|
}
|
|
151
167
|
/** A user interaction, as a rendering runtime describes it to `withInteraction`. */
|
|
152
168
|
export interface InteractionRef {
|
|
@@ -238,3 +254,12 @@ export declare function withInteraction<T>(ref: InteractionRef, fn: () => T): T;
|
|
|
238
254
|
* ```
|
|
239
255
|
*/
|
|
240
256
|
export declare function withOrigin<T>(ref: OriginRef, fn: () => T): T;
|
|
257
|
+
/**
|
|
258
|
+
* The provenance a root write performed now would carry, as the installed
|
|
259
|
+
* engine sees it (`AttributionHooks.currentOrigin`); `undefined` with no
|
|
260
|
+
* engine, or when nothing is in effect. Reachable as
|
|
261
|
+
* `OBSERVE.attribution.currentOrigin` — how a runtime stamps a fact of its
|
|
262
|
+
* own (a server-function call) with the interaction or navigation it ran
|
|
263
|
+
* for, so an observer joins the two by identity.
|
|
264
|
+
*/
|
|
265
|
+
export declare function currentOrigin(): ChangeOrigin | undefined;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Point queries over the engine's live state — the devtools/console view of
|
|
3
|
+
* one scope. Their own module so a records-only consumer never ships them;
|
|
4
|
+
* they read the engine's ring buffer and the graph, and register nothing.
|
|
5
|
+
*/
|
|
6
|
+
import { type RerunEvent } from "./attribution.js";
|
|
7
|
+
/** Re-run history for one node — pass a memo/effect accessor or raw node. */
|
|
8
|
+
export declare function why(target: unknown): RerunEvent[];
|
|
9
|
+
/** Current dependency names of one scope — the devtools subscription view. */
|
|
10
|
+
export declare function subscriptions(target: unknown): string[];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Computed } from "./types.js";
|
|
1
|
+
import type { Computed, Signal } from "./types.js";
|
|
2
2
|
/**
|
|
3
3
|
* "Why did this run" attribution — the engine behind
|
|
4
4
|
* `@solidjs/signals/attribution`.
|
|
@@ -92,7 +92,16 @@ export interface RerunEvent {
|
|
|
92
92
|
nodeRuns: number;
|
|
93
93
|
nodeKind: "effect" | "memo";
|
|
94
94
|
nodeName: string;
|
|
95
|
-
|
|
95
|
+
/**
|
|
96
|
+
* Identity of the scope that ran, stable for the node's lifetime within
|
|
97
|
+
* the process: every run of one memo/effect carries the same `nodeId`, so
|
|
98
|
+
* runs join to a scope after the record has left the process (where
|
|
99
|
+
* `nodeName` alone would merge every unnamed `effect`). The engine's own
|
|
100
|
+
* per-node id, also what `ChangeOrigin.run` and the cycle/relay checks
|
|
101
|
+
* key on; not meaningful across processes or sessions. In-process
|
|
102
|
+
* consumers that want the live node ask `OBSERVE.subjectOf(event)`.
|
|
103
|
+
*/
|
|
104
|
+
nodeId: number;
|
|
96
105
|
/**
|
|
97
106
|
* The deps that changed since this node's previous run. Empty means the
|
|
98
107
|
* re-run was not triggered by a tracked value change (creation-adjacent
|
|
@@ -260,32 +269,45 @@ export interface AttributionRecords {
|
|
|
260
269
|
navigation: NavigationEvent;
|
|
261
270
|
}
|
|
262
271
|
export type AttributionRecordType = keyof AttributionRecords;
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
/** Summed self-time of every downstream re-run it caused. */
|
|
284
|
-
downstreamMs: number;
|
|
272
|
+
/** @internal The engine's clock: `performance.now()` where it exists. */
|
|
273
|
+
export declare const now: () => number;
|
|
274
|
+
/**
|
|
275
|
+
* @internal The fold tables' seam. The engine emits records; the tables that
|
|
276
|
+
* fold them — `costs()` (scopes and writes), `feedback()` (sources,
|
|
277
|
+
* interactions, navigations, flights, fallbacks) — live in their own modules
|
|
278
|
+
* and register here when imported, so a consumer that only subscribes to
|
|
279
|
+
* records never ships them. Each hook fires at the moment the engine has the
|
|
280
|
+
* fact; `reset` on `enable()`/`disable()`. With nothing registered every
|
|
281
|
+
* site is one empty loop.
|
|
282
|
+
*/
|
|
283
|
+
export interface FoldHooks {
|
|
284
|
+
rerun?(el: Computed<any>, event: RerunEvent): void;
|
|
285
|
+
hold?(event: HoldEvent): void;
|
|
286
|
+
navigation?(event: NavigationEvent): void;
|
|
287
|
+
/** A flight started; `abandoned` when it superseded one still in the air. */
|
|
288
|
+
flightStart?(el: Computed<any>, abandoned: boolean): void;
|
|
289
|
+
flightLanded?(el: Computed<any>, ms: number): void;
|
|
290
|
+
fallback?(boundary: object, tree: Computed<any> | undefined, shown: boolean): void;
|
|
291
|
+
reset?(): void;
|
|
285
292
|
}
|
|
293
|
+
/** @internal */
|
|
294
|
+
export declare function registerFold(hooks: FoldHooks): void;
|
|
295
|
+
/** @internal Root cause names of a cause chain — the writes/landings/refreshes the chain bottoms out in. */
|
|
296
|
+
export declare function rootsOf(causes: ChangeRecord[], out: Set<string>): void;
|
|
297
|
+
export declare function nodeName(node: Signal<any> | Computed<any>): string;
|
|
286
298
|
/** `click on button#next "Next →"`, `effect "syncTitle"`, `action "save"`, `navigation to /users/:id`, … */
|
|
287
299
|
export declare function formatOrigin(origin: ChangeOrigin): string;
|
|
288
300
|
export declare function formatRerun(event: RerunEvent): string;
|
|
301
|
+
/**
|
|
302
|
+
* The engine: turn it on, subscribe to its records, read its ring buffers.
|
|
303
|
+
* The folds over those records — `costs()`, `feedback()` — and the point
|
|
304
|
+
* queries — `why()`, `subscriptions()` — and the formatters —
|
|
305
|
+
* `formatRerun()`, `formatOrigin()` — are named exports of
|
|
306
|
+
* `@solidjs/signals/attribution` rather than methods here, so a consumer
|
|
307
|
+
* that only wants records (a production adapter) does not ship the tables a
|
|
308
|
+
* console or an agent reads; importing a fold is what turns its accounting
|
|
309
|
+
* on.
|
|
310
|
+
*/
|
|
289
311
|
export interface Attribution {
|
|
290
312
|
enable(opts?: AttributionOptions): void;
|
|
291
313
|
disable(): void;
|
|
@@ -299,19 +321,6 @@ export interface Attribution {
|
|
|
299
321
|
subscribe(listener: (event: RerunEvent) => void): () => void;
|
|
300
322
|
subscribe<K extends AttributionRecordType>(type: K, listener: (record: AttributionRecords[K]) => void): () => void;
|
|
301
323
|
history(): readonly RerunEvent[];
|
|
302
|
-
/** Re-run history for one node — pass a memo/effect accessor or raw node. */
|
|
303
|
-
why(target: unknown): RerunEvent[];
|
|
304
|
-
/** Current dependency names of one scope — the devtools subscription view. */
|
|
305
|
-
subscriptions(target: unknown): string[];
|
|
306
|
-
/**
|
|
307
|
-
* Aggregated cost tables since enable(): `scopes` ranked by self-time
|
|
308
|
-
* (with `wastedMs` = time spent on unchanged-value runs), `writes` ranked
|
|
309
|
-
* by total downstream re-run time each root write caused.
|
|
310
|
-
*/
|
|
311
|
-
costs(): {
|
|
312
|
-
scopes: ScopeCost[];
|
|
313
|
-
writes: WriteCost[];
|
|
314
|
-
};
|
|
315
324
|
/**
|
|
316
325
|
* Every graph-provable sequential flight chain observed since enable()
|
|
317
326
|
* (ring-buffered like history()). Facts, not verdicts: chains are recorded
|
|
@@ -346,21 +355,6 @@ export interface Attribution {
|
|
|
346
355
|
* record `feedback().interactions` folds by name.
|
|
347
356
|
*/
|
|
348
357
|
interactions(): readonly InteractionEvent[];
|
|
349
|
-
/**
|
|
350
|
-
* What the user waited on, folded from holds() and the interaction on each
|
|
351
|
-
* re-run: `sources` ranks async sources by the silent time writes spent
|
|
352
|
-
* held behind them (with which affordances answered, how often, and which
|
|
353
|
-
* interactions were held); `interactions` ranks user events by the total
|
|
354
|
-
* time they cost — re-run work caused (long-flush hazard) beside time held
|
|
355
|
-
* (silent-hold hazard). Facts at every duration; SILENT_HOLD is the
|
|
356
|
-
* thresholded verdict. Three more tables round out the picture:
|
|
357
|
-
* `navigations` ranks routes by the time spent held navigating to them
|
|
358
|
-
* (folded from navigations()), `flights` counts each async source's
|
|
359
|
-
* flights and how many were abandoned before landing (the re-ask storm),
|
|
360
|
-
* and `fallbacks` measures how long each loading boundary showed its
|
|
361
|
-
* fallback and how often that was a flash.
|
|
362
|
-
*/
|
|
363
|
-
feedback(): AttributionFeedbackTables;
|
|
364
358
|
/**
|
|
365
359
|
* Cooperative preload declaration: stamp a flight object (promise or async
|
|
366
360
|
* iterable) with its true kickoff time BEFORE the reactive graph sees it.
|
|
@@ -373,8 +367,6 @@ export interface Attribution {
|
|
|
373
367
|
* later enable()).
|
|
374
368
|
*/
|
|
375
369
|
markFlight(flight: object, startedAt?: number): void;
|
|
376
|
-
format: typeof formatRerun;
|
|
377
|
-
formatOrigin: typeof formatOrigin;
|
|
378
370
|
}
|
|
379
371
|
/** One landed flight: its node name, wall duration, and upstream chain. */
|
|
380
372
|
export interface FlightLink {
|
|
@@ -457,6 +449,9 @@ export interface Acknowledgement {
|
|
|
457
449
|
source: string;
|
|
458
450
|
reader?: string[];
|
|
459
451
|
}
|
|
452
|
+
/** `isLongHold` — the tail outlasted `longHolds.infoMs`. */
|
|
453
|
+
/** @internal */
|
|
454
|
+
export declare function isLongHold(event: HoldEvent): boolean;
|
|
460
455
|
/** A destination a navigation abandoned when a redirect sent it elsewhere. */
|
|
461
456
|
export interface NavigationHop {
|
|
462
457
|
name?: string;
|
|
@@ -529,122 +524,6 @@ export interface InteractionEvent {
|
|
|
529
524
|
*/
|
|
530
525
|
origin: ChangeOrigin;
|
|
531
526
|
}
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
sources: string[];
|
|
535
|
-
holds: number;
|
|
536
|
-
/** Summed wait across the holds (ms). */
|
|
537
|
-
heldMs: number;
|
|
538
|
-
worstMs: number;
|
|
539
|
-
/** Holds with no acknowledgment at all — the SILENT_HOLD signature, at any duration. */
|
|
540
|
-
silent: number;
|
|
541
|
-
silentMs: number;
|
|
542
|
-
/** Holds whose only acknowledgment was a `latest()` shadow: the input showed, nothing said "loading". */
|
|
543
|
-
latestOnly: number;
|
|
544
|
-
/**
|
|
545
|
-
* Holds whose quiescent tail (last write to join → commit) reached
|
|
546
|
-
* `longHolds.infoMs`, acknowledged or not — the LONG_HOLD signature at the
|
|
547
|
-
* table level. The affordance is not the whole answer there: a fallback
|
|
548
|
-
* (`Loading` keyed with `on`), a preload, a cache, or a faster source is.
|
|
549
|
-
* `longMs` sums the tails.
|
|
550
|
-
*/
|
|
551
|
-
long: number;
|
|
552
|
-
longMs: number;
|
|
553
|
-
/** Which affordances answered, and in how many holds — ranked. */
|
|
554
|
-
acknowledgedBy: {
|
|
555
|
-
by: string;
|
|
556
|
-
holds: number;
|
|
557
|
-
}[];
|
|
558
|
-
/** Interactions whose writes were held here, ranked by holds. */
|
|
559
|
-
interactions: {
|
|
560
|
-
interaction: string;
|
|
561
|
-
holds: number;
|
|
562
|
-
}[];
|
|
563
|
-
/** Distinct root writes that were held. */
|
|
564
|
-
writes: string[];
|
|
565
|
-
/** Holds an action opened or joined. */
|
|
566
|
-
actions: number;
|
|
567
|
-
}
|
|
568
|
-
export interface FeedbackInteraction {
|
|
569
|
-
/** `click on button#next "Next →"` — type and target; repeated dispatches fold together. */
|
|
570
|
-
interaction: string;
|
|
571
|
-
/** Distinct dispatches seen (by dispatch time). */
|
|
572
|
-
dispatches: number;
|
|
573
|
-
/** Re-runs traced back to this interaction, and their summed self-time. */
|
|
574
|
-
runs: number;
|
|
575
|
-
selfMs: number;
|
|
576
|
-
/** The most re-run self-time a single dispatch caused — the long-flush hazard. */
|
|
577
|
-
worstDispatchMs: number;
|
|
578
|
-
/** Holds this interaction's writes waited in — the silent-hold hazard. */
|
|
579
|
-
holds: number;
|
|
580
|
-
heldMs: number;
|
|
581
|
-
silentMs: number;
|
|
582
|
-
worstHoldMs: number;
|
|
583
|
-
}
|
|
584
|
-
/** Per async source: how many flights it started, and how many it threw away. */
|
|
585
|
-
export interface FlightStats {
|
|
586
|
-
source: string;
|
|
587
|
-
/** Flights registered (a recompute that produced a new promise/iterable). */
|
|
588
|
-
flights: number;
|
|
589
|
-
/** Flights that landed (whether or not the value changed). */
|
|
590
|
-
landed: number;
|
|
591
|
-
/**
|
|
592
|
-
* Flights superseded by a newer one before landing — the search-as-you-type
|
|
593
|
-
* signature when large: every keystroke asked, most answers were discarded.
|
|
594
|
-
* A debounced/equality-gated derivation between input and fetch is the repair.
|
|
595
|
-
*/
|
|
596
|
-
abandoned: number;
|
|
597
|
-
/** Summed and worst wall time of landed flights (ms). */
|
|
598
|
-
landedMs: number;
|
|
599
|
-
worstMs: number;
|
|
600
|
-
}
|
|
601
|
-
/** Per loading boundary: how long, and how briefly, it showed its fallback. */
|
|
602
|
-
export interface FallbackStats {
|
|
603
|
-
/** The boundary's owner path (`<App> › <Feed>`), or `boundary` when unnamed. */
|
|
604
|
-
boundary: string;
|
|
605
|
-
/** Times the fallback was shown. */
|
|
606
|
-
shows: number;
|
|
607
|
-
/** Summed and worst fallback duration (ms) across completed shows. */
|
|
608
|
-
shownMs: number;
|
|
609
|
-
worstMs: number;
|
|
610
|
-
/**
|
|
611
|
-
* Shows shorter than the flash window (default 150ms): a spinner that
|
|
612
|
-
* appeared and vanished — the other end of the SILENT_HOLD spectrum, too
|
|
613
|
-
* much feedback for too little wait. A preload, a cache, or lifting the
|
|
614
|
-
* fetch above the boundary removes the flash.
|
|
615
|
-
*/
|
|
616
|
-
flashes: number;
|
|
617
|
-
}
|
|
618
|
-
/**
|
|
619
|
-
* Per route: what navigating to it cost, folded from settled
|
|
620
|
-
* `NavigationEvent`s — the route-level view a router integration used to
|
|
621
|
-
* have to build itself, from the runtime's own facts.
|
|
622
|
-
*/
|
|
623
|
-
export interface FeedbackNavigation {
|
|
624
|
-
/** The route pattern (`/users/:id`), or the concrete `to` when the router gave no pattern. */
|
|
625
|
-
name: string;
|
|
626
|
-
navigations: number;
|
|
627
|
-
/** Summed and worst request-to-settle time (ms) across settled navigations. */
|
|
628
|
-
settledMs: number;
|
|
629
|
-
worstMs: number;
|
|
630
|
-
/** Navigations whose writes waited in a hold, and the time they waited. */
|
|
631
|
-
held: number;
|
|
632
|
-
heldMs: number;
|
|
633
|
-
/** Held navigations the screen acknowledged nothing for — the SILENT_HOLD signature. */
|
|
634
|
-
silent: number;
|
|
635
|
-
/** Navigations overwritten by a later one before they landed. */
|
|
636
|
-
superseded: number;
|
|
637
|
-
/** Navigations a redirect sent elsewhere on the way (keyed by where they ended up). */
|
|
638
|
-
redirected: number;
|
|
639
|
-
}
|
|
640
|
-
export interface AttributionFeedbackTables {
|
|
641
|
-
sources: FeedbackSource[];
|
|
642
|
-
interactions: FeedbackInteraction[];
|
|
643
|
-
/** Routes ranked by the time spent held navigating to them, then by total settle time. */
|
|
644
|
-
navigations: FeedbackNavigation[];
|
|
645
|
-
/** Async sources ranked by abandoned flights, then by flights. */
|
|
646
|
-
flights: FlightStats[];
|
|
647
|
-
/** Loading boundaries ranked by flashes, then by time shown. */
|
|
648
|
-
fallbacks: FallbackStats[];
|
|
649
|
-
}
|
|
527
|
+
/** @internal No affordance answered and nothing painted while held. */
|
|
528
|
+
export declare function isSilentHold(event: HoldEvent): boolean;
|
|
650
529
|
export declare const attribution: Attribution;
|
|
@@ -122,6 +122,18 @@ export declare const CONFIG_SLOT_NODE: number;
|
|
|
122
122
|
* paths (asyncWrite, transition-held recompute); cleared by a fresh optimistic
|
|
123
123
|
* write (a new override re-masks) and by the revert. */
|
|
124
124
|
export declare const CONFIG_OVERRIDE_SUPERSEDED: number;
|
|
125
|
+
/** HELD children (#3404): this node's `_firstChild` chain (and `_disposal`
|
|
126
|
+
* list) was built by a recompute whose result has not committed — a staged
|
|
127
|
+
* value, a pending window, or a run under a held transaction. A later
|
|
128
|
+
* recompute may tear those children down immediately: nothing observable
|
|
129
|
+
* was ever built on them. Unset, the children belong to the committed frame
|
|
130
|
+
* and a recompute defers them as zombies (`_pendingFirstChild`) until this
|
|
131
|
+
* node commits — regardless of whether the recompute runs under a
|
|
132
|
+
* transaction. A parked node (status propagation stamps `_transition`
|
|
133
|
+
* without recomputing) recomputed when its source lands otherwise disposed
|
|
134
|
+
* its committed children mid-hold, running their cleanups before the
|
|
135
|
+
* transaction's atomic reveal. Cleared by `commitPendingNode`. */
|
|
136
|
+
export declare const CONFIG_HELD_CHILDREN: number;
|
|
125
137
|
/** In-flight async node whose inputs were PUBLISHED while it was pending: a
|
|
126
138
|
* batch or transaction committed with the node still `STATUS_PENDING` (an
|
|
127
139
|
* unobserved flight, #3305), so the inputs are on screen and the node's
|
|
@@ -133,6 +145,27 @@ export declare const CONFIG_OVERRIDE_SUPERSEDED: number;
|
|
|
133
145
|
* lane-revealed. Set by `commitPendingNodes`; cleared when the node next
|
|
134
146
|
* enters pending fresh (a new flight from a settled state). */
|
|
135
147
|
export declare const CONFIG_INPUTS_PUBLISHED: number;
|
|
148
|
+
/** A28 (4): the node was written inside a recompute that ran OUTSIDE a flush
|
|
149
|
+
* (a creation-time compute — boundary machinery, a mapArray's first run). Such
|
|
150
|
+
* a write is promoted at that recompute's end: readers in the same block see
|
|
151
|
+
* it. Cleared when the next flush begins; set only on that rare path. */
|
|
152
|
+
export declare const CONFIG_PROMOTED: number;
|
|
153
|
+
/** A28 for same-tick adoption: the node was staged outside a flush and then
|
|
154
|
+
* adopted by a transaction (initTransition) before any flush carried the
|
|
155
|
+
* staging — the stamp says "held", but nothing flushed is staged for it, so
|
|
156
|
+
* on no channel is the write visible yet: `latest()` answers the committed
|
|
157
|
+
* value, the verdict sees nothing pending (as the store's leaves already did
|
|
158
|
+
* through their own selection). Cleared when the carrying flush re-stamps the
|
|
159
|
+
* transaction's pending nodes (reassignPendingTransition). */
|
|
160
|
+
export declare const CONFIG_ADOPTED_UNFLUSHED: number;
|
|
161
|
+
/** The node's active override is a DERIVED one: a lane pass published its
|
|
162
|
+
* speculative result into the override slot instead of `_value` (lanes
|
|
163
|
+
* stage — an optimistic derivation is an override, #3479). Its truth is not
|
|
164
|
+
* `_value` but a recompute from its inputs' truth, so the body-end
|
|
165
|
+
* supersession (`endOptimism`) and the authoritative-flight blockage
|
|
166
|
+
* (`transitionBlocked`) skip it; the revert drops the override and re-derives
|
|
167
|
+
* it (`resolveOptimisticNodes`). Cleared with the override. */
|
|
168
|
+
export declare const CONFIG_DERIVED_OVERRIDE: number;
|
|
136
169
|
export declare const STATUS_NONE = 0;
|
|
137
170
|
export declare const STATUS_PENDING: number;
|
|
138
171
|
export declare const STATUS_ERROR: number;
|