browser-debugger-cli 0.8.0 → 0.9.0

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 (251) hide show
  1. package/README.md +4 -1
  2. package/dist/cdp/schema.d.ts +4 -1
  3. package/dist/cdp/schema.js +48 -7
  4. package/dist/commands/cdp.js +3 -2
  5. package/dist/commands/cleanup.d.ts +11 -0
  6. package/dist/commands/cleanup.js +161 -57
  7. package/dist/commands/console.d.ts +20 -1
  8. package/dist/commands/console.js +57 -17
  9. package/dist/commands/details.js +3 -2
  10. package/dist/commands/dom/DomElementResolver.d.ts +10 -3
  11. package/dist/commands/dom/DomElementResolver.js +35 -17
  12. package/dist/commands/dom/a11y.d.ts +10 -0
  13. package/dist/commands/dom/a11y.js +27 -5
  14. package/dist/commands/dom/eval.d.ts +3 -1
  15. package/dist/commands/dom/eval.js +29 -4
  16. package/dist/commands/dom/form.js +16 -62
  17. package/dist/commands/dom/formInteraction.js +152 -113
  18. package/dist/commands/dom/formSummary.d.ts +49 -0
  19. package/dist/commands/dom/formSummary.js +180 -0
  20. package/dist/commands/dom/frames.d.ts +2 -1
  21. package/dist/commands/dom/frames.js +17 -2
  22. package/dist/commands/dom/get.d.ts +6 -5
  23. package/dist/commands/dom/get.js +92 -82
  24. package/dist/commands/dom/helpers/index.d.ts +1 -1
  25. package/dist/commands/dom/helpers/index.js +1 -1
  26. package/dist/commands/dom/helpers/query.d.ts +44 -17
  27. package/dist/commands/dom/helpers/query.js +244 -97
  28. package/dist/commands/dom/helpers/runElementCommand.d.ts +10 -2
  29. package/dist/commands/dom/helpers/runElementCommand.js +97 -30
  30. package/dist/commands/dom/helpers/screenshot.d.ts +4 -1
  31. package/dist/commands/dom/helpers/screenshot.js +164 -49
  32. package/dist/commands/dom/index.d.ts +3 -1
  33. package/dist/commands/dom/index.js +16 -6
  34. package/dist/commands/dom/layout.d.ts +14 -0
  35. package/dist/commands/dom/layout.js +54 -0
  36. package/dist/commands/dom/listeners.d.ts +5 -1
  37. package/dist/commands/dom/listeners.js +13 -3
  38. package/dist/commands/dom/query.js +2 -3
  39. package/dist/commands/dom/screenshot.d.ts +12 -2
  40. package/dist/commands/dom/screenshot.js +27 -3
  41. package/dist/commands/dom/semanticUtils.d.ts +6 -13
  42. package/dist/commands/dom/semanticUtils.js +15 -19
  43. package/dist/commands/dom/wait.d.ts +13 -0
  44. package/dist/commands/dom/wait.js +83 -0
  45. package/dist/commands/helpJson.js +2 -2
  46. package/dist/commands/network/list.js +4 -11
  47. package/dist/commands/optionBehaviors.js +112 -21
  48. package/dist/commands/page.d.ts +2 -1
  49. package/dist/commands/page.js +41 -5
  50. package/dist/commands/peek.js +4 -11
  51. package/dist/commands/sessions.d.ts +8 -0
  52. package/dist/commands/sessions.js +19 -0
  53. package/dist/commands/shared/CommandRunner.js +4 -4
  54. package/dist/commands/shared/dataFetcher.js +2 -2
  55. package/dist/commands/shared/followMode.d.ts +21 -1
  56. package/dist/commands/shared/followMode.js +29 -2
  57. package/dist/commands/shared/handleValidationError.d.ts +2 -2
  58. package/dist/commands/shared/handleValidationError.js +12 -3
  59. package/dist/commands/shared/optionTypes.d.ts +40 -5
  60. package/dist/commands/shared/startHelpers.js +12 -3
  61. package/dist/commands/shared/validation.d.ts +3 -2
  62. package/dist/commands/shared/validation.js +4 -3
  63. package/dist/commands/start.d.ts +63 -0
  64. package/dist/commands/start.js +115 -15
  65. package/dist/commands/status.js +29 -7
  66. package/dist/commands/stop.js +7 -6
  67. package/dist/commands/tail.js +4 -11
  68. package/dist/commands/types.d.ts +2 -0
  69. package/dist/commands.js +2 -0
  70. package/dist/connection/chromeIdentity.d.ts +65 -0
  71. package/dist/connection/chromeIdentity.js +143 -0
  72. package/dist/connection/launcher/profilePreferences.d.ts +47 -0
  73. package/dist/connection/launcher/profilePreferences.js +151 -0
  74. package/dist/connection/launcher.d.ts +21 -2
  75. package/dist/connection/launcher.js +42 -16
  76. package/dist/connection/portReservation.d.ts +14 -4
  77. package/dist/connection/portReservation.js +21 -6
  78. package/dist/connection/startupExit.d.ts +8 -0
  79. package/dist/connection/startupExit.js +15 -6
  80. package/dist/constants.d.ts +6 -2
  81. package/dist/constants.js +9 -2
  82. package/dist/daemon/SessionController.js +23 -7
  83. package/dist/daemon/errors.d.ts +1 -1
  84. package/dist/daemon/errors.js +1 -1
  85. package/dist/daemon/launcher.d.ts +2 -1
  86. package/dist/daemon/launcher.js +5 -6
  87. package/dist/daemon/server/SocketServer.js +1 -2
  88. package/dist/daemon/session/Session.d.ts +13 -0
  89. package/dist/daemon/session/Session.js +57 -8
  90. package/dist/daemon/session/chromeConnection.d.ts +9 -0
  91. package/dist/daemon/session/chromeConnection.js +45 -8
  92. package/dist/daemon/session/commandRegistry.js +52 -62
  93. package/dist/daemon/session/interactions.d.ts +35 -9
  94. package/dist/daemon/session/interactions.js +36 -9
  95. package/dist/daemon/session/triggeredRequests.d.ts +67 -0
  96. package/dist/daemon/session/triggeredRequests.js +157 -0
  97. package/dist/daemon/session/types.d.ts +5 -1
  98. package/dist/daemon.js +5393 -1600
  99. package/dist/errors/messages.d.ts +387 -24
  100. package/dist/errors/messages.js +761 -67
  101. package/dist/index.js +3976 -1558
  102. package/dist/ipc/client.d.ts +12 -1
  103. package/dist/ipc/client.js +22 -3
  104. package/dist/ipc/protocol/commands.d.ts +89 -4
  105. package/dist/ipc/protocol/commands.js +2 -0
  106. package/dist/ipc/protocol/domTypes.d.ts +258 -7
  107. package/dist/ipc/session/lifecycle.d.ts +8 -1
  108. package/dist/ipc/session/queries.d.ts +5 -1
  109. package/dist/ipc/session/types.d.ts +5 -0
  110. package/dist/ipc/transport/index.d.ts +2 -1
  111. package/dist/ipc/transport/index.js +2 -2
  112. package/dist/runtime/dom/actionEffects.d.ts +106 -0
  113. package/dist/runtime/dom/actionEffects.js +256 -0
  114. package/dist/runtime/dom/actionEffectsScripts.d.ts +52 -0
  115. package/dist/runtime/dom/actionEffectsScripts.js +234 -0
  116. package/dist/runtime/dom/elementGeometry.d.ts +170 -0
  117. package/dist/runtime/dom/elementGeometry.js +553 -0
  118. package/dist/runtime/dom/elementInfo.d.ts +77 -0
  119. package/dist/runtime/dom/elementInfo.js +191 -0
  120. package/dist/runtime/dom/evalHelpers.d.ts +51 -6
  121. package/dist/runtime/dom/evalHelpers.js +136 -26
  122. package/dist/runtime/dom/eventListeners.d.ts +2 -1
  123. package/dist/runtime/dom/eventListeners.js +174 -47
  124. package/dist/runtime/dom/formDiscovery.d.ts +1 -1
  125. package/dist/runtime/dom/formDiscovery.js +116 -16
  126. package/dist/runtime/dom/formFillHelpers/fill.d.ts +10 -0
  127. package/dist/runtime/dom/formFillHelpers/fill.js +125 -10
  128. package/dist/runtime/dom/formFillHelpers/index.d.ts +2 -2
  129. package/dist/runtime/dom/formFillHelpers/index.js +2 -2
  130. package/dist/runtime/dom/formFillHelpers/pressKey.js +14 -3
  131. package/dist/runtime/dom/formFillHelpers/scroll.d.ts +3 -0
  132. package/dist/runtime/dom/formFillHelpers/scroll.js +60 -18
  133. package/dist/runtime/dom/formFillHelpers/shared.d.ts +17 -0
  134. package/dist/runtime/dom/formFillHelpers/shared.js +25 -1
  135. package/dist/runtime/dom/formFillHelpers/stability.d.ts +20 -6
  136. package/dist/runtime/dom/formFillHelpers/stability.js +50 -19
  137. package/dist/runtime/dom/formSubmitHelpers.d.ts +3 -0
  138. package/dist/runtime/dom/formSubmitHelpers.js +89 -15
  139. package/dist/runtime/dom/frameLayout.d.ts +60 -0
  140. package/dist/runtime/dom/frameLayout.js +140 -0
  141. package/dist/runtime/dom/frameOrigin.d.ts +50 -0
  142. package/dist/runtime/dom/frameOrigin.js +62 -0
  143. package/dist/runtime/dom/frameScopedConnection.d.ts +92 -0
  144. package/dist/runtime/dom/frameScopedConnection.js +252 -0
  145. package/dist/runtime/dom/frameSelection.d.ts +1 -1
  146. package/dist/runtime/dom/frameSelection.js +2 -2
  147. package/dist/runtime/dom/frames.d.ts +25 -2
  148. package/dist/runtime/dom/frames.js +202 -63
  149. package/dist/runtime/dom/layout.d.ts +67 -0
  150. package/dist/runtime/dom/layout.js +333 -0
  151. package/dist/runtime/dom/listenerPageScripts.d.ts +66 -0
  152. package/dist/runtime/dom/listenerPageScripts.js +279 -0
  153. package/dist/runtime/dom/listenerSummary.d.ts +132 -11
  154. package/dist/runtime/dom/listenerSummary.js +344 -22
  155. package/dist/runtime/dom/pageActivity.d.ts +41 -0
  156. package/dist/runtime/dom/pageActivity.js +123 -0
  157. package/dist/runtime/dom/reactEventHelpers.d.ts +58 -2
  158. package/dist/runtime/dom/reactEventHelpers.js +212 -41
  159. package/dist/runtime/dom/targetNode.d.ts +80 -27
  160. package/dist/runtime/dom/targetNode.js +249 -33
  161. package/dist/runtime/dom/wait.d.ts +25 -0
  162. package/dist/runtime/dom/wait.js +199 -0
  163. package/dist/runtime/dom/waitCondition.d.ts +71 -0
  164. package/dist/runtime/dom/waitCondition.js +75 -0
  165. package/dist/runtime/page/emulation.d.ts +51 -0
  166. package/dist/runtime/page/emulation.js +80 -0
  167. package/dist/runtime/page/loadingState.d.ts +36 -0
  168. package/dist/runtime/page/loadingState.js +86 -0
  169. package/dist/runtime/page/navigation.d.ts +46 -2
  170. package/dist/runtime/page/navigation.js +69 -33
  171. package/dist/session/QueryCacheManager.d.ts +11 -1
  172. package/dist/session/QueryCacheManager.js +25 -3
  173. package/dist/session/chromeOwners.d.ts +34 -0
  174. package/dist/session/chromeOwners.js +51 -0
  175. package/dist/session/cleanup/staleSession.d.ts +11 -1
  176. package/dist/session/cleanup/staleSession.js +17 -6
  177. package/dist/session/cleanup/userCommands.js +2 -4
  178. package/dist/session/metadata.d.ts +5 -1
  179. package/dist/session/metadata.js +2 -1
  180. package/dist/session/paths.d.ts +77 -3
  181. package/dist/session/paths.js +111 -5
  182. package/dist/session/port.d.ts +31 -7
  183. package/dist/session/port.js +50 -43
  184. package/dist/session/portClaims.d.ts +66 -0
  185. package/dist/session/portClaims.js +284 -0
  186. package/dist/session/sessionList.d.ts +58 -0
  187. package/dist/session/sessionList.js +199 -0
  188. package/dist/session/sessionName.d.ts +46 -0
  189. package/dist/session/sessionName.js +97 -0
  190. package/dist/telemetry/a11y.d.ts +8 -3
  191. package/dist/telemetry/a11y.js +92 -28
  192. package/dist/telemetry/requestKinds.d.ts +32 -0
  193. package/dist/telemetry/requestKinds.js +61 -0
  194. package/dist/telemetry/requestState.d.ts +31 -0
  195. package/dist/telemetry/requestState.js +38 -0
  196. package/dist/types.d.ts +80 -3
  197. package/dist/ui/formatters/a11y.js +3 -0
  198. package/dist/ui/formatters/console/chronological.d.ts +8 -0
  199. package/dist/ui/formatters/console/chronological.js +17 -4
  200. package/dist/ui/formatters/console/json.js +3 -4
  201. package/dist/ui/formatters/console/shared.d.ts +12 -0
  202. package/dist/ui/formatters/console.d.ts +2 -2
  203. package/dist/ui/formatters/console.js +1 -1
  204. package/dist/ui/formatters/details.js +2 -1
  205. package/dist/ui/formatters/dom.d.ts +26 -14
  206. package/dist/ui/formatters/dom.js +65 -52
  207. package/dist/ui/formatters/form.js +29 -18
  208. package/dist/ui/formatters/layout.d.ts +31 -0
  209. package/dist/ui/formatters/layout.js +53 -0
  210. package/dist/ui/formatters/listeners.d.ts +3 -2
  211. package/dist/ui/formatters/listeners.js +73 -9
  212. package/dist/ui/formatters/networkHeaders.js +13 -0
  213. package/dist/ui/formatters/preview.js +2 -1
  214. package/dist/ui/formatters/requestStatus.d.ts +1 -17
  215. package/dist/ui/formatters/requestStatus.js +2 -30
  216. package/dist/ui/formatters/sessions.d.ts +12 -0
  217. package/dist/ui/formatters/sessions.js +40 -0
  218. package/dist/ui/formatters/status.d.ts +21 -2
  219. package/dist/ui/formatters/status.js +47 -10
  220. package/dist/ui/formatters/triggeredRequests.d.ts +36 -0
  221. package/dist/ui/formatters/triggeredRequests.js +65 -0
  222. package/dist/ui/formatting.d.ts +10 -0
  223. package/dist/ui/formatting.js +28 -36
  224. package/dist/ui/messages/chrome.d.ts +9 -0
  225. package/dist/ui/messages/chrome.js +17 -5
  226. package/dist/ui/messages/commands.d.ts +388 -14
  227. package/dist/ui/messages/commands.js +664 -21
  228. package/dist/ui/messages/consoleMessages.d.ts +10 -0
  229. package/dist/ui/messages/consoleMessages.js +17 -0
  230. package/dist/ui/messages/hints.js +2 -1
  231. package/dist/ui/messages/preview.js +5 -4
  232. package/dist/ui/messages/session.d.ts +16 -21
  233. package/dist/ui/messages/session.js +28 -26
  234. package/dist/ui/messages/sessionCommand.d.ts +43 -0
  235. package/dist/ui/messages/sessionCommand.js +52 -0
  236. package/dist/utils/async.d.ts +8 -0
  237. package/dist/utils/async.js +19 -0
  238. package/dist/utils/http.d.ts +22 -1
  239. package/dist/utils/http.js +28 -9
  240. package/dist/utils/selectorFilters.d.ts +36 -8
  241. package/dist/utils/selectorFilters.js +267 -53
  242. package/dist/utils/shellDetection.d.ts +8 -2
  243. package/dist/utils/shellDetection.js +120 -33
  244. package/dist/utils/suggestions.d.ts +26 -0
  245. package/dist/utils/suggestions.js +73 -0
  246. package/dist/utils/taskMappings.js +10 -0
  247. package/dist/utils/url.d.ts +12 -2
  248. package/dist/utils/url.js +69 -7
  249. package/package.json +1 -1
  250. package/dist/ui/formatters/sessionFormatters.d.ts +0 -58
  251. package/dist/ui/formatters/sessionFormatters.js +0 -121
@@ -6,13 +6,15 @@
6
6
  * these name, place, filter and order them.
7
7
  */
8
8
  import type { Protocol } from '../../connection/typed-cdp.js';
9
- import type { ElementListener, ListenerPlacement } from '../../ipc/protocol/domTypes.js';
9
+ import type { CollapsedListeners, ElementListener, ListenerPlacement } from '../../ipc/protocol/domTypes.js';
10
10
  /** An event target on the path from the element out to its window. */
11
11
  export interface ChainEntry {
12
12
  /** CDP class name, e.g. `HTMLDivElement`, `HTMLDocument`, `Window` */
13
13
  className?: string | undefined;
14
14
  /** CDP description, e.g. `div#root.app` */
15
15
  description?: string | undefined;
16
+ /** Framework recognised on the node, e.g. `React root` */
17
+ framework?: string | undefined;
16
18
  }
17
19
  /** Listeners found on one entry of the chain. */
18
20
  export interface ChainListeners {
@@ -51,22 +53,141 @@ export declare function handlerPreview(source: string | undefined): string;
51
53
  * @returns Name, or '' for anonymous and arrow functions
52
54
  */
53
55
  export declare function functionNameFromSource(source: string | undefined): string;
56
+ /** What the page reported about one listener's handler */
57
+ export interface HandlerDetails {
58
+ /** `Function.name` of the handler */
59
+ name?: string | undefined;
60
+ /**
61
+ * Identity of the function the handler calls (bound functions unwrapped to
62
+ * their target): equal ids are the same function object
63
+ */
64
+ identity?: number | undefined;
65
+ /** `Function.name` of that function */
66
+ targetName?: string | undefined;
67
+ /** Handlers jQuery runs from this listener (set when it is jQuery's dispatcher) */
68
+ jquery?: ResolvedHandler[] | undefined;
69
+ }
70
+ /** A handler registered through a framework, found behind its dispatcher */
71
+ export interface ResolvedHandler {
72
+ /** Event type it was registered for */
73
+ type: string;
74
+ /** Delegate selector, for delegated handlers */
75
+ selector?: string | undefined;
76
+ /** `Function.name` */
77
+ name: string;
78
+ /** Function source */
79
+ source?: string | undefined;
80
+ scriptId: string;
81
+ lineNumber: number;
82
+ columnNumber: number;
83
+ }
84
+ /** A React `on…` prop handler of the element or one of its React parents */
85
+ export interface ReactPropHandler extends ResolvedHandler {
86
+ /**
87
+ * Position in the chain of the element the prop is on (0 = the element);
88
+ * null for a React parent outside the chain (the parent of a portal)
89
+ */
90
+ position: number | null;
91
+ /** Description of that element when it is outside the chain */
92
+ node?: string | undefined;
93
+ /** Prop name, e.g. `onClickCapture` */
94
+ prop: string;
95
+ /** Runs in the capture phase (`on…Capture`) */
96
+ capture: boolean;
97
+ }
98
+ /** What to keep in the report */
99
+ export interface ReportOptions {
100
+ /** Event types to keep (default: all) */
101
+ types?: string[] | undefined;
102
+ /** Keep every framework root listener instead of a summary per node */
103
+ all?: boolean | undefined;
104
+ }
105
+ /** The `bdg dom listeners` report */
106
+ export interface ListenerReport {
107
+ listeners: ElementListener[];
108
+ collapsed: CollapsedListeners[];
109
+ }
110
+ /** A report entry with its position in the chain */
111
+ export interface PlacedListener {
112
+ position: number;
113
+ entry: ChainEntry;
114
+ listener: ElementListener;
115
+ /** Identity of the handler's function (unique when unknown) */
116
+ identity: string;
117
+ /** Name of the handler's function, bound functions unwrapped */
118
+ targetName?: string | undefined;
119
+ }
54
120
  /**
55
- * Build the listener report: filtered to `types` (if given), grouped by
56
- * event type (alphabetically), nearest first within a type.
121
+ * Whether a function source is an empty function (`function u0(){}`,
122
+ * `() => {}`): React sets one as `onclick` on clickable elements.
123
+ *
124
+ * @param source - Function source
125
+ * @returns True for a handler that does nothing
126
+ */
127
+ export declare function isNoopSource(source: string | undefined): boolean;
128
+ /**
129
+ * Collapse framework roots: on a recognised React root container (not the
130
+ * element itself), function objects that each listen for several event
131
+ * types, and together for many, are React's dispatchers. Each root's
132
+ * dispatchers become one summary entry; other multi-type handlers (an
133
+ * analytics listener on `document`) are kept as they are.
134
+ *
135
+ * @param placed - Report entries
136
+ * @returns Entries kept as they are, and one summary per framework root
137
+ */
138
+ export declare function collapseFrameworkRoots(placed: PlacedListener[]): {
139
+ kept: PlacedListener[];
140
+ collapsed: CollapsedListeners[];
141
+ };
142
+ /**
143
+ * Build the listener report: React's `on…` props added, filtered to `types`
144
+ * (if given), framework roots collapsed (unless `all`), grouped by event
145
+ * type with the handlers nearest the element first (see {@link orderListeners}).
57
146
  *
58
147
  * @param found - Listeners per chain entry, in chain order
59
- * @param names - Handler names in the order of the flattened listeners (missing = unknown)
60
- * @param types - Event types to keep (default: all)
61
- * @returns Ordered listeners
148
+ * @param details - Per flattened listener: handler name, jQuery handlers (missing = unknown)
149
+ * @param options - Event types to keep, whether to keep every root listener
150
+ * @param react - React prop handlers of the element and its ancestors
151
+ * @returns Ordered listeners and collapsed framework roots
152
+ */
153
+ export declare function buildListenerReport(found: ChainListeners[], details: HandlerDetails[], options?: ReportOptions, react?: ReactPropHandler[]): ListenerReport;
154
+ /**
155
+ * How an event type without a listener of its own on the element reaches
156
+ * its handlers:
157
+ * - `react`: React runs `on…` props listed in the report (from its root)
158
+ * - `react-root`: a React root listens for it, but no `on…` prop was found
159
+ * - `jquery`: jQuery runs delegated handlers listed in the report
160
+ * - `delegated`: plain listeners on ancestors, document or window
62
161
  */
63
- export declare function buildListenerReport(found: ChainListeners[], names: Array<string | undefined>, types?: string[]): ElementListener[];
162
+ export type DelegationKind = 'react' | 'react-root' | 'jquery' | 'delegated';
163
+ /** Interaction event types handled the same way, for the report's notes */
164
+ export interface DelegationNote {
165
+ kind: DelegationKind;
166
+ /** Event types, in report order */
167
+ types: string[];
168
+ /** Those of `types` whose only listener on the element is a no-op (React's placeholder) */
169
+ placeholderTypes: string[];
170
+ /** The node handling them: the React root container or jQuery's node */
171
+ node?: string | undefined;
172
+ }
64
173
  /**
65
- * Interaction event types that have listeners, but none on the element
66
- * itself: frameworks (React, jQuery) handle these by delegation.
174
+ * Notes for interaction event types the element has no listener of its own
175
+ * for (no-ops aside), grouped by how they reach their handlers: React `on…`
176
+ * props, a React root without props, jQuery delegation, or plain listeners
177
+ * on ancestors, document or window.
67
178
  *
68
179
  * @param listeners - Listener report
69
- * @returns Event types, in report order
180
+ * @param collapsed - Collapsed framework roots
181
+ * @returns Notes, React first; none when every type has a listener on the element
182
+ */
183
+ export declare function delegationNotes(listeners: ElementListener[], collapsed?: CollapsedListeners[]): DelegationNote[];
184
+ /**
185
+ * Event types with listeners that a `--type` value probably meant, for an
186
+ * empty result: `Click` → `click`, `onclick` → `click`.
187
+ *
188
+ * @param requested - Types asked for
189
+ * @param available - Types that have listeners
190
+ * @returns Suggestions (none when nothing is close)
70
191
  */
71
- export declare function delegatedOnlyTypes(listeners: ElementListener[]): string[];
192
+ export declare function suggestEventTypes(requested: string[], available: string[]): string[];
72
193
  //# sourceMappingURL=listenerSummary.d.ts.map
@@ -6,6 +6,7 @@
6
6
  * these name, place, filter and order them.
7
7
  */
8
8
  import { truncateByLength } from '../../utils/strings.js';
9
+ import { findSimilar } from '../../utils/suggestions.js';
9
10
  /** Longest handler source preview */
10
11
  const PREVIEW_MAX_LENGTH = 80;
11
12
  /** Longest node description (class lists can be very long) */
@@ -88,6 +89,29 @@ export function functionNameFromSource(source) {
88
89
  const method = /^(?:async\s+)?(?:get\s+|set\s+)?([\w$]+)\s*\(/.exec(text);
89
90
  return method?.[1] && method[1] !== 'function' ? method[1] : '';
90
91
  }
92
+ /** Distinct event types one function must handle on a node to count as a dispatcher */
93
+ const DISPATCHER_MIN_TYPES = 3;
94
+ /** Distinct event types a node's dispatchers must handle together to be collapsed */
95
+ const COLLAPSE_MIN_TYPES = 8;
96
+ /** Names of React's event dispatchers (development builds keep them) */
97
+ const REACT_DISPATCHER_NAMES = new Set([
98
+ 'dispatchDiscreteEvent',
99
+ 'dispatchContinuousEvent',
100
+ 'dispatchEvent',
101
+ ]);
102
+ /** Label of a recognised React root container */
103
+ const REACT_ROOT = 'React root';
104
+ /**
105
+ * Whether a function source is an empty function (`function u0(){}`,
106
+ * `() => {}`): React sets one as `onclick` on clickable elements.
107
+ *
108
+ * @param source - Function source
109
+ * @returns True for a handler that does nothing
110
+ */
111
+ export function isNoopSource(source) {
112
+ const text = (source ?? '').replace(/\s+/g, '');
113
+ return /^(?:function[\w$]*\(\)|\(\)=>)\{\}$/.test(text);
114
+ }
91
115
  /**
92
116
  * Build the report entry for one listener.
93
117
  *
@@ -112,39 +136,337 @@ function toElementListener(listener, found, name) {
112
136
  lineNumber: listener.lineNumber,
113
137
  columnNumber: listener.columnNumber,
114
138
  },
139
+ ...(isNoopSource(source) && { noop: true }),
140
+ };
141
+ }
142
+ /**
143
+ * Report entries for a jQuery dispatcher: one per jQuery handler that runs
144
+ * for the element, with the dispatcher's flags.
145
+ *
146
+ * @param dispatcher - Entry built for jQuery's own listener
147
+ * @param handlers - Handlers behind it
148
+ * @returns Entries naming the real handlers
149
+ */
150
+ function jqueryListeners(dispatcher, handlers) {
151
+ return handlers.map((resolved) => ({
152
+ ...dispatcher,
153
+ handler: {
154
+ name: resolved.name || functionNameFromSource(resolved.source),
155
+ preview: handlerPreview(resolved.source),
156
+ scriptId: resolved.scriptId,
157
+ lineNumber: resolved.lineNumber,
158
+ columnNumber: resolved.columnNumber,
159
+ },
160
+ framework: 'jQuery',
161
+ ...(resolved.selector && { delegateSelector: resolved.selector }),
162
+ }));
163
+ }
164
+ /** Chain position given to React parents outside the chain: after the last one inside */
165
+ const OUTSIDE_CHAIN_OFFSET = 0.5;
166
+ /**
167
+ * Where a React prop handler's element sits: its chain entry, or for a
168
+ * React parent outside the chain (a portal's), an ancestor entry placed
169
+ * just after the nearest parent inside it.
170
+ *
171
+ * @param found - Listeners per chain entry (every entry of the chain)
172
+ * @param resolved - The prop handler
173
+ * @param previous - Chain position of the previous (nearer) prop handler's element
174
+ * @returns Position and chain entry, undefined when the position is unknown
175
+ */
176
+ function reactOwner(found, resolved, previous) {
177
+ if (resolved.position === null) {
178
+ const entry = { className: 'HTMLElement', description: resolved.node ?? '?' };
179
+ return { position: Math.floor(previous) + OUTSIDE_CHAIN_OFFSET, entry };
180
+ }
181
+ const owner = found.find((item) => item.position === resolved.position);
182
+ return owner && { position: owner.position, entry: owner.entry };
183
+ }
184
+ /**
185
+ * Report entries for React's `on…` props: React runs them from its root
186
+ * container's dispatchers, for the element the prop is on.
187
+ *
188
+ * @param found - Listeners per chain entry (every entry of the chain)
189
+ * @param react - React prop handlers, nearest first
190
+ * @returns Entries, one per prop
191
+ */
192
+ function placeReactProps(found, react) {
193
+ let previous = 0;
194
+ return react.flatMap((resolved, i) => {
195
+ const owner = reactOwner(found, resolved, previous);
196
+ if (!owner)
197
+ return [];
198
+ previous = owner.position;
199
+ const listener = {
200
+ type: resolved.type,
201
+ on: listenerPlacement(owner.entry, owner.position),
202
+ node: describeChainEntry(owner.entry),
203
+ useCapture: resolved.capture,
204
+ passive: false,
205
+ once: false,
206
+ handler: {
207
+ name: resolved.name || functionNameFromSource(resolved.source),
208
+ preview: handlerPreview(resolved.source),
209
+ scriptId: resolved.scriptId,
210
+ lineNumber: resolved.lineNumber,
211
+ columnNumber: resolved.columnNumber,
212
+ },
213
+ framework: 'React',
214
+ reactProp: resolved.prop,
215
+ };
216
+ return [{ ...owner, listener, identity: `react-${i}` }];
217
+ });
218
+ }
219
+ /**
220
+ * Identity of a dispatcher: the node and the function object it calls.
221
+ *
222
+ * @param placed - Report entry
223
+ * @returns Key, unique per node and function
224
+ */
225
+ function dispatcherKey({ position, identity }) {
226
+ return `${position}|${identity}`;
227
+ }
228
+ /**
229
+ * The framework a node's listeners belong to: a React root container
230
+ * (recognised by React's keys on the node, or by its dispatchers' names).
231
+ *
232
+ * @param nodeListeners - Listeners of one node
233
+ * @returns Framework label, undefined when none is recognised
234
+ */
235
+ function nodeFramework(nodeListeners) {
236
+ const [first] = nodeListeners;
237
+ if (first?.entry.framework)
238
+ return first.entry.framework;
239
+ const hasDispatcher = nodeListeners.some((item) => REACT_DISPATCHER_NAMES.has(item.targetName ?? ''));
240
+ return hasDispatcher ? REACT_ROOT : undefined;
241
+ }
242
+ /**
243
+ * Summarise one node's dispatcher listeners.
244
+ *
245
+ * @param group - Listeners of one node (not empty)
246
+ * @param framework - Framework recognised on the node
247
+ * @returns Collapsed entry
248
+ */
249
+ function summarizeNode(group, framework) {
250
+ const first = group[0];
251
+ const handlers = new Map(group.map((placed) => [dispatcherKey(placed), placed.listener.handler]));
252
+ return {
253
+ on: first.listener.on,
254
+ node: first.listener.node,
255
+ framework,
256
+ types: [...new Set(group.map(({ listener }) => listener.type))].sort(),
257
+ count: group.length,
258
+ capture: group.some(({ listener }) => listener.useCapture),
259
+ bubble: group.some(({ listener }) => !listener.useCapture),
260
+ handlers: [...handlers.values()],
261
+ };
262
+ }
263
+ /**
264
+ * Group items by a key, keeping their order.
265
+ *
266
+ * @param items - Items
267
+ * @param keyOf - Key of an item
268
+ * @returns Groups by key
269
+ */
270
+ function groupBy(items, keyOf) {
271
+ const groups = new Map();
272
+ for (const item of items)
273
+ groups.set(keyOf(item), [...(groups.get(keyOf(item)) ?? []), item]);
274
+ return groups;
275
+ }
276
+ /**
277
+ * Distinct event types of listeners.
278
+ *
279
+ * @param items - Report entries
280
+ * @returns Number of types
281
+ */
282
+ function typeCount(items) {
283
+ return new Set(items.map(({ listener }) => listener.type)).size;
284
+ }
285
+ /**
286
+ * A framework root's dispatcher listeners: those of function objects that
287
+ * each listen for several event types, when together they cover many types.
288
+ *
289
+ * @param nodeListeners - Listeners of one node
290
+ * @returns The dispatchers and the framework, or undefined when the node is no framework root
291
+ */
292
+ function frameworkDispatchers(nodeListeners) {
293
+ const framework = nodeFramework(nodeListeners);
294
+ if (!framework)
295
+ return undefined;
296
+ const candidates = nodeListeners.filter((item) => !item.listener.framework);
297
+ const dispatchers = [...groupBy(candidates, dispatcherKey).values()]
298
+ .filter((group) => typeCount(group) >= DISPATCHER_MIN_TYPES)
299
+ .flat();
300
+ return typeCount(dispatchers) >= COLLAPSE_MIN_TYPES ? { framework, dispatchers } : undefined;
301
+ }
302
+ /**
303
+ * Collapse framework roots: on a recognised React root container (not the
304
+ * element itself), function objects that each listen for several event
305
+ * types, and together for many, are React's dispatchers. Each root's
306
+ * dispatchers become one summary entry; other multi-type handlers (an
307
+ * analytics listener on `document`) are kept as they are.
308
+ *
309
+ * @param placed - Report entries
310
+ * @returns Entries kept as they are, and one summary per framework root
311
+ */
312
+ export function collapseFrameworkRoots(placed) {
313
+ const ancestors = placed.filter((item) => item.position > 0);
314
+ const roots = [...groupBy(ancestors, (item) => item.position).values()].flatMap((nodeListeners) => frameworkDispatchers(nodeListeners) ?? []);
315
+ const collapsedItems = new Set(roots.flatMap((root) => root.dispatchers));
316
+ return {
317
+ kept: placed.filter((item) => !collapsedItems.has(item)),
318
+ collapsed: roots.map((root) => summarizeNode(root.dispatchers, root.framework)),
115
319
  };
116
320
  }
117
321
  /**
118
- * Build the listener report: filtered to `types` (if given), grouped by
119
- * event type (alphabetically), nearest first within a type.
322
+ * Report entries of every listener, jQuery dispatchers replaced by the
323
+ * handlers they run for the element.
120
324
  *
121
325
  * @param found - Listeners per chain entry, in chain order
122
- * @param names - Handler names in the order of the flattened listeners (missing = unknown)
123
- * @param types - Event types to keep (default: all)
124
- * @returns Ordered listeners
326
+ * @param details - Per flattened listener: handler name, jQuery handlers
327
+ * @returns Entries in chain order
125
328
  */
126
- export function buildListenerReport(found, names, types) {
329
+ function placeListeners(found, details) {
127
330
  const flat = found.flatMap((entry) => entry.listeners.map((listener) => ({ entry, listener })));
128
- const listeners = flat
129
- .map(({ entry, listener }, i) => ({
130
- position: entry.position,
131
- listener: toElementListener(listener, entry, names[i]),
132
- }))
133
- .filter(({ listener }) => !types?.length || types.includes(listener.type));
134
- return listeners
135
- .sort((a, b) => a.listener.type.localeCompare(b.listener.type) || a.position - b.position)
136
- .map(({ listener }) => listener);
331
+ return flat.flatMap(({ entry, listener }, i) => {
332
+ const detail = details[i] ?? {};
333
+ const built = toElementListener(listener, entry, detail.name);
334
+ const listeners = detail.jquery ? jqueryListeners(built, detail.jquery) : [built];
335
+ return listeners.map((item) => ({
336
+ position: entry.position,
337
+ entry: entry.entry,
338
+ listener: item,
339
+ identity: detail.identity === undefined ? `listener-${i}` : `fn-${detail.identity}`,
340
+ targetName: detail.targetName,
341
+ }));
342
+ });
343
+ }
344
+ /** Sort rank of no-op handlers: after every handler that does something */
345
+ const NOOP_RANK = Number.MAX_SAFE_INTEGER;
346
+ /**
347
+ * Sort rank of a report entry: its distance from the element, no-ops last.
348
+ *
349
+ * @param placed - Report entry
350
+ * @returns Rank (lower comes first)
351
+ */
352
+ function handlerRank({ listener, position }) {
353
+ return listener.noop ? NOOP_RANK : position;
137
354
  }
138
355
  /**
139
- * Interaction event types that have listeners, but none on the element
140
- * itself: frameworks (React, jQuery) handle these by delegation.
356
+ * Order report entries so the handlers that run for the element come
357
+ * first: event types whose nearest handler (no-ops aside) is closest to the
358
+ * element first (alphabetically on ties), and within a type, nearest first
359
+ * with no-ops last and, on one node, framework handlers (React props, jQuery
360
+ * handlers) before plain listeners.
141
361
  *
362
+ * @param placed - Report entries
363
+ * @returns Entries in report order
364
+ */
365
+ function orderListeners(placed) {
366
+ const typeRank = new Map();
367
+ for (const item of placed) {
368
+ const type = item.listener.type;
369
+ typeRank.set(type, Math.min(typeRank.get(type) ?? NOOP_RANK, handlerRank(item)));
370
+ }
371
+ const rankOf = (item) => typeRank.get(item.listener.type) ?? NOOP_RANK;
372
+ return [...placed].sort((a, b) => rankOf(a) - rankOf(b) ||
373
+ a.listener.type.localeCompare(b.listener.type) ||
374
+ handlerRank(a) - handlerRank(b) ||
375
+ a.position - b.position ||
376
+ Number(!a.listener.framework) - Number(!b.listener.framework));
377
+ }
378
+ /**
379
+ * Build the listener report: React's `on…` props added, filtered to `types`
380
+ * (if given), framework roots collapsed (unless `all`), grouped by event
381
+ * type with the handlers nearest the element first (see {@link orderListeners}).
382
+ *
383
+ * @param found - Listeners per chain entry, in chain order
384
+ * @param details - Per flattened listener: handler name, jQuery handlers (missing = unknown)
385
+ * @param options - Event types to keep, whether to keep every root listener
386
+ * @param react - React prop handlers of the element and its ancestors
387
+ * @returns Ordered listeners and collapsed framework roots
388
+ */
389
+ export function buildListenerReport(found, details, options = {}, react = []) {
390
+ const { types, all } = options;
391
+ const placed = [...placeListeners(found, details), ...placeReactProps(found, react)].filter(({ listener }) => !types?.length || types.includes(listener.type));
392
+ const { kept, collapsed } = all
393
+ ? { kept: placed, collapsed: [] }
394
+ : collapseFrameworkRoots(placed);
395
+ return { listeners: orderListeners(kept).map(({ listener }) => listener), collapsed };
396
+ }
397
+ /** Order of the notes */
398
+ const DELEGATION_KINDS = ['react', 'react-root', 'jquery', 'delegated'];
399
+ /**
400
+ * How one event type reaches its handlers, if not by a listener of the element.
401
+ *
402
+ * @param type - Event type
142
403
  * @param listeners - Listener report
143
- * @returns Event types, in report order
404
+ * @param collapsed - Collapsed framework roots
405
+ * @returns Kind and handling node, undefined when the element has its own listener
406
+ */
407
+ function delegationOf(type, listeners, collapsed) {
408
+ const ofType = listeners.filter((l) => l.type === type);
409
+ const ownListener = ofType.some((l) => l.on === 'target' && !l.noop && l.framework !== 'React');
410
+ if (ownListener)
411
+ return undefined;
412
+ if (ofType.some((l) => l.framework === 'React'))
413
+ return { kind: 'react' };
414
+ const root = collapsed.find((c) => c.framework === REACT_ROOT && c.types.includes(type));
415
+ if (root)
416
+ return { kind: 'react-root', node: root.node };
417
+ const jquery = ofType.find((l) => l.framework === 'jQuery');
418
+ if (jquery)
419
+ return { kind: 'jquery', node: jquery.node };
420
+ return { kind: 'delegated' };
421
+ }
422
+ /**
423
+ * Notes for interaction event types the element has no listener of its own
424
+ * for (no-ops aside), grouped by how they reach their handlers: React `on…`
425
+ * props, a React root without props, jQuery delegation, or plain listeners
426
+ * on ancestors, document or window.
427
+ *
428
+ * @param listeners - Listener report
429
+ * @param collapsed - Collapsed framework roots
430
+ * @returns Notes, React first; none when every type has a listener on the element
431
+ */
432
+ export function delegationNotes(listeners, collapsed = []) {
433
+ const types = [
434
+ ...new Set([...listeners.map((l) => l.type), ...collapsed.flatMap((c) => c.types)]),
435
+ ].filter((type) => INTERACTION_EVENT_TYPES.has(type));
436
+ const notes = new Map();
437
+ for (const type of types) {
438
+ const delegation = delegationOf(type, listeners, collapsed);
439
+ if (!delegation)
440
+ continue;
441
+ const note = notes.get(delegation.kind) ?? {
442
+ kind: delegation.kind,
443
+ types: [],
444
+ placeholderTypes: [],
445
+ node: delegation.node,
446
+ };
447
+ note.types.push(type);
448
+ if (listeners.some((l) => l.type === type && l.on === 'target' && l.noop)) {
449
+ note.placeholderTypes.push(type);
450
+ }
451
+ notes.set(delegation.kind, note);
452
+ }
453
+ return DELEGATION_KINDS.flatMap((kind) => notes.get(kind) ?? []);
454
+ }
455
+ /**
456
+ * Event types with listeners that a `--type` value probably meant, for an
457
+ * empty result: `Click` → `click`, `onclick` → `click`.
458
+ *
459
+ * @param requested - Types asked for
460
+ * @param available - Types that have listeners
461
+ * @returns Suggestions (none when nothing is close)
144
462
  */
145
- export function delegatedOnlyTypes(listeners) {
146
- const types = [...new Set(listeners.map((listener) => listener.type))];
147
- return types.filter((type) => INTERACTION_EVENT_TYPES.has(type) &&
148
- !listeners.some((listener) => listener.type === type && listener.on === 'target'));
463
+ export function suggestEventTypes(requested, available) {
464
+ const suggestions = requested.flatMap((type) => {
465
+ const plain = type.toLowerCase().replace(/^on(?=.)/, '');
466
+ if (available.includes(plain))
467
+ return [plain];
468
+ return findSimilar(type, available, { maxDistance: 2, maxSuggestions: 1 });
469
+ });
470
+ return [...new Set(suggestions)].filter((type) => !requested.includes(type));
149
471
  }
150
472
  //# sourceMappingURL=listenerSummary.js.map
@@ -0,0 +1,41 @@
1
+ /**
2
+ * CDP events a DOM action causes, for its "what changed" report: main-frame
3
+ * navigations (with document statuses), requests and new windows.
4
+ */
5
+ import type { CDPConnection } from '../../connection/cdp.js';
6
+ /** Main-frame navigation events seen during the action */
7
+ export interface NavigationEvents {
8
+ /** Last new document committed in the main frame */
9
+ document?: {
10
+ url: string;
11
+ loaderId: string;
12
+ };
13
+ /** Last same-document URL change of the main frame */
14
+ withinDocumentUrl?: string;
15
+ /** HTTP status of documents by loader */
16
+ statusByLoader: Map<string, number>;
17
+ }
18
+ /** Live view of the CDP events an action caused */
19
+ export interface ActivityListener {
20
+ events: NavigationEvents;
21
+ /** Requests started and windows opened so far */
22
+ activity: () => {
23
+ requests: number;
24
+ opened: boolean;
25
+ };
26
+ /** Whether a main-frame load started and has not committed or stopped */
27
+ navigationPending: () => boolean;
28
+ dispose: () => void;
29
+ }
30
+ /**
31
+ * Listen for the main frame's navigations, document responses, requests
32
+ * and new windows. Events of attached child targets (out-of-process frames,
33
+ * workers) are not main-frame navigations, but their requests count.
34
+ * `Network.enable` is sent without waiting (see `withActionStability`): the
35
+ * session's network collector usually has the domain enabled already.
36
+ *
37
+ * @param cdp - CDP connection
38
+ * @returns Live listener
39
+ */
40
+ export declare function listenForActivity(cdp: CDPConnection): ActivityListener;
41
+ //# sourceMappingURL=pageActivity.d.ts.map