@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.
Files changed (134) hide show
  1. package/dist/dev-shared.js +6357 -0
  2. package/dist/dev.attribution.js +2485 -0
  3. package/dist/dev.js +2851 -7027
  4. package/dist/observe/affects.js +129 -0
  5. package/dist/observe/attribution.js +7 -0
  6. package/dist/observe/boundaries.js +641 -0
  7. package/dist/observe/core/action.js +177 -0
  8. package/dist/observe/core/async.js +846 -0
  9. package/dist/observe/core/attribution-costs.js +66 -0
  10. package/dist/observe/core/attribution-feedback.js +282 -0
  11. package/dist/observe/core/attribution-hooks.js +82 -0
  12. package/dist/observe/core/attribution-queries.js +28 -0
  13. package/dist/observe/core/attribution.js +2095 -0
  14. package/dist/observe/core/constants.js +248 -0
  15. package/dist/{types-cjs/core/context.d.cts → observe/core/context.js} +37 -12
  16. package/dist/observe/core/core.js +1789 -0
  17. package/dist/observe/core/dev.js +293 -0
  18. package/dist/observe/core/effect.js +195 -0
  19. package/dist/observe/core/error-hooks.js +71 -0
  20. package/dist/observe/core/error.js +80 -0
  21. package/dist/observe/core/external.js +98 -0
  22. package/dist/observe/core/graph.js +155 -0
  23. package/dist/observe/core/heap.js +147 -0
  24. package/dist/observe/core/invariants.js +45 -0
  25. package/dist/observe/core/lanes.js +205 -0
  26. package/dist/observe/core/optimistic.js +585 -0
  27. package/dist/observe/core/owner.js +322 -0
  28. package/dist/observe/core/scheduler.js +1428 -0
  29. package/dist/observe/core/verdict.js +669 -0
  30. package/dist/observe/index.js +59 -0
  31. package/dist/observe/map.js +484 -0
  32. package/dist/{types-cjs/signals.d.cts → observe/signals.js} +311 -317
  33. package/dist/observe/store/index.js +40 -0
  34. package/dist/observe/store/next/optimistic.js +660 -0
  35. package/dist/observe/store/next/projection.js +255 -0
  36. package/dist/observe/store/next/reconcile.js +375 -0
  37. package/dist/observe/store/next/store.js +2327 -0
  38. package/dist/observe/store/next/target.js +51 -0
  39. package/dist/observe/store/store.js +325 -0
  40. package/dist/observe/store/storePath.js +103 -0
  41. package/dist/observe/store/utils.js +1023 -0
  42. package/dist/prod/attribution.js +41 -0
  43. package/dist/prod/boundaries.js +128 -76
  44. package/dist/prod/core/action.js +65 -37
  45. package/dist/prod/core/async.js +324 -172
  46. package/dist/prod/core/constants.js +57 -1
  47. package/dist/prod/core/context.js +3 -3
  48. package/dist/prod/core/core.js +1003 -364
  49. package/dist/prod/core/dev.js +17 -1
  50. package/dist/prod/core/effect.js +76 -42
  51. package/dist/prod/core/error-hooks.js +71 -0
  52. package/dist/prod/core/external.js +4 -4
  53. package/dist/prod/core/graph.js +37 -37
  54. package/dist/prod/core/heap.js +61 -56
  55. package/dist/prod/core/lanes.js +107 -63
  56. package/dist/prod/core/optimistic.js +374 -93
  57. package/dist/prod/core/owner.js +62 -46
  58. package/dist/prod/core/scheduler.js +653 -286
  59. package/dist/prod/core/verdict.js +245 -127
  60. package/dist/prod/index.js +15 -5
  61. package/dist/prod/map.js +292 -141
  62. package/dist/prod/signals.js +29 -16
  63. package/dist/prod/store/next/optimistic.js +77 -55
  64. package/dist/prod/store/next/projection.js +31 -20
  65. package/dist/prod/store/next/reconcile.js +87 -84
  66. package/dist/prod/store/next/store.js +718 -401
  67. package/dist/prod/store/next/target.js +32 -10
  68. package/dist/prod/store/store.js +12 -14
  69. package/dist/prod/store/utils.js +950 -130
  70. package/dist/types/attribution.d.ts +20 -0
  71. package/dist/types/attribution.prod.d.ts +22 -0
  72. package/dist/types/boundaries.d.ts +10 -1
  73. package/dist/types/core/action.d.ts +12 -5
  74. package/dist/types/core/async.d.ts +1 -1
  75. package/dist/types/core/attribution-costs.d.ts +35 -0
  76. package/dist/types/core/attribution-feedback.d.ts +133 -0
  77. package/dist/types/core/attribution-hooks.d.ts +147 -9
  78. package/dist/types/core/attribution-queries.d.ts +10 -0
  79. package/dist/types/core/attribution.d.ts +216 -182
  80. package/dist/types/core/constants.d.ts +56 -0
  81. package/dist/types/core/core.d.ts +190 -2
  82. package/dist/types/core/dev.d.ts +246 -28
  83. package/dist/types/core/effect.d.ts +8 -1
  84. package/dist/types/core/error-hooks.d.ts +71 -0
  85. package/dist/types/core/index.d.ts +4 -1
  86. package/dist/types/core/invariants.d.ts +4 -0
  87. package/dist/types/core/lanes.d.ts +39 -5
  88. package/dist/types/core/scheduler.d.ts +153 -0
  89. package/dist/types/core/types.d.ts +32 -9
  90. package/dist/types/index.d.ts +10 -3
  91. package/dist/types/map.d.ts +2 -0
  92. package/dist/types/signals.d.ts +8 -0
  93. package/dist/types/store/index.d.ts +2 -0
  94. package/dist/types/store/next/optimistic.d.ts +1 -1
  95. package/dist/types/store/next/store.d.ts +11 -5
  96. package/dist/types/store/next/target.d.ts +37 -10
  97. package/dist/types/store/utils.d.ts +178 -0
  98. package/package.json +21 -17
  99. package/dist/node.cjs +0 -10541
  100. package/dist/node.dev.cjs +0 -13724
  101. package/dist/types-cjs/affects.d.cts +0 -47
  102. package/dist/types-cjs/boundaries.d.cts +0 -175
  103. package/dist/types-cjs/core/action.d.cts +0 -64
  104. package/dist/types-cjs/core/async.d.cts +0 -23
  105. package/dist/types-cjs/core/attribution-hooks.d.cts +0 -127
  106. package/dist/types-cjs/core/attribution.d.cts +0 -495
  107. package/dist/types-cjs/core/constants.d.cts +0 -159
  108. package/dist/types-cjs/core/core.d.cts +0 -185
  109. package/dist/types-cjs/core/dev.d.cts +0 -136
  110. package/dist/types-cjs/core/effect.d.cts +0 -30
  111. package/dist/types-cjs/core/error.d.cts +0 -56
  112. package/dist/types-cjs/core/external.d.cts +0 -15
  113. package/dist/types-cjs/core/graph.d.cts +0 -28
  114. package/dist/types-cjs/core/heap.d.cts +0 -24
  115. package/dist/types-cjs/core/index.d.cts +0 -13
  116. package/dist/types-cjs/core/invariants.d.cts +0 -59
  117. package/dist/types-cjs/core/lanes.d.cts +0 -54
  118. package/dist/types-cjs/core/optimistic.d.cts +0 -6
  119. package/dist/types-cjs/core/owner.d.cts +0 -124
  120. package/dist/types-cjs/core/scheduler.d.cts +0 -236
  121. package/dist/types-cjs/core/types.d.cts +0 -204
  122. package/dist/types-cjs/core/verdict.d.cts +0 -2
  123. package/dist/types-cjs/index.d.cts +0 -10
  124. package/dist/types-cjs/map.d.cts +0 -69
  125. package/dist/types-cjs/package.json +0 -3
  126. package/dist/types-cjs/store/index.d.cts +0 -18
  127. package/dist/types-cjs/store/next/optimistic.d.cts +0 -25
  128. package/dist/types-cjs/store/next/projection.d.cts +0 -8
  129. package/dist/types-cjs/store/next/reconcile.d.cts +0 -10
  130. package/dist/types-cjs/store/next/store.d.cts +0 -121
  131. package/dist/types-cjs/store/next/target.d.cts +0 -166
  132. package/dist/types-cjs/store/store.d.cts +0 -143
  133. package/dist/types-cjs/store/storePath.d.cts +0 -58
  134. package/dist/types-cjs/store/utils.d.cts +0 -74
@@ -0,0 +1,20 @@
1
+ /**
2
+ * `@solidjs/signals/attribution` — the "why did this run" engine.
3
+ *
4
+ * A separate entry on purpose: the core ships only the hook slot
5
+ * (`OBSERVE.attribution.install`) and the interaction frame
6
+ * (`OBSERVE.attribution.withInteraction`); the engine that turns hook facts
7
+ * into re-run explanations, cost tables, holds and feedback lives here, so an
8
+ * observe build carries it only when something imports this module. The dev
9
+ * and observe tiers resolve to this file; the prod tier resolves to
10
+ * `attribution.prod.ts`, an inert engine with the same surface, so app code
11
+ * can import it unconditionally.
12
+ */
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";
20
+ export type { InteractionRef, NavigationRef, OriginRef } from "./core/attribution-hooks.js";
@@ -0,0 +1,22 @@
1
+ /**
2
+ * `@solidjs/signals/attribution` as the prod tier resolves it.
3
+ *
4
+ * A prod build has no hook sites — `__OBSERVE__` folded every one out — so an
5
+ * engine installed there would never hear a fact. Rather than make apps guard
6
+ * the import per tier, prod resolves this inert twin: the same `Attribution`
7
+ * surface, every query empty, `enable()` a no-op. Type-checked against the
8
+ * real engine's interface so the two cannot drift.
9
+ */
10
+ import type { Attribution } from "./core/attribution.js";
11
+ import type * as Engine from "./attribution.js";
12
+ export declare const attribution: Attribution;
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";
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 writes to fresh signals
27
- * between an `await` and the next `yield` escape the transaction and commit
28
- * immediately. `await` is still the ergonomic choice for typed results; just
29
- * put a bare `yield` before any writes that follow it:
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
@@ -14,7 +14,7 @@ export declare function setPendingError(el: Computed<any>, source?: Computed<any
14
14
  export declare function forEachDependent(el: Computed<any>, fn: (node: Computed<any>, link: Link) => void): void;
15
15
  export declare function releaseSettledDependents(el: Computed<any>): void;
16
16
  export declare function settleErroredDependents(el: Computed<any>, error: any): void;
17
- export declare function settlePendingSource(el: Computed<any>): void;
17
+ export declare function settlePendingSource(el: Computed<any>, source?: Computed<any>): void;
18
18
  export declare function isThenable<T>(value: T | PromiseLike<T>): value is PromiseLike<T>;
19
19
  /** Fire and clear a node's iterator-flight cancellation hook (#3122). */
20
20
  export declare function releaseFlightTeardown(el: Computed<any>): void;
@@ -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,22 +1,48 @@
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
- * Dev-only observability hook points for the reactive core.
5
+ * Observe-tier hook points for the reactive core.
5
6
  *
6
7
  * Core's obligation is to call these with true facts at the moments they
7
8
  * happen; ALL attribution semantics (stamps, cause chains, timings, warnings)
8
- * live in the engine that installs them (attribution.ts same pattern as the
9
- * GlobalQueue._* feature slots). `attrHooks` is null unless an engine is
10
- * installed, so the disabled cost is one null check per site, and prod builds
11
- * fold every site out behind __DEV__.
9
+ * live in the engine that installs them — `@solidjs/signals/attribution`, a
10
+ * separate entry so an observe build that never enables it never ships it
11
+ * (same pattern as the GlobalQueue._* feature slots). `attrHooks` is null
12
+ * unless an engine is installed, so the disabled cost is one null check per
13
+ * site, and prod builds fold every site out behind __OBSERVE__.
12
14
  *
13
15
  * IMPORTANT for implementers of call sites: a hook call must never sit inside
14
16
  * a `try` block — rollup's tryCatchDeoptimization retains functions referenced
15
- * inside `try` even behind a folded __DEV__ guard, which re-couples the dev
17
+ * inside `try` even behind a folded __OBSERVE__ guard, which re-couples the
16
18
  * engine into prod bundles (#2883 harness). Set a local flag inside the try
17
19
  * and call the hook after the catch.
18
20
  */
19
21
  export interface AttributionHooks {
22
+ /**
23
+ * `withInteraction` opened a user-interaction frame: root writes until the
24
+ * matching `interactionEnd` were performed by the handler of `ref`. Frames
25
+ * nest strictly (synchronous dispatch), so the engine keeps a stack.
26
+ */
27
+ interactionStart(ref: InteractionRef): void;
28
+ interactionEnd(): void;
29
+ /**
30
+ * `withOrigin` opened a declared-origin frame: root writes until the
31
+ * matching `originEnd` are the unit of work `ref` describes (a router's
32
+ * navigation). Nests inside an interaction frame — a link click that
33
+ * navigates — or stands alone (a redirect from an action, a programmatic
34
+ * `navigate()`). Frames nest strictly, so the engine keeps a stack.
35
+ */
36
+ originStart(ref: OriginRef): void;
37
+ originEnd(): void;
38
+ /**
39
+ * A `flush()` drain finished: every batch it processed either committed
40
+ * (its effects have run) or was parked in a held transition (`holdStart`
41
+ * fired for it). Fires once per drain, after the loop — not per batch, and
42
+ * not for a `flush()` call that found nothing to do. Gives the engine the
43
+ * "committed, screen updated" instant for writes no transition ever held.
44
+ */
45
+ flushEnd(): void;
20
46
  /**
21
47
  * A recompute is starting; `el._deps` still holds the previous run's links.
22
48
  * Always paired with `recomputeEnd` (recompute has no early returns).
@@ -102,9 +128,12 @@ export interface AttributionHooks {
102
128
  * `prevTotal` leaves in the old one. Containers above 64 leaves are not
103
129
  * announced. Fired per written key from the write channel's notify. The
104
130
  * engine decides whether the replacement was a spread-copy worth a
105
- * 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.
106
135
  */
107
- 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;
108
137
  /**
109
138
  * A `mapArray` update both disposed and created rows: `removed` are the
110
139
  * items whose rows were disposed, `created` the items that got new rows,
@@ -122,6 +151,115 @@ export interface AttributionHooks {
122
151
  * registers / last one clears), not per flush.
123
152
  */
124
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;
166
+ }
167
+ /** A user interaction, as a rendering runtime describes it to `withInteraction`. */
168
+ export interface InteractionRef {
169
+ /** Event type — `click`, `keydown`, `input`… */
170
+ type: string;
171
+ /** The element hit, e.g. `button#next "Next →"`. */
172
+ target?: string;
173
+ /** Dispatch time on the `performance.now()` clock; defaults to now. */
174
+ at?: number;
125
175
  }
176
+ /**
177
+ * A navigation, as a router describes it to `withOrigin` around the location
178
+ * write it is about to perform. Match eagerly and describe before writing:
179
+ * the engine keys the work the write causes — the hold behind route data,
180
+ * the re-runs, the verdicts — to this record, and names it by the
181
+ * parametrized route so occurrences fold together.
182
+ *
183
+ * The engine keeps the object and reads `name`, `to` and `params` again when
184
+ * the navigation settles (and when a hold on it is judged), so a router whose
185
+ * match is not final at write time — a lazy route subtree that resolves inside
186
+ * the hold — may describe coarsely (`/admin/*`) and assign the exact pattern
187
+ * and params onto the same object once it knows them. `from` and `at` are
188
+ * read once, when the frame opens.
189
+ */
190
+ export interface NavigationRef {
191
+ kind: "navigation";
192
+ /** The matched route pattern — `/users/:id`. The name every consumer groups by. */
193
+ name?: string;
194
+ /** Concrete destination path. */
195
+ to?: string;
196
+ /** Concrete path being left. */
197
+ from?: string;
198
+ /** Route params the pattern bound — `{ id: "42" }` (optional params unbound: `undefined`). */
199
+ params?: Readonly<Record<string, string | undefined>>;
200
+ /**
201
+ * When the navigation was requested on the `performance.now()` clock;
202
+ * defaults to now. A router whose request predates the write (loaders
203
+ * awaited before the location moves) passes its own start here.
204
+ */
205
+ at?: number;
206
+ /**
207
+ * `>= 1`: this frame is the Nth redirect hop of the navigation still
208
+ * pending — a guard or loader sent it elsewhere before it landed — not a
209
+ * new navigation. The engine folds it onto that pending record: the record
210
+ * keeps the user's request time and interaction, its destination becomes
211
+ * this one, and the abandoned destination is kept in `redirects`. Without
212
+ * a pending navigation to fold onto it opens a navigation of its own.
213
+ */
214
+ redirect?: number;
215
+ }
216
+ /**
217
+ * What `withOrigin` accepts: a declared unit of work whose writes the engine
218
+ * should attribute as a whole. A discriminated union so kinds can be added
219
+ * (a form submission, a tab switch) without the seam changing shape; the
220
+ * engine knows `navigation` today.
221
+ */
222
+ export type OriginRef = NavigationRef;
126
223
  export declare let attrHooks: AttributionHooks | null;
127
224
  export declare function setAttributionHooks(hooks: AttributionHooks | null): void;
225
+ /**
226
+ * Run `fn` as the handler of a user interaction: every root write it performs
227
+ * (and every action step, effect or flight the write causes) is attributed to
228
+ * `ref` by whichever engine is installed. The web runtime wraps event
229
+ * dispatch in this; custom renderers and test harnesses call it themselves.
230
+ * With no engine installed it is `fn()` — the wiring, not the engine, so it
231
+ * lives in core and is reachable as `OBSERVE.attribution.withInteraction`.
232
+ *
233
+ * The `finally` is deliberate and safe under the try rule above: this
234
+ * function is referenced only from the `OBSERVE` object, which prod builds
235
+ * fold to `undefined`, so nothing retains it there.
236
+ */
237
+ export declare function withInteraction<T>(ref: InteractionRef, fn: () => T): T;
238
+ /**
239
+ * Run `fn` as a declared unit of work — a router's navigation: every root
240
+ * write it performs is attributed to `ref` (and, through it, to the enclosing
241
+ * interaction when there is one), so the hold those writes wait in, the
242
+ * re-runs they cause and the verdicts on them all carry the route's name
243
+ * instead of a bare signal's. Same contract as `withInteraction`: the wiring,
244
+ * not the engine; `fn()` with no engine installed. Reachable as
245
+ * `OBSERVE.attribution.withOrigin`.
246
+ *
247
+ * ```ts
248
+ * OBSERVE
249
+ * ? OBSERVE.attribution.withOrigin(
250
+ * { kind: "navigation", name: match.pattern, to, from, params: match.params },
251
+ * () => setLocation(to)
252
+ * )
253
+ * : setLocation(to);
254
+ * ```
255
+ */
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[];