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