@solidjs/signals 2.0.0-rc.7 → 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 +6357 -0
- package/dist/dev.attribution.js +2485 -0
- package/dist/dev.js +2851 -7027
- package/dist/observe/affects.js +129 -0
- package/dist/observe/attribution.js +7 -0
- package/dist/observe/boundaries.js +641 -0
- package/dist/observe/core/action.js +177 -0
- package/dist/observe/core/async.js +846 -0
- 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 +82 -0
- package/dist/observe/core/attribution-queries.js +28 -0
- package/dist/observe/core/attribution.js +2095 -0
- package/dist/observe/core/constants.js +248 -0
- package/dist/{types-cjs/core/context.d.cts → observe/core/context.js} +37 -12
- package/dist/observe/core/core.js +1789 -0
- package/dist/observe/core/dev.js +293 -0
- package/dist/observe/core/effect.js +195 -0
- package/dist/observe/core/error-hooks.js +71 -0
- package/dist/observe/core/error.js +80 -0
- package/dist/observe/core/external.js +98 -0
- package/dist/observe/core/graph.js +155 -0
- package/dist/observe/core/heap.js +147 -0
- package/dist/observe/core/invariants.js +45 -0
- package/dist/observe/core/lanes.js +205 -0
- package/dist/observe/core/optimistic.js +585 -0
- package/dist/observe/core/owner.js +322 -0
- package/dist/observe/core/scheduler.js +1428 -0
- package/dist/observe/core/verdict.js +669 -0
- package/dist/observe/index.js +59 -0
- package/dist/observe/map.js +484 -0
- package/dist/{types-cjs/signals.d.cts → observe/signals.js} +311 -317
- package/dist/observe/store/index.js +40 -0
- package/dist/observe/store/next/optimistic.js +660 -0
- package/dist/observe/store/next/projection.js +255 -0
- package/dist/observe/store/next/reconcile.js +375 -0
- package/dist/observe/store/next/store.js +2327 -0
- package/dist/observe/store/next/target.js +51 -0
- package/dist/observe/store/store.js +325 -0
- package/dist/observe/store/storePath.js +103 -0
- package/dist/observe/store/utils.js +1023 -0
- package/dist/prod/attribution.js +41 -0
- package/dist/prod/boundaries.js +128 -76
- package/dist/prod/core/action.js +65 -37
- package/dist/prod/core/async.js +324 -172
- package/dist/prod/core/constants.js +57 -1
- package/dist/prod/core/context.js +3 -3
- package/dist/prod/core/core.js +1003 -364
- package/dist/prod/core/dev.js +17 -1
- package/dist/prod/core/effect.js +76 -42
- 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 +61 -56
- package/dist/prod/core/lanes.js +107 -63
- package/dist/prod/core/optimistic.js +374 -93
- package/dist/prod/core/owner.js +62 -46
- package/dist/prod/core/scheduler.js +653 -286
- package/dist/prod/core/verdict.js +245 -127
- package/dist/prod/index.js +15 -5
- package/dist/prod/map.js +292 -141
- package/dist/prod/signals.js +29 -16
- package/dist/prod/store/next/optimistic.js +77 -55
- package/dist/prod/store/next/projection.js +31 -20
- package/dist/prod/store/next/reconcile.js +87 -84
- package/dist/prod/store/next/store.js +718 -401
- package/dist/prod/store/next/target.js +32 -10
- package/dist/prod/store/store.js +12 -14
- package/dist/prod/store/utils.js +950 -130
- package/dist/types/attribution.d.ts +20 -0
- package/dist/types/attribution.prod.d.ts +22 -0
- package/dist/types/boundaries.d.ts +10 -1
- package/dist/types/core/action.d.ts +12 -5
- package/dist/types/core/async.d.ts +1 -1
- 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 +147 -9
- package/dist/types/core/attribution-queries.d.ts +10 -0
- package/dist/types/core/attribution.d.ts +216 -182
- package/dist/types/core/constants.d.ts +56 -0
- package/dist/types/core/core.d.ts +190 -2
- package/dist/types/core/dev.d.ts +246 -28
- package/dist/types/core/effect.d.ts +8 -1
- package/dist/types/core/error-hooks.d.ts +71 -0
- package/dist/types/core/index.d.ts +4 -1
- package/dist/types/core/invariants.d.ts +4 -0
- package/dist/types/core/lanes.d.ts +39 -5
- package/dist/types/core/scheduler.d.ts +153 -0
- package/dist/types/core/types.d.ts +32 -9
- package/dist/types/index.d.ts +10 -3
- package/dist/types/map.d.ts +2 -0
- package/dist/types/signals.d.ts +8 -0
- package/dist/types/store/index.d.ts +2 -0
- package/dist/types/store/next/optimistic.d.ts +1 -1
- package/dist/types/store/next/store.d.ts +11 -5
- package/dist/types/store/next/target.d.ts +37 -10
- package/dist/types/store/utils.d.ts +178 -0
- package/package.json +21 -17
- package/dist/node.cjs +0 -10541
- package/dist/node.dev.cjs +0 -13724
- 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 -23
- package/dist/types-cjs/core/attribution-hooks.d.cts +0 -127
- package/dist/types-cjs/core/attribution.d.cts +0 -495
- package/dist/types-cjs/core/constants.d.cts +0 -159
- package/dist/types-cjs/core/core.d.cts +0 -185
- package/dist/types-cjs/core/dev.d.cts +0 -136
- package/dist/types-cjs/core/effect.d.cts +0 -30
- package/dist/types-cjs/core/error.d.cts +0 -56
- package/dist/types-cjs/core/external.d.cts +0 -15
- package/dist/types-cjs/core/graph.d.cts +0 -28
- package/dist/types-cjs/core/heap.d.cts +0 -24
- 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 -54
- 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 -236
- package/dist/types-cjs/core/types.d.cts +0 -204
- 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/store/index.d.cts +0 -18
- package/dist/types-cjs/store/next/optimistic.d.cts +0 -25
- package/dist/types-cjs/store/next/projection.d.cts +0 -8
- package/dist/types-cjs/store/next/reconcile.d.cts +0 -10
- package/dist/types-cjs/store/next/store.d.cts +0 -121
- package/dist/types-cjs/store/next/target.d.cts +0 -166
- package/dist/types-cjs/store/store.d.cts +0 -143
- package/dist/types-cjs/store/storePath.d.cts +0 -58
- package/dist/types-cjs/store/utils.d.cts +0 -74
|
@@ -1,6 +1,7 @@
|
|
|
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
|
+
* `@solidjs/signals/attribution`.
|
|
4
5
|
*
|
|
5
6
|
* The runtime already knows the full dependency set of every scope; this
|
|
6
7
|
* module surfaces it. Every value commit stamps its node with a ChangeRecord
|
|
@@ -15,11 +16,13 @@ import type { Computed } from "./types.js";
|
|
|
15
16
|
*
|
|
16
17
|
* This module is the attribution ENGINE: all semantics live here, and it is
|
|
17
18
|
* decoupled from the core. `enable()` installs it into the core's narrow
|
|
18
|
-
*
|
|
19
|
-
* call those hooks with true facts. Disabled cost is one null check per
|
|
20
|
-
* site; prod builds fold the sites out entirely.
|
|
21
|
-
*
|
|
22
|
-
*
|
|
19
|
+
* observe-tier hook points (attribution-hooks.ts); core's only obligation is
|
|
20
|
+
* to call those hooks with true facts. Disabled cost is one null check per
|
|
21
|
+
* hook site; prod builds fold the sites out entirely. Nothing in the core
|
|
22
|
+
* imports this module — it is reachable only through the package's
|
|
23
|
+
* `./attribution` entry, so an observe build that never imports it never
|
|
24
|
+
* ships it. The same hook surface is the intended substrate for external
|
|
25
|
+
* consumers (devtools) — one mechanism, two front-ends.
|
|
23
26
|
*/
|
|
24
27
|
export type ChangeKind = "write" | "derived" | "async" | "refresh";
|
|
25
28
|
/**
|
|
@@ -36,30 +39,32 @@ export type ChangeKind = "write" | "derived" | "async" | "refresh";
|
|
|
36
39
|
* name, when it has one). Writes after an `await` (not a `yield`) run in a
|
|
37
40
|
* bare microtask and stamp `external` — the documented escape.
|
|
38
41
|
* - `async` — an async landing (`name` = the node whose flight landed).
|
|
42
|
+
* - `navigation` — a router's navigation, declared via `withOrigin` around
|
|
43
|
+
* the location write (`name` = the matched route pattern `/users/:id`;
|
|
44
|
+
* `to`/`from` the concrete paths; `params` what the pattern bound; `at`
|
|
45
|
+
* when it was requested). The router-agnostic seam: any router that wraps
|
|
46
|
+
* its write gets navigations named by route in every hold, re-run and
|
|
47
|
+
* verdict, with no per-router knowledge anywhere in the engine.
|
|
39
48
|
* - `external` — none of the above: timers, sockets, promise callbacks, setup.
|
|
40
49
|
*
|
|
41
50
|
* `interaction` on a non-interaction frame is the user event the frame runs
|
|
42
51
|
* under — an action started by a click, an effect whose run was caused by a
|
|
43
|
-
* click's write, a landing whose flight a click started
|
|
44
|
-
* every downstream cost be keyed by the
|
|
52
|
+
* click's write, a landing whose flight a click started, a navigation a link
|
|
53
|
+
* click performed. It is what lets every downstream cost be keyed by the
|
|
54
|
+
* interaction that paid for it.
|
|
45
55
|
*/
|
|
46
56
|
export interface ChangeOrigin {
|
|
47
|
-
kind: "interaction" | "effect" | "action" | "async" | "external";
|
|
57
|
+
kind: "interaction" | "effect" | "action" | "async" | "navigation" | "external";
|
|
48
58
|
name?: string;
|
|
49
59
|
target?: string;
|
|
50
60
|
at?: number;
|
|
51
61
|
interaction?: ChangeOrigin;
|
|
52
62
|
/** `effect` only: the `RerunEvent.run` of the compute run this callback belongs to. */
|
|
53
63
|
run?: number;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
type: string;
|
|
59
|
-
/** The element hit, e.g. `button#next "Next →"`. */
|
|
60
|
-
target?: string;
|
|
61
|
-
/** Dispatch time on the `performance.now()` clock; defaults to now. */
|
|
62
|
-
at?: number;
|
|
64
|
+
/** `navigation` only: concrete destination and departure paths, and the bound params. */
|
|
65
|
+
to?: string;
|
|
66
|
+
from?: string;
|
|
67
|
+
params?: Readonly<Record<string, string | undefined>>;
|
|
63
68
|
}
|
|
64
69
|
export interface ChangeRecord {
|
|
65
70
|
/** Global monotonic change sequence — orders causes across the app. */
|
|
@@ -81,11 +86,22 @@ export interface ChangeRecord {
|
|
|
81
86
|
export interface RerunEvent {
|
|
82
87
|
/** Global monotonic run sequence. */
|
|
83
88
|
run: number;
|
|
89
|
+
/** When the run started (`performance.now()` clock). */
|
|
90
|
+
at: number;
|
|
84
91
|
/** How many times this node has re-run since attribution was enabled. */
|
|
85
92
|
nodeRuns: number;
|
|
86
93
|
nodeKind: "effect" | "memo";
|
|
87
94
|
nodeName: string;
|
|
88
|
-
|
|
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;
|
|
89
105
|
/**
|
|
90
106
|
* The deps that changed since this node's previous run. Empty means the
|
|
91
107
|
* re-run was not triggered by a tracked value change (creation-adjacent
|
|
@@ -178,14 +194,11 @@ export interface AttributionOptions {
|
|
|
178
194
|
/**
|
|
179
195
|
* Written-fan-out warning: emit a diagnostic when a committed root
|
|
180
196
|
* invalidation (write, refresh, async landing) reaches a node with at
|
|
181
|
-
* least this many subscribers (default 250).
|
|
182
|
-
* HUGE_FAN_OUT graph-size warning,
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
* anything when the node actually changes. Once per node, re-warning only
|
|
187
|
-
* on 2x subscriber growth, so the two never spam the same node. `false`
|
|
188
|
-
* disables.
|
|
197
|
+
* least this many subscribers (default 250). The lower-bar, opt-in sibling
|
|
198
|
+
* of the always-on HUGE_FAN_OUT graph-size warning, which fires on the
|
|
199
|
+
* same kind of write from GRAPH_SIZE_WARN_AT (2000) up; this one hands
|
|
200
|
+
* over to it there, so a write never carries both. Once per node,
|
|
201
|
+
* re-warning only on 2x subscriber growth. `false` disables.
|
|
189
202
|
*/
|
|
190
203
|
wideWrites?: number | false;
|
|
191
204
|
/**
|
|
@@ -243,57 +256,71 @@ export interface AttributionOptions {
|
|
|
243
256
|
warnMs: number;
|
|
244
257
|
} | false;
|
|
245
258
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
wastedMs: number;
|
|
258
|
-
/** Self-time spent in optimistic/transition (overlay) runs. */
|
|
259
|
-
overlayMs: number;
|
|
260
|
-
}
|
|
261
|
-
export interface WriteCost {
|
|
262
|
-
/** Root cause name (a signal write, async landing, or refresh target). */
|
|
263
|
-
name: string;
|
|
264
|
-
/** Number of downstream re-runs this root triggered. */
|
|
265
|
-
runs: number;
|
|
266
|
-
/** Summed self-time of every downstream re-run it caused. */
|
|
267
|
-
downstreamMs: number;
|
|
259
|
+
/**
|
|
260
|
+
* The records the engine delivers, by `subscribe(type, …)` name. Every one is
|
|
261
|
+
* delivered synchronously at the moment it is complete — a re-run when its
|
|
262
|
+
* recompute ends, an interaction / hold / navigation when it settles — so a
|
|
263
|
+
* consumer never polls the ring buffers to learn that something finished.
|
|
264
|
+
*/
|
|
265
|
+
export interface AttributionRecords {
|
|
266
|
+
rerun: RerunEvent;
|
|
267
|
+
interaction: InteractionEvent;
|
|
268
|
+
hold: HoldEvent;
|
|
269
|
+
navigation: NavigationEvent;
|
|
268
270
|
}
|
|
271
|
+
export type AttributionRecordType = keyof AttributionRecords;
|
|
272
|
+
/** @internal The engine's clock: `performance.now()` where it exists. */
|
|
273
|
+
export declare const now: () => number;
|
|
269
274
|
/**
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
* and
|
|
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.
|
|
274
282
|
*/
|
|
275
|
-
export
|
|
276
|
-
|
|
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;
|
|
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;
|
|
298
|
+
/** `click on button#next "Next →"`, `effect "syncTitle"`, `action "save"`, `navigation to /users/:id`, … */
|
|
277
299
|
export declare function formatOrigin(origin: ChangeOrigin): string;
|
|
278
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
|
+
*/
|
|
279
311
|
export interface Attribution {
|
|
280
312
|
enable(opts?: AttributionOptions): void;
|
|
281
313
|
disable(): void;
|
|
282
|
-
subscribe(listener: (event: RerunEvent) => void): () => void;
|
|
283
|
-
history(): readonly RerunEvent[];
|
|
284
|
-
/** Re-run history for one node — pass a memo/effect accessor or raw node. */
|
|
285
|
-
why(target: unknown): RerunEvent[];
|
|
286
|
-
/** Current dependency names of one scope — the devtools subscription view. */
|
|
287
|
-
subscriptions(target: unknown): string[];
|
|
288
314
|
/**
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
*
|
|
315
|
+
* Deliver records as they complete — see `AttributionRecords`. The bare
|
|
316
|
+
* form is `subscribe("rerun", …)`. Records are the same objects the ring
|
|
317
|
+
* buffers hold (`history()`, `interactions()`, `holds()`, `navigations()`),
|
|
318
|
+
* delivered synchronously from the engine, so a listener must not write
|
|
319
|
+
* signals. All subscriptions are dropped by `disable()`.
|
|
292
320
|
*/
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
};
|
|
321
|
+
subscribe(listener: (event: RerunEvent) => void): () => void;
|
|
322
|
+
subscribe<K extends AttributionRecordType>(type: K, listener: (record: AttributionRecords[K]) => void): () => void;
|
|
323
|
+
history(): readonly RerunEvent[];
|
|
297
324
|
/**
|
|
298
325
|
* Every graph-provable sequential flight chain observed since enable()
|
|
299
326
|
* (ring-buffered like history()). Facts, not verdicts: chains are recorded
|
|
@@ -309,18 +336,25 @@ export interface Attribution {
|
|
|
309
336
|
*/
|
|
310
337
|
holds(): readonly HoldEvent[];
|
|
311
338
|
/**
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
* counts each async source's flights and how many were abandoned before
|
|
320
|
-
* landing (the re-ask storm), and `fallbacks` measures how long each
|
|
321
|
-
* loading boundary showed its fallback and how often that was a flash.
|
|
339
|
+
* Every navigation a router declared via `withOrigin` since enable()
|
|
340
|
+
* (ring-buffered like history()), settled or not: what route, under which
|
|
341
|
+
* interaction, how many writes, and — once its writes are through — how
|
|
342
|
+
* long that took and how (`committed` in a plain drain, `held` behind
|
|
343
|
+
* route data with the HoldEvent attached, or `superseded` by a later
|
|
344
|
+
* navigation before it landed). Facts for any consumer that wants
|
|
345
|
+
* navigation spans named by route: no router integration needed.
|
|
322
346
|
*/
|
|
323
|
-
|
|
347
|
+
navigations(): readonly NavigationEvent[];
|
|
348
|
+
/**
|
|
349
|
+
* Every user interaction a runtime declared via `withInteraction` since
|
|
350
|
+
* enable() (ring-buffered like history()), settled or not: what was
|
|
351
|
+
* dispatched, when, what it wrote, the re-runs and creations it caused,
|
|
352
|
+
* the holds its writes waited in and the navigations it performed — and,
|
|
353
|
+
* once all of that is through, how long the person waited (`settledMs`)
|
|
354
|
+
* and how it ended (`idle` / `committed` / `held`). The per-dispatch
|
|
355
|
+
* record `feedback().interactions` folds by name.
|
|
356
|
+
*/
|
|
357
|
+
interactions(): readonly InteractionEvent[];
|
|
324
358
|
/**
|
|
325
359
|
* Cooperative preload declaration: stamp a flight object (promise or async
|
|
326
360
|
* iterable) with its true kickoff time BEFORE the reactive graph sees it.
|
|
@@ -330,18 +364,9 @@ export interface Attribution {
|
|
|
330
364
|
* is then judged against the real start — work already in the air when its
|
|
331
365
|
* upstream landed is parallel, never a waterfall link. Callable while
|
|
332
366
|
* attribution is disabled (marks made at navigation time must survive a
|
|
333
|
-
* later enable()).
|
|
367
|
+
* later enable()).
|
|
334
368
|
*/
|
|
335
369
|
markFlight(flight: object, startedAt?: number): void;
|
|
336
|
-
/**
|
|
337
|
-
* Run `fn` as a user interaction's handler: root writes inside stamp it as
|
|
338
|
-
* their origin, and actions/effects/flights it causes carry it. The web
|
|
339
|
-
* runtime wraps every event dispatch in this; custom renderers and test
|
|
340
|
-
* harnesses call it themselves. Callable while attribution is disabled.
|
|
341
|
-
*/
|
|
342
|
-
withInteraction: typeof withInteraction;
|
|
343
|
-
format: typeof formatRerun;
|
|
344
|
-
formatOrigin: typeof formatOrigin;
|
|
345
370
|
}
|
|
346
371
|
/** One landed flight: its node name, wall duration, and upstream chain. */
|
|
347
372
|
export interface FlightLink {
|
|
@@ -362,10 +387,13 @@ export interface HeldWrite {
|
|
|
362
387
|
}
|
|
363
388
|
export interface HoldEvent {
|
|
364
389
|
/**
|
|
365
|
-
*
|
|
366
|
-
* writes when one is known (`interaction.at`) or the
|
|
367
|
-
* parked them, whichever is earlier
|
|
390
|
+
* When the wait began (`performance.now()` clock): the interaction that
|
|
391
|
+
* performed the held writes when one is known (`interaction.at`) or the
|
|
392
|
+
* first flush that parked them, whichever is earlier. `at + holdMs` is the
|
|
393
|
+
* commit.
|
|
368
394
|
*/
|
|
395
|
+
at: number;
|
|
396
|
+
/** Wall time the user waited: `at` to the commit. */
|
|
369
397
|
holdMs: number;
|
|
370
398
|
/**
|
|
371
399
|
* The quiescent tail: from the LAST held write to join (the user's final
|
|
@@ -375,6 +403,13 @@ export interface HoldEvent {
|
|
|
375
403
|
tailMs: number;
|
|
376
404
|
/** The user interaction whose writes were held, when the stamp is known. */
|
|
377
405
|
interaction?: ChangeOrigin;
|
|
406
|
+
/**
|
|
407
|
+
* The declared unit of work the held writes belong to — the `navigation`
|
|
408
|
+
* a router described via `withOrigin` — when one is known. What names the
|
|
409
|
+
* hold by route (`navigation to /users/:id`) rather than by signal; the
|
|
410
|
+
* same object as `navigations()[].origin`, so the two join by identity.
|
|
411
|
+
*/
|
|
412
|
+
origin?: ChangeOrigin;
|
|
378
413
|
/** Flushes that ended with the hold still open. */
|
|
379
414
|
flushes: number;
|
|
380
415
|
/** Root signal writes staged behind the hold (the user's unanswered input). */
|
|
@@ -382,11 +417,12 @@ export interface HoldEvent {
|
|
|
382
417
|
/** Async nodes the hold waited on (union across its parked flushes). */
|
|
383
418
|
blockers: string[];
|
|
384
419
|
/**
|
|
385
|
-
* Feedback the graph provably rendered for this hold
|
|
386
|
-
*
|
|
387
|
-
*
|
|
420
|
+
* Feedback the graph provably rendered for this hold — an `isPending()`
|
|
421
|
+
* reader, a `latest()` shadow, an optimistic value, an `affects()` mark —
|
|
422
|
+
* one entry per affordance, in the order found. Empty and
|
|
423
|
+
* `paintedDuringHold === 0` is the SILENT_HOLD signature.
|
|
388
424
|
*/
|
|
389
|
-
|
|
425
|
+
acknowledgements: Acknowledgement[];
|
|
390
426
|
/**
|
|
391
427
|
* Effect callbacks that ran inside the hold's parked flushes. Mainline
|
|
392
428
|
* effects are stashed while a hold is open, so these are lane effects —
|
|
@@ -398,98 +434,96 @@ export interface HoldEvent {
|
|
|
398
434
|
/** The hold was opened (or joined) by an `action()`. */
|
|
399
435
|
action: boolean;
|
|
400
436
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
437
|
+
/**
|
|
438
|
+
* One way the screen acknowledged a hold. `reader` is where it was painted —
|
|
439
|
+
* the owner path of the first effect the census found reading the
|
|
440
|
+
* affordance (`["<App>", "<Feed>", "effect"]`), so a consumer can say WHICH
|
|
441
|
+
* screen answered, not only that one did. Absent when the affordance was
|
|
442
|
+
* registered but the census found no reader through the graph (an optimistic
|
|
443
|
+
* store: its readers are proxy traps, not nodes). `feedback()` ranks
|
|
444
|
+
* acknowledgements by `kind:source` (`isPending:posts`).
|
|
445
|
+
*/
|
|
446
|
+
export interface Acknowledgement {
|
|
447
|
+
kind: "isPending" | "latest" | "optimistic" | "affects";
|
|
448
|
+
/** The node the affordance hangs on — the async source for `isPending`/`latest`, the optimistic/affected node otherwise. */
|
|
449
|
+
source: string;
|
|
450
|
+
reader?: string[];
|
|
451
|
+
}
|
|
452
|
+
/** `isLongHold` — the tail outlasted `longHolds.infoMs`. */
|
|
453
|
+
/** @internal */
|
|
454
|
+
export declare function isLongHold(event: HoldEvent): boolean;
|
|
455
|
+
/** A destination a navigation abandoned when a redirect sent it elsewhere. */
|
|
456
|
+
export interface NavigationHop {
|
|
457
|
+
name?: string;
|
|
458
|
+
to?: string;
|
|
459
|
+
params?: Readonly<Record<string, string | undefined>>;
|
|
460
|
+
/** When the redirect away from it was declared (`performance.now()` clock). */
|
|
461
|
+
at: number;
|
|
462
|
+
}
|
|
463
|
+
export interface NavigationEvent {
|
|
464
|
+
/** The matched route pattern the router gave — `/users/:id`. After a redirect, the final one. */
|
|
465
|
+
name?: string;
|
|
466
|
+
to?: string;
|
|
467
|
+
from?: string;
|
|
468
|
+
params?: Readonly<Record<string, string | undefined>>;
|
|
469
|
+
/** When the navigation was requested (`performance.now()` clock). */
|
|
470
|
+
at: number;
|
|
471
|
+
/** The user interaction it ran under, when known — a link click. */
|
|
472
|
+
interaction?: ChangeOrigin;
|
|
473
|
+
/** Root writes the frame performed, redirect hops included. */
|
|
474
|
+
writes: number;
|
|
475
|
+
/** Destinations abandoned along the way, in order — present only when a redirect occurred. */
|
|
476
|
+
redirects?: NavigationHop[];
|
|
477
|
+
/**
|
|
478
|
+
* Wall time from the request to settle: the end of the drain that committed
|
|
479
|
+
* its writes, or the commit of the hold they waited in. `undefined` while
|
|
480
|
+
* unsettled.
|
|
481
|
+
*/
|
|
482
|
+
settledMs?: number;
|
|
483
|
+
outcome?: "committed" | "held" | "superseded";
|
|
484
|
+
/** The hold its writes waited in, when hold tracking recorded one. */
|
|
485
|
+
hold?: HoldEvent;
|
|
413
486
|
/**
|
|
414
|
-
*
|
|
415
|
-
* `
|
|
416
|
-
* table level. The affordance is not the whole answer there: a fallback
|
|
417
|
-
* (`Loading` keyed with `on`), a preload, a cache, or a faster source is.
|
|
418
|
-
* `longMs` sums the tails.
|
|
487
|
+
* The frame object its writes were stamped with — `ChangeRecord.origin` on
|
|
488
|
+
* each, `HoldEvent.origin` on the hold. Join key, by identity.
|
|
419
489
|
*/
|
|
420
|
-
|
|
421
|
-
longMs: number;
|
|
422
|
-
/** Which affordances answered, and in how many holds — ranked. */
|
|
423
|
-
acknowledgedBy: {
|
|
424
|
-
by: string;
|
|
425
|
-
holds: number;
|
|
426
|
-
}[];
|
|
427
|
-
/** Interactions whose writes were held here, ranked by holds. */
|
|
428
|
-
interactions: {
|
|
429
|
-
interaction: string;
|
|
430
|
-
holds: number;
|
|
431
|
-
}[];
|
|
432
|
-
/** Distinct root writes that were held. */
|
|
433
|
-
writes: string[];
|
|
434
|
-
/** Holds an action opened or joined. */
|
|
435
|
-
actions: number;
|
|
490
|
+
origin: ChangeOrigin;
|
|
436
491
|
}
|
|
437
|
-
export interface
|
|
438
|
-
/**
|
|
439
|
-
|
|
440
|
-
/**
|
|
441
|
-
|
|
442
|
-
/**
|
|
492
|
+
export interface InteractionEvent {
|
|
493
|
+
/** Event type — `click`, `keydown`, `input`… */
|
|
494
|
+
name: string;
|
|
495
|
+
/** The element hit, as the runtime described it — `button#next "Next →"`. */
|
|
496
|
+
target?: string;
|
|
497
|
+
/** Dispatch time (`performance.now()` clock). */
|
|
498
|
+
at: number;
|
|
499
|
+
/** Wall time of the handler itself, dispatch to return. */
|
|
500
|
+
handlerMs: number;
|
|
501
|
+
/** Root writes attributed to the frame: the handler's, and those of frames it opened (a navigation). */
|
|
502
|
+
writes: number;
|
|
503
|
+
/** Re-runs traced back to this interaction while the record was open. */
|
|
443
504
|
runs: number;
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
}
|
|
453
|
-
/** Per async source: how many flights it started, and how many it threw away. */
|
|
454
|
-
export interface FlightStats {
|
|
455
|
-
source: string;
|
|
456
|
-
/** Flights registered (a recompute that produced a new promise/iterable). */
|
|
457
|
-
flights: number;
|
|
458
|
-
/** Flights that landed (whether or not the value changed). */
|
|
459
|
-
landed: number;
|
|
505
|
+
/** Computations created in those runs or in the frame's flushes (the "create 1,000 rows" work). */
|
|
506
|
+
created: number;
|
|
507
|
+
/** Summed self-time of `runs` and `created` (ms). Quantized per run; `settledMs` is the wall clock. */
|
|
508
|
+
runMs: number;
|
|
509
|
+
/** Holds its writes waited in, in settle order. */
|
|
510
|
+
holds: HoldEvent[];
|
|
511
|
+
/** Navigations performed under it, in open order. */
|
|
512
|
+
navigations: NavigationEvent[];
|
|
460
513
|
/**
|
|
461
|
-
*
|
|
462
|
-
*
|
|
463
|
-
*
|
|
514
|
+
* Dispatch to settle: the handler's return when it wrote nothing, the end of
|
|
515
|
+
* the drain that committed its writes, or the commit of the last hold they
|
|
516
|
+
* waited in — whichever came last. `undefined` while unsettled.
|
|
464
517
|
*/
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
landedMs: number;
|
|
468
|
-
worstMs: number;
|
|
469
|
-
}
|
|
470
|
-
/** Per loading boundary: how long, and how briefly, it showed its fallback. */
|
|
471
|
-
export interface FallbackStats {
|
|
472
|
-
/** The boundary's owner path (`<App> › <Feed>`), or `boundary` when unnamed. */
|
|
473
|
-
boundary: string;
|
|
474
|
-
/** Times the fallback was shown. */
|
|
475
|
-
shows: number;
|
|
476
|
-
/** Summed and worst fallback duration (ms) across completed shows. */
|
|
477
|
-
shownMs: number;
|
|
478
|
-
worstMs: number;
|
|
518
|
+
settledMs?: number;
|
|
519
|
+
outcome?: "idle" | "committed" | "held";
|
|
479
520
|
/**
|
|
480
|
-
*
|
|
481
|
-
*
|
|
482
|
-
*
|
|
483
|
-
* fetch above the boundary removes the flash.
|
|
521
|
+
* The frame object every downstream fact carries — `ChangeOrigin.interaction`
|
|
522
|
+
* on writes and frames, `RerunEvent.interaction`, `HoldEvent.interaction`,
|
|
523
|
+
* `NavigationEvent.interaction`. Join key, by identity.
|
|
484
524
|
*/
|
|
485
|
-
|
|
486
|
-
}
|
|
487
|
-
export interface AttributionFeedbackTables {
|
|
488
|
-
sources: FeedbackSource[];
|
|
489
|
-
interactions: FeedbackInteraction[];
|
|
490
|
-
/** Async sources ranked by abandoned flights, then by flights. */
|
|
491
|
-
flights: FlightStats[];
|
|
492
|
-
/** Loading boundaries ranked by flashes, then by time shown. */
|
|
493
|
-
fallbacks: FallbackStats[];
|
|
525
|
+
origin: ChangeOrigin;
|
|
494
526
|
}
|
|
527
|
+
/** @internal No affordance answered and nothing painted while held. */
|
|
528
|
+
export declare function isSilentHold(event: HoldEvent): boolean;
|
|
495
529
|
export declare const attribution: Attribution;
|
|
@@ -113,6 +113,59 @@ export declare const CONFIG_HELD_TRUTH: number;
|
|
|
113
113
|
* unobserved closure, NodeExtension) were the measured create-floor bytes
|
|
114
114
|
* (warm dbmon profile: store node machinery ~36% + GC ~29%). */
|
|
115
115
|
export declare const CONFIG_SLOT_NODE: number;
|
|
116
|
+
/** Optimistic node whose own source arrived with a value DIFFERENT from its
|
|
117
|
+
* active override (A18 supersession, #3331). The override survives only as
|
|
118
|
+
* the displayed value — untracked reads and the applied frame keep it until
|
|
119
|
+
* the owning transaction commits — while the graph has already moved to the
|
|
120
|
+
* staged truth in `_pendingValue`: tracked readers see it and the corrected
|
|
121
|
+
* cascade is that transaction's held work. Set by the two own-source write
|
|
122
|
+
* paths (asyncWrite, transition-held recompute); cleared by a fresh optimistic
|
|
123
|
+
* write (a new override re-masks) and by the revert. */
|
|
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;
|
|
137
|
+
/** In-flight async node whose inputs were PUBLISHED while it was pending: a
|
|
138
|
+
* batch or transaction committed with the node still `STATUS_PENDING` (an
|
|
139
|
+
* unobserved flight, #3305), so the inputs are on screen and the node's
|
|
140
|
+
* committed `_value` is stale against them. Governs read()'s reveal
|
|
141
|
+
* carve-out: a stale (render) reader in some OTHER transaction may show a
|
|
142
|
+
* foreign-held pending node's committed value — parallel transactions, no
|
|
143
|
+
* entanglement — only while that value is coherent with the visible frame,
|
|
144
|
+
* i.e. while the flight's inputs are themselves held (unpublished) and not
|
|
145
|
+
* lane-revealed. Set by `commitPendingNodes`; cleared when the node next
|
|
146
|
+
* enters pending fresh (a new flight from a settled state). */
|
|
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;
|
|
116
169
|
export declare const STATUS_NONE = 0;
|
|
117
170
|
export declare const STATUS_PENDING: number;
|
|
118
171
|
export declare const STATUS_ERROR: number;
|
|
@@ -121,6 +174,9 @@ export declare const EFFECT_PURE = 0;
|
|
|
121
174
|
export declare const EFFECT_RENDER = 1;
|
|
122
175
|
export declare const EFFECT_USER = 2;
|
|
123
176
|
export declare const EFFECT_TRACKED = 3;
|
|
177
|
+
/** OR-ed into the `type` a lane passes to its effect runners: lane runs
|
|
178
|
+
* apply ahead of their transaction and are exempt from ownership parking. */
|
|
179
|
+
export declare const LANE_RUN = 4;
|
|
124
180
|
export declare const NOT_PENDING: {};
|
|
125
181
|
export declare const NO_SNAPSHOT: {};
|
|
126
182
|
/**
|