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
@@ -24,10 +24,22 @@ export declare function buildTreeFromRawNodes(rawNodes: Protocol.Accessibility.A
24
24
  * @throws Error if CDP Accessibility domain fails
25
25
  */
26
26
  export declare function collectA11yTree(): Promise<A11yTree>;
27
+ /**
28
+ * The tree with the values of secret fields masked ({@link MASKED_VALUE},
29
+ * whatever their length), and the names and values of the nodes inside them
30
+ * left out (the text Chrome lists under a text field is its value).
31
+ *
32
+ * @param tree - Accessibility tree
33
+ * @param secretIds - Node ids of secret fields
34
+ * @returns The same tree (nodes replaced in place)
35
+ */
36
+ export declare function maskSecretValues(tree: A11yTree, secretIds: ReadonlySet<string>): A11yTree;
27
37
  /**
28
38
  * Queries accessibility tree by pattern (role, name, description).
29
39
  *
30
40
  * Performs case-insensitive matching with AND logic for multiple fields.
41
+ * An element reported more than once (by the page's tree and its frame's)
42
+ * is listed once.
31
43
  *
32
44
  * @param tree - Accessibility tree to search
33
45
  * @param pattern - Query pattern with optional role, name, description
@@ -50,18 +62,21 @@ export declare function queryA11yTree(tree: A11yTree, pattern: A11yQueryPattern)
50
62
  * Parses query pattern string into A11yQueryPattern object.
51
63
  *
52
64
  * Fields are `key:value` or `key=value`, separated by spaces or commas.
53
- * Values may contain spaces (quote them if they contain `key:`-like text).
54
- * Keys: role, name, description (desc). Case-insensitive.
65
+ * Keys: role, name, description (desc). Case-insensitive. A role ends at the
66
+ * next `key:`; a name or description runs to the next role/name/description
67
+ * field with a value, or to the end, so it may contain spaces and colons
68
+ * (`name=E-mail address:`). Quote a value to end it explicitly.
55
69
  *
56
70
  * @param patternString - Query pattern string
57
71
  * @returns Parsed query pattern
58
- * @throws CommandError (81) for unknown keys
72
+ * @throws CommandError (81) for unknown keys, also a misspelled one inside a name
59
73
  *
60
74
  * @example
61
75
  * ```typescript
62
76
  * parseQueryPattern('role:button name:Submit') // { role: 'button', name: 'Submit' }
63
77
  * parseQueryPattern('role=link,name=Google Chrome') // { role: 'link', name: 'Google Chrome' }
64
78
  * parseQueryPattern('name:"Sign in" role:button') // { name: 'Sign in', role: 'button' }
79
+ * parseQueryPattern('name=E-mail address:') // { name: 'E-mail address:' }
65
80
  * ```
66
81
  */
67
82
  export declare function parseQueryPattern(patternString: string): A11yQueryPattern;
@@ -1,7 +1,11 @@
1
1
  import { CommandError } from '../errors/index.js';
2
2
  import { unknownQueryFieldError } from '../errors/messages.js';
3
3
  import { callCDP } from '../ipc/client.js';
4
+ import { MASKED_VALUE, SENSITIVE_FIELD_JS } from '../runtime/dom/elementInfo.js';
5
+ import { childFrameIds } from '../runtime/dom/frameLayout.js';
6
+ import { ConcurrencyLimiter } from '../utils/concurrency.js';
4
7
  import { EXIT_CODES } from '../utils/exitCodes.js';
8
+ import { levenshteinDistance } from '../utils/levenshtein.js';
5
9
  /**
6
10
  * Builds accessibility tree from raw CDP nodes.
7
11
  *
@@ -78,12 +82,87 @@ export async function collectA11yTree() {
78
82
  if (!result?.nodes) {
79
83
  throw new CommandError('Failed to get accessibility tree', { suggestion: 'CDP returned no nodes. The page may not be fully loaded.' }, EXIT_CODES.SOFTWARE_ERROR);
80
84
  }
81
- return buildTreeFromRawNodes([...result.nodes, ...(await collectFrameNodes(result.nodes))]);
85
+ const tree = buildTreeFromRawNodes([
86
+ ...result.nodes,
87
+ ...(await collectFrameNodes(result.nodes)),
88
+ ]);
89
+ return maskSecretValues(tree, await sensitiveNodeIds(tree));
82
90
  }
83
91
  finally {
84
92
  await callCDP('Accessibility.disable', {});
85
93
  }
86
94
  }
95
+ /** Concurrent page checks for {@link sensitiveNodeIds} */
96
+ const SENSITIVE_CHECK_CONCURRENCY = 10;
97
+ /**
98
+ * The accessibility nodes whose value is a secret: nodes with a value whose
99
+ * element is a sensitive field ({@link SENSITIVE_FIELD_JS}: passwords, card
100
+ * fields, one-time codes). Only nodes with a value are checked, one page
101
+ * call each (a page has few).
102
+ *
103
+ * @param tree - Accessibility tree
104
+ * @returns Node ids of secret fields
105
+ */
106
+ async function sensitiveNodeIds(tree) {
107
+ const candidates = [...tree.nodes.values()].filter((node) => node.value !== undefined && node.backendDOMNodeId !== undefined);
108
+ const limiter = new ConcurrencyLimiter(SENSITIVE_CHECK_CONCURRENCY);
109
+ const checked = await Promise.all(candidates.map((node) => limiter.run(async () => (await isSensitiveField(node.backendDOMNodeId ?? 0)) ? node.nodeId : null)));
110
+ return new Set(checked.filter((id) => id !== null));
111
+ }
112
+ /**
113
+ * Whether an element is a sensitive field. An element that cannot be read
114
+ * counts as one, so a value is never shown by mistake.
115
+ *
116
+ * @param backendNodeId - The element
117
+ * @returns True for secret fields (and unreadable elements)
118
+ */
119
+ async function isSensitiveField(backendNodeId) {
120
+ const objectGroup = 'bdg-a11y-secret';
121
+ const resolved = await callCDP('DOM.resolveNode', { backendNodeId, objectGroup });
122
+ const objectId = resolved.data?.result?.object
123
+ ?.objectId;
124
+ if (!objectId)
125
+ return true;
126
+ try {
127
+ const response = await callCDP('Runtime.callFunctionOn', {
128
+ objectId,
129
+ functionDeclaration: `function () { return this.nodeType !== 1 || (${SENSITIVE_FIELD_JS})(this); }`,
130
+ returnByValue: true,
131
+ });
132
+ const value = response.data?.result?.result
133
+ ?.value;
134
+ return value !== false;
135
+ }
136
+ finally {
137
+ await callCDP('Runtime.releaseObjectGroup', { objectGroup });
138
+ }
139
+ }
140
+ /**
141
+ * The tree with the values of secret fields masked ({@link MASKED_VALUE},
142
+ * whatever their length), and the names and values of the nodes inside them
143
+ * left out (the text Chrome lists under a text field is its value).
144
+ *
145
+ * @param tree - Accessibility tree
146
+ * @param secretIds - Node ids of secret fields
147
+ * @returns The same tree (nodes replaced in place)
148
+ */
149
+ export function maskSecretValues(tree, secretIds) {
150
+ const hide = (nodeId, inside) => {
151
+ const node = tree.nodes.get(nodeId);
152
+ if (!node)
153
+ return;
154
+ const { name: _name, value: _value, ...rest } = node;
155
+ const masked = inside
156
+ ? rest
157
+ : { ...node, ...(node.value !== undefined && { value: MASKED_VALUE }) };
158
+ tree.nodes.set(nodeId, masked);
159
+ if (tree.root.nodeId === nodeId)
160
+ tree.root = masked;
161
+ node.childIds?.forEach((childId) => hide(childId, true));
162
+ };
163
+ secretIds.forEach((nodeId) => hide(nodeId, false));
164
+ return tree;
165
+ }
87
166
  /**
88
167
  * Accessibility nodes of the page's iframes, attached under their `Iframe`
89
168
  * node (the page's own tree stops there). Frames in another process
@@ -98,22 +177,13 @@ async function collectFrameNodes(pageNodes) {
98
177
  const tree = (await callCDP('Page.getFrameTree', {})).data?.result;
99
178
  const known = [...pageNodes];
100
179
  const collected = [];
101
- for (const [index, frameId] of childFrames(tree?.frameTree).entries()) {
180
+ for (const [index, frameId] of childFrameIds(tree?.frameTree).entries()) {
102
181
  const nodes = await frameNodes(frameId, `f${index}:`, known);
103
182
  known.push(...nodes);
104
183
  collected.push(...nodes);
105
184
  }
106
185
  return collected;
107
186
  }
108
- /**
109
- * Ids of all frames below a frame tree's root.
110
- *
111
- * @param tree - Frame tree
112
- * @returns Frame ids, depth-first
113
- */
114
- function childFrames(tree) {
115
- return (tree?.childFrames ?? []).flatMap((child) => [child.frame.id, ...childFrames(child)]);
116
- }
117
187
  /**
118
188
  * One frame's nodes, with ids prefixed, its root attached to the node of its
119
189
  * `<iframe>` element.
@@ -234,6 +304,8 @@ const TEXT_ROLES = new Set(['statictext', 'inlinetextbox']);
234
304
  * Queries accessibility tree by pattern (role, name, description).
235
305
  *
236
306
  * Performs case-insensitive matching with AND logic for multiple fields.
307
+ * An element reported more than once (by the page's tree and its frame's)
308
+ * is listed once.
237
309
  *
238
310
  * @param tree - Accessibility tree to search
239
311
  * @param pattern - Query pattern with optional role, name, description
@@ -253,13 +325,19 @@ const TEXT_ROLES = new Set(['statictext', 'inlinetextbox']);
253
325
  */
254
326
  export function queryA11yTree(tree, pattern) {
255
327
  const matches = [];
328
+ const seenElements = new Set();
256
329
  const wantsText = pattern.role !== undefined && TEXT_ROLES.has(pattern.role.toLowerCase());
257
330
  for (const node of tree.nodes.values()) {
258
331
  if (!wantsText && TEXT_ROLES.has(node.role.toLowerCase()))
259
332
  continue;
260
- if (matchesPattern(node, pattern)) {
261
- matches.push(node);
333
+ if (!matchesPattern(node, pattern))
334
+ continue;
335
+ if (node.backendDOMNodeId !== undefined) {
336
+ if (seenElements.has(node.backendDOMNodeId))
337
+ continue;
338
+ seenElements.add(node.backendDOMNodeId);
262
339
  }
340
+ matches.push(node);
263
341
  }
264
342
  return {
265
343
  nodes: matches,
@@ -317,47 +395,110 @@ const QUERY_FIELDS = {
317
395
  description: 'description',
318
396
  desc: 'description',
319
397
  };
398
+ /** The next `key:`/`key=` anywhere (fields may follow other text). */
399
+ const QUERY_KEY = /([a-z]+)\s*[:=]/i;
400
+ /** A quoted value (it may contain anything but its own quote). */
401
+ const QUOTED_QUERY_VALUE = /^\s*("[^"]*"|'[^']*')/;
402
+ /** Where a role value ends: at the next `key:` after a space or comma. */
403
+ const NEXT_QUERY_FIELD = /[\s,]+[a-z]+\s*[:=]/i;
320
404
  /**
321
- * One `key:value` (or `key=value`) field: the value is quoted, or runs until
322
- * the next `key:`/`key=` after a space or comma.
405
+ * Where a name or description ends: only at a known field that has a value,
406
+ * so names keep their spaces and colons (`name=E-mail address:`).
323
407
  */
324
- const QUERY_FIELD = /([a-z]+)\s*[:=](?:\s*("[^"]*"|'[^']*')|((?:(?![\s,]+[a-z]+\s*[:=]).)*))/gis;
408
+ const NEXT_KNOWN_QUERY_FIELD = /[\s,]+(?:role|name|description|desc)\s*[:=](?=\s*[^\s,])/i;
409
+ /**
410
+ * Read one field value.
411
+ *
412
+ * @param text - Text after `key:`
413
+ * @param field - Field the value is for
414
+ * @returns The value, where it ends in `text`, and whether it was quoted
415
+ */
416
+ function readQueryValue(text, field) {
417
+ const quoted = QUOTED_QUERY_VALUE.exec(text);
418
+ if (quoted?.[1])
419
+ return { value: quoted[1].slice(1, -1), end: quoted[0].length, quoted: true };
420
+ const next = (field === 'role' ? NEXT_QUERY_FIELD : NEXT_KNOWN_QUERY_FIELD).exec(text);
421
+ const end = next ? next.index : text.length;
422
+ const value = text
423
+ .slice(0, end)
424
+ .trim()
425
+ .replace(/,+$/, '')
426
+ .replace(/^(["'])(.*)\1$/s, '$2');
427
+ return { value, end, quoted: false };
428
+ }
325
429
  /**
326
430
  * Parses query pattern string into A11yQueryPattern object.
327
431
  *
328
432
  * Fields are `key:value` or `key=value`, separated by spaces or commas.
329
- * Values may contain spaces (quote them if they contain `key:`-like text).
330
- * Keys: role, name, description (desc). Case-insensitive.
433
+ * Keys: role, name, description (desc). Case-insensitive. A role ends at the
434
+ * next `key:`; a name or description runs to the next role/name/description
435
+ * field with a value, or to the end, so it may contain spaces and colons
436
+ * (`name=E-mail address:`). Quote a value to end it explicitly.
331
437
  *
332
438
  * @param patternString - Query pattern string
333
439
  * @returns Parsed query pattern
334
- * @throws CommandError (81) for unknown keys
440
+ * @throws CommandError (81) for unknown keys, also a misspelled one inside a name
335
441
  *
336
442
  * @example
337
443
  * ```typescript
338
444
  * parseQueryPattern('role:button name:Submit') // { role: 'button', name: 'Submit' }
339
445
  * parseQueryPattern('role=link,name=Google Chrome') // { role: 'link', name: 'Google Chrome' }
340
446
  * parseQueryPattern('name:"Sign in" role:button') // { name: 'Sign in', role: 'button' }
447
+ * parseQueryPattern('name=E-mail address:') // { name: 'E-mail address:' }
341
448
  * ```
342
449
  */
343
450
  export function parseQueryPattern(patternString) {
344
451
  const pattern = {};
345
- for (const [, rawKey = '', quoted, plain = ''] of patternString.matchAll(QUERY_FIELD)) {
346
- const rawValue = quoted ?? plain;
452
+ let rest = patternString;
453
+ for (let key = QUERY_KEY.exec(rest); key; key = QUERY_KEY.exec(rest)) {
454
+ const rawKey = key[1] ?? '';
347
455
  const field = QUERY_FIELDS[rawKey.toLowerCase()];
348
- if (!field) {
349
- const err = unknownQueryFieldError(rawKey);
350
- throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.INVALID_ARGUMENTS);
351
- }
352
- const value = rawValue
353
- .trim()
354
- .replace(/,+$/, '')
355
- .replace(/^(["'])(.*)\1$/s, '$2');
456
+ if (!field)
457
+ throwUnknownField(rawKey);
458
+ const valueText = rest.slice(key.index + key[0].length);
459
+ const { value, end, quoted } = readQueryValue(valueText, field);
460
+ if (!quoted && field !== 'role')
461
+ checkMisspelledField(value, field);
356
462
  if (value)
357
463
  pattern[field] = value;
464
+ rest = valueText.slice(end);
358
465
  }
359
466
  return pattern;
360
467
  }
468
+ /**
469
+ * Throw the exit-81 error for an unknown query field.
470
+ *
471
+ * @param field - The unrecognized key
472
+ * @param similar - A known field it looks like a typo of
473
+ * @param value - The name or description that absorbed it
474
+ * @throws CommandError (81)
475
+ */
476
+ function throwUnknownField(field, similar, value) {
477
+ const err = unknownQueryFieldError(field, similar, value);
478
+ throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.INVALID_ARGUMENTS);
479
+ }
480
+ /** A `word:`/`word=` inside a name or description. */
481
+ const ABSORBED_KEY = /[\s,]([a-z]+)\s*[:=]/gi;
482
+ /**
483
+ * Reject a name or description that swallowed a misspelled field
484
+ * (`name:Save rol:button`): a `word:` within edit distance 1 of role, name or
485
+ * desc (2 of description). Wider distances would catch common label words
486
+ * (`Date:`, `Note:`, `Code:`).
487
+ *
488
+ * @param value - Unquoted name or description
489
+ * @param field - Field it is the value of
490
+ * @throws CommandError (81) with a "did you mean" suggestion
491
+ */
492
+ function checkMisspelledField(value, field) {
493
+ for (const [, word = ''] of value.matchAll(ABSORBED_KEY)) {
494
+ const lower = word.toLowerCase();
495
+ if (QUERY_FIELDS[lower])
496
+ continue;
497
+ const similar = Object.keys(QUERY_FIELDS).find((name) => levenshteinDistance(lower, name) <= (name.length > 4 ? 2 : 1));
498
+ if (similar)
499
+ throwUnknownField(word, similar, `${field}=${value}`);
500
+ }
501
+ }
361
502
  /**
362
503
  * Resolve accessibility properties for a DOM node via IPC.
363
504
  *
@@ -2,6 +2,7 @@
2
2
  * Console message collection via CDP Runtime and Log domains.
3
3
  *
4
4
  * Captures console.log, console.error, etc. and JavaScript exceptions
5
+ * (unhandled rejections removed again when a handler is attached later)
5
6
  * with automatic nested object expansion, browser messages (failed loads,
6
7
  * CORS, security, deprecations), and the same from cross-origin iframes and
7
8
  * workers.
@@ -2,6 +2,7 @@
2
2
  * Console message collection via CDP Runtime and Log domains.
3
3
  *
4
4
  * Captures console.log, console.error, etc. and JavaScript exceptions
5
+ * (unhandled rejections removed again when a handler is attached later)
5
6
  * with automatic nested object expansion, browser messages (failed loads,
6
7
  * CORS, security, deprecations), and the same from cross-origin iframes and
7
8
  * workers.
@@ -76,14 +77,17 @@ function createMessage(type, text, timestamp, args, context) {
76
77
  /**
77
78
  * Insert a message in timestamp order.
78
79
  * Messages are kept sorted by timestamp to handle async expansion delays.
80
+ *
81
+ * @returns False when the message limit is reached (the message is dropped)
79
82
  */
80
83
  function insertMessageByTimestamp(messages, message) {
81
84
  if (messages.length >= MAX_CONSOLE_MESSAGES) {
82
85
  log.debug(`Warning: Console message limit reached (${MAX_CONSOLE_MESSAGES})`);
83
- return;
86
+ return false;
84
87
  }
85
88
  const insertIndex = findInsertIndex(messages, message.timestamp);
86
89
  messages.splice(insertIndex, 0, message);
90
+ return true;
87
91
  }
88
92
  /**
89
93
  * Find the correct insertion index to maintain timestamp order.
@@ -171,15 +175,93 @@ export function formatExceptionText(details) {
171
175
  }
172
176
  /**
173
177
  * Handle an exception thrown event.
178
+ *
179
+ * @returns The message added, undefined when filtered out or over the limit
174
180
  */
175
181
  function handleExceptionThrown(messages, params, context, includeAll) {
176
182
  const exception = params.exceptionDetails;
177
183
  const text = formatExceptionText(exception);
178
184
  if (shouldExcludeConsoleMessage(text, 'error', includeAll)) {
179
- return;
185
+ return undefined;
180
186
  }
181
187
  const message = createMessage('error', text, params.timestamp, undefined, context);
182
- insertMessageByTimestamp(messages, message);
188
+ return insertMessageByTimestamp(messages, message) ? message : undefined;
189
+ }
190
+ /** CDP's text of an unhandled promise rejection (the only kind Chrome revokes) */
191
+ const UNHANDLED_REJECTION_TEXT = 'Uncaught (in promise)';
192
+ /**
193
+ * Unhandled promise rejections that may still be revoked: Chrome reports a
194
+ * rejection without a handler at the end of the task, and revokes the
195
+ * report (`Runtime.exceptionRevoked`) when a handler is attached later, as
196
+ * DevTools does by removing the message. `bdg dom eval` attaches its
197
+ * handler only after the evaluation returns, so this is what keeps
198
+ * `bdg dom eval 'Promise.reject(…)'` out of the console; a rejection
199
+ * nothing ever handles stays.
200
+ */
201
+ class RevocableRejections {
202
+ /** Messages of rejections, by session and exception id, oldest first */
203
+ pending = new Map();
204
+ /**
205
+ * Remember a reported exception if it is an unhandled rejection. Past
206
+ * {@link MAX_CONSOLE_MESSAGES} rejections, the oldest is forgotten.
207
+ *
208
+ * @param details - Exception details
209
+ * @param message - Its console message
210
+ * @param sessionId - Session it was reported on (undefined: the page)
211
+ */
212
+ track(details, message, sessionId) {
213
+ if (!details.text.startsWith(UNHANDLED_REJECTION_TEXT))
214
+ return;
215
+ this.pending.set(this.key(details.exceptionId, sessionId), message);
216
+ if (this.pending.size <= MAX_CONSOLE_MESSAGES)
217
+ return;
218
+ const [oldest] = this.pending.keys();
219
+ if (oldest !== undefined)
220
+ this.pending.delete(oldest);
221
+ }
222
+ /**
223
+ * Remove the message of a revoked rejection.
224
+ *
225
+ * @param messages - Console messages (updated)
226
+ * @param exceptionId - Revoked exception
227
+ * @param sessionId - Session it was revoked on (undefined: the page)
228
+ */
229
+ revoke(messages, exceptionId, sessionId) {
230
+ const key = this.key(exceptionId, sessionId);
231
+ const message = this.pending.get(key);
232
+ this.pending.delete(key);
233
+ const index = message ? messages.indexOf(message) : -1;
234
+ if (index !== -1)
235
+ messages.splice(index, 1);
236
+ }
237
+ /**
238
+ * Forget the rejections of a session whose documents are gone (it
239
+ * detached, or its contexts were cleared by a navigation): they can no
240
+ * longer be revoked.
241
+ *
242
+ * @param sessionId - Session (undefined: the page)
243
+ */
244
+ forgetSession(sessionId) {
245
+ const prefix = this.key('', sessionId);
246
+ for (const key of this.pending.keys()) {
247
+ if (key.startsWith(prefix))
248
+ this.pending.delete(key);
249
+ }
250
+ }
251
+ /** Forget every rejection (the collection stopped). */
252
+ clear() {
253
+ this.pending.clear();
254
+ }
255
+ /**
256
+ * Map key of an exception: ids are counted per session.
257
+ *
258
+ * @param exceptionId - Exception id ('' for the session's prefix)
259
+ * @param sessionId - Session
260
+ * @returns Key
261
+ */
262
+ key(exceptionId, sessionId) {
263
+ return `${sessionId ?? ''}:${exceptionId}`;
264
+ }
183
265
  }
184
266
  /**
185
267
  * Handle a browser log entry (failed resource loads, CORS, security,
@@ -255,12 +337,24 @@ export async function startConsoleCollection(cdp, messages, includeAll = false,
255
337
  };
256
338
  handleConsoleAPICall(senderFor(cdp, sessionId), messages, params, context, includeAll);
257
339
  });
258
- registry.registerTyped(typed, 'Runtime.exceptionThrown', (params) => {
340
+ const rejections = new RevocableRejections();
341
+ registry.registerTyped(typed, 'Runtime.exceptionThrown', (params, sessionId) => {
259
342
  const context = {
260
343
  navigationId: getCurrentNavigationId?.(),
261
344
  stackTrace: convertStackTrace(params.exceptionDetails.stackTrace),
262
345
  };
263
- handleExceptionThrown(messages, params, context, includeAll);
346
+ const message = handleExceptionThrown(messages, params, context, includeAll);
347
+ if (message)
348
+ rejections.track(params.exceptionDetails, message, sessionId);
349
+ });
350
+ registry.registerTyped(typed, 'Runtime.exceptionRevoked', ({ exceptionId }, sessionId) => {
351
+ rejections.revoke(messages, exceptionId, sessionId);
352
+ });
353
+ registry.registerTyped(typed, 'Runtime.executionContextsCleared', (_params, sessionId) => {
354
+ rejections.forgetSession(sessionId);
355
+ });
356
+ registry.registerTyped(typed, 'Target.detachedFromTarget', ({ sessionId }) => {
357
+ rejections.forgetSession(sessionId);
264
358
  });
265
359
  registry.registerTyped(typed, 'Log.entryAdded', ({ entry }) => {
266
360
  handleLogEntry(messages, entry, getCurrentNavigationId?.(), includeAll);
@@ -275,6 +369,7 @@ export async function startConsoleCollection(cdp, messages, includeAll = false,
275
369
  const detachChildren = await startOptionalSources(cdp, typed);
276
370
  return async () => {
277
371
  registry.cleanup();
372
+ rejections.clear();
278
373
  await detachChildren();
279
374
  };
280
375
  }
@@ -110,7 +110,7 @@ export function skippedBodyReason(body) {
110
110
  * @param resourceType - Resource type reported with the response
111
111
  */
112
112
  function applyResponse(request, response, resourceType) {
113
- const { status, mimeType, headers, timing, remoteIPAddress, connectionId } = response;
113
+ const { status, mimeType, headers, timing, remoteIPAddress, remotePort, connectionId } = response;
114
114
  request.status = status;
115
115
  if (response.statusText)
116
116
  request.statusText = response.statusText;
@@ -138,6 +138,8 @@ function applyResponse(request, response, resourceType) {
138
138
  }
139
139
  if (remoteIPAddress)
140
140
  request.serverIPAddress = remoteIPAddress;
141
+ if (remotePort)
142
+ request.serverPort = remotePort;
141
143
  if (connectionId !== undefined)
142
144
  request.connection = String(connectionId);
143
145
  }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Which requests an action is about (pages, API calls, sockets) and which
3
+ * are static assets loaded along the way (stylesheets, scripts, fonts,
4
+ * images), by CDP resource type.
5
+ */
6
+ /** Fields a request needs to be classified */
7
+ interface ClassifiedRequest {
8
+ method?: string | undefined;
9
+ resourceType?: string | undefined;
10
+ status?: number | undefined;
11
+ failed?: true | undefined;
12
+ }
13
+ /**
14
+ * Whether a request is worth its own line after an action: one of the
15
+ * {@link ACTIVITY_RESOURCE_TYPES}, one of unknown type, any request that is
16
+ * not a GET (a `sendBeacon` POST typed `Other`), or an asset that failed (an
17
+ * HTTP error or no response).
18
+ *
19
+ * @param request - Request with its method and CDP resource type
20
+ * @returns False for assets fetched with GET that loaded normally
21
+ */
22
+ export declare function isNotableRequest(request: ClassifiedRequest): boolean;
23
+ /**
24
+ * Short names of the asset types among requests, e.g. `["css", "js", "images"]`
25
+ * (types without a short name are lowercased, after the known ones).
26
+ *
27
+ * @param requests - Asset requests
28
+ * @returns Distinct names, known types first
29
+ */
30
+ export declare function assetTypeNames(requests: ClassifiedRequest[]): string[];
31
+ export {};
32
+ //# sourceMappingURL=requestKinds.d.ts.map
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Which requests an action is about (pages, API calls, sockets) and which
3
+ * are static assets loaded along the way (stylesheets, scripts, fonts,
4
+ * images), by CDP resource type.
5
+ */
6
+ /**
7
+ * Resource types listed one by one after an action: pages, API calls,
8
+ * sockets, and the requests a page sends about itself (pings, beacons,
9
+ * preflights, CSP reports)
10
+ */
11
+ const ACTIVITY_RESOURCE_TYPES = new Set([
12
+ 'Document',
13
+ 'XHR',
14
+ 'Fetch',
15
+ 'WebSocket',
16
+ 'EventSource',
17
+ 'Ping',
18
+ 'Preflight',
19
+ 'CSPViolationReport',
20
+ ]);
21
+ /** Short names of asset types, in the order summaries list them */
22
+ const ASSET_TYPE_NAMES = [
23
+ ['Stylesheet', 'css'],
24
+ ['Script', 'js'],
25
+ ['Font', 'fonts'],
26
+ ['Image', 'images'],
27
+ ['Media', 'media'],
28
+ ];
29
+ /**
30
+ * Whether a request is worth its own line after an action: one of the
31
+ * {@link ACTIVITY_RESOURCE_TYPES}, one of unknown type, any request that is
32
+ * not a GET (a `sendBeacon` POST typed `Other`), or an asset that failed (an
33
+ * HTTP error or no response).
34
+ *
35
+ * @param request - Request with its method and CDP resource type
36
+ * @returns False for assets fetched with GET that loaded normally
37
+ */
38
+ export function isNotableRequest(request) {
39
+ if (request.resourceType === undefined || ACTIVITY_RESOURCE_TYPES.has(request.resourceType)) {
40
+ return true;
41
+ }
42
+ if (request.method !== undefined && request.method.toUpperCase() !== 'GET')
43
+ return true;
44
+ return request.failed === true || (request.status ?? 0) >= 400;
45
+ }
46
+ /**
47
+ * Short names of the asset types among requests, e.g. `["css", "js", "images"]`
48
+ * (types without a short name are lowercased, after the known ones).
49
+ *
50
+ * @param requests - Asset requests
51
+ * @returns Distinct names, known types first
52
+ */
53
+ export function assetTypeNames(requests) {
54
+ const types = new Set(requests.map((request) => request.resourceType ?? 'Other'));
55
+ const known = ASSET_TYPE_NAMES.filter(([type]) => types.has(type)).map(([, name]) => name);
56
+ const others = [...types]
57
+ .filter((type) => !ASSET_TYPE_NAMES.some(([known]) => known === type))
58
+ .map((type) => type.toLowerCase());
59
+ return [...known, ...others];
60
+ }
61
+ //# sourceMappingURL=requestKinds.js.map
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Lifecycle state of captured network requests.
3
+ *
4
+ * A request is pending until it completes or fails. Status 0 means it failed
5
+ * without any response (DNS, refused, aborted, blocked); a request that got a
6
+ * response keeps its HTTP status even if loading failed afterwards.
7
+ */
8
+ import type { NetworkRequest } from '../types.js';
9
+ /** Lifecycle state of a captured request. */
10
+ export type RequestState = 'pending' | 'failed' | 'complete';
11
+ /**
12
+ * Classify a request.
13
+ *
14
+ * @param request - Captured request
15
+ * @returns Its lifecycle state
16
+ */
17
+ export declare function getRequestState(request: Pick<NetworkRequest, 'status'>): RequestState;
18
+ /**
19
+ * Why a request failed, leaving out Chrome stopping an unneeded body after a
20
+ * complete response (204/304 report `net::ERR_ABORTED` that way).
21
+ *
22
+ * @param request - Captured request
23
+ * @returns Reason, or undefined when there is none worth showing
24
+ */
25
+ export declare function failureReason(request: Pick<NetworkRequest, 'status' | 'errorText'>): string | undefined;
26
+ /**
27
+ * Error Chrome reports when it stops reading a response body nobody needs
28
+ * (the response itself arrived; not a failure of the request).
29
+ */
30
+ export declare const BODY_ABORTED = "net::ERR_ABORTED";
31
+ //# sourceMappingURL=requestState.d.ts.map