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
@@ -1,16 +1,25 @@
1
- import { CDPConnectionError } from '../../connection/errors.js';
1
+ import { CDPConnectionError, CDPProtocolError } from '../../connection/errors.js';
2
2
  import { PatternDetector } from '../patternDetector.js';
3
3
  import { createInteractionRunner } from './interactions.js';
4
+ import { withTriggeredRequestCount } from './triggeredRequests.js';
4
5
  import { CommandError } from '../../errors/index.js';
6
+ import { cdpCallError, formDiscoveryFailedError } from '../../errors/messages.js';
5
7
  import { evaluateScript, withBusyPageRecovery } from '../../runtime/dom/evalHelpers.js';
6
8
  import { inspectEventListeners } from '../../runtime/dom/eventListeners.js';
7
9
  import { FORM_DISCOVERY_SCRIPT, isRawFormData } from '../../runtime/dom/formDiscovery.js';
8
- import { fillElement, clickElement, pressKeyElement, scrollPage, waitForActionStability, } from '../../runtime/dom/formFillHelpers/index.js';
10
+ import { fillElement, clickElement, pressKeyElement, scrollPage, withActionStability, } from '../../runtime/dom/formFillHelpers/index.js';
11
+ import { exceptionSummary } from '../../runtime/dom/formFillHelpers/shared.js';
9
12
  import { submitForm } from '../../runtime/dom/formSubmitHelpers.js';
10
13
  import { evaluateInFrame, listFrames } from '../../runtime/dom/frames.js';
11
- import { resolveScriptTarget, withUserSelector } from '../../runtime/dom/targetNode.js';
14
+ import { inspectElement } from '../../runtime/dom/inspect.js';
15
+ import { inspectLayout } from '../../runtime/dom/layout.js';
16
+ import { onScriptTarget } from '../../runtime/dom/targetNode.js';
17
+ import { waitForCondition } from '../../runtime/dom/wait.js';
18
+ import { emulatePage, pageAppearance } from '../../runtime/page/emulation.js';
19
+ import { readDocumentReadyState } from '../../runtime/page/loadingState.js';
12
20
  import { navigatePage } from '../../runtime/page/navigation.js';
13
21
  import { skippedBodyReason } from '../../telemetry/network.js';
22
+ import { sessionCommand } from '../../ui/messages/sessionCommand.js';
14
23
  import { EXIT_CODES } from '../../utils/exitCodes.js';
15
24
  import { filterDefined } from '../../utils/objects.js';
16
25
  import { VERSION } from '../../utils/version.js';
@@ -54,6 +63,8 @@ function mapNetworkRequestToPreview(req, withHeaders = false) {
54
63
  return filterDefined({
55
64
  requestId: req.requestId,
56
65
  timestamp: req.timestamp,
66
+ sentTime: req.sentTime,
67
+ navigationId: req.navigationId,
57
68
  method: req.method,
58
69
  url: req.url,
59
70
  status: req.status,
@@ -169,7 +180,7 @@ function findNetworkRequestOrThrow(store, id) {
169
180
  store.pendingNetworkRequests.get(id)?.request ??
170
181
  (webSocket && webSocketAsRequest(webSocket));
171
182
  if (!request) {
172
- throw new CommandError(`Network request not found: ${id}`, { suggestion: 'List request ids with: bdg network list' }, EXIT_CODES.RESOURCE_NOT_FOUND);
183
+ throw new CommandError(`Network request not found: ${id}`, { suggestion: `List request ids with: ${sessionCommand('bdg network list')}` }, EXIT_CODES.RESOURCE_NOT_FOUND);
173
184
  }
174
185
  return request;
175
186
  }
@@ -183,13 +194,13 @@ function findNetworkRequestOrThrow(store, id) {
183
194
  */
184
195
  function findConsoleMessageOrThrow(messages, indexStr) {
185
196
  if (!/^\d+$/.test(indexStr)) {
186
- throw new CommandError(`Invalid console message index: ${indexStr}`, { suggestion: 'Use a 0-based index from: bdg console --list' }, EXIT_CODES.INVALID_ARGUMENTS);
197
+ throw new CommandError(`Invalid console message index: ${indexStr}`, { suggestion: `Use a 0-based index from: ${sessionCommand('bdg console --list')}` }, EXIT_CODES.INVALID_ARGUMENTS);
187
198
  }
188
199
  const index = parseInt(indexStr, 10);
189
200
  if (index >= messages.length) {
190
201
  throw new CommandError(messages.length === 0
191
202
  ? `Console message not found at index: ${indexStr} (no messages captured yet)`
192
- : `Console message not found at index: ${indexStr} (available: 0-${messages.length - 1})`, { suggestion: 'List messages with: bdg console --list' }, EXIT_CODES.RESOURCE_NOT_FOUND);
203
+ : `Console message not found at index: ${indexStr} (available: 0-${messages.length - 1})`, { suggestion: `List messages with: ${sessionCommand('bdg console --list')}` }, EXIT_CODES.RESOURCE_NOT_FOUND);
193
204
  }
194
205
  const message = messages[index];
195
206
  if (!message) {
@@ -252,9 +263,45 @@ function pageWebSocketUrl(store) {
252
263
  throw new CDPConnectionError('No page target');
253
264
  return url;
254
265
  }
255
- export function createCommandRegistry(store) {
266
+ /** Chrome's "server error" code, used for failures on the page's state (a missing node, a bad id…) */
267
+ const CDP_SERVER_ERROR = -32000;
268
+ /**
269
+ * A `bdg cdp` failure that is the caller's: wrong parameters (81), or an
270
+ * id of a node, target or frame that does not exist (83). Other failures
271
+ * (internal errors, a detached page) stay software errors.
272
+ *
273
+ * @param method - CDP method
274
+ * @param error - What the call threw
275
+ * @returns The error to report, or undefined to keep the original
276
+ */
277
+ function callerError(method, error) {
278
+ if (!(error instanceof CDPProtocolError))
279
+ return undefined;
280
+ const err = cdpCallError(method, error.message);
281
+ if (error.isRequestError()) {
282
+ return new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.INVALID_ARGUMENTS);
283
+ }
284
+ if (error.code === CDP_SERVER_ERROR && err.notFound) {
285
+ return new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.RESOURCE_NOT_FOUND);
286
+ }
287
+ if (error.code === CDP_SERVER_ERROR &&
288
+ /must be specified|invalid|expected|missing/i.test(error.message)) {
289
+ return new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.INVALID_ARGUMENTS);
290
+ }
291
+ return undefined;
292
+ }
293
+ /**
294
+ * The handlers of the session commands.
295
+ *
296
+ * @param store - Telemetry of the session
297
+ * @param emulation - The session's page emulation
298
+ * @returns Command registry
299
+ */
300
+ export function createCommandRegistry(store, emulation) {
256
301
  const patternDetector = new PatternDetector();
257
302
  const interact = createInteractionRunner(store);
303
+ /** Frame id behind each index of the last `dom frames` listing */
304
+ let listedFrameIds;
258
305
  return {
259
306
  session_peek: async (_cdp, params) => {
260
307
  const lastN = calculateLastN(params.lastN);
@@ -304,7 +351,7 @@ export function createCommandRegistry(store) {
304
351
  }
305
352
  return Promise.reject(new Error(`Unknown itemType: ${String(params.itemType)}. Expected 'network' or 'console'.`));
306
353
  },
307
- session_status: async (_cdp, _params) => {
354
+ session_status: async (cdp, _params) => {
308
355
  const duration = Date.now() - store.sessionStartTime;
309
356
  const lastNetworkRequest = store.networkRequests[store.networkRequests.length - 1];
310
357
  const lastConsoleMessage = store.consoleMessages[store.consoleMessages.length - 1];
@@ -314,6 +361,7 @@ export function createCommandRegistry(store) {
314
361
  target: {
315
362
  url: store.targetInfo?.url ?? '',
316
363
  title: store.targetInfo?.title ?? '',
364
+ ...(await pageAppearance(cdp)),
317
365
  },
318
366
  activeTelemetry: store.activeTelemetry,
319
367
  activity: filterDefined({
@@ -324,7 +372,7 @@ export function createCommandRegistry(store) {
324
372
  }),
325
373
  navigationId: store.getCurrentNavigationId?.() ?? 0,
326
374
  };
327
- return Promise.resolve(result);
375
+ return result;
328
376
  },
329
377
  session_har_data: async (_cdp, _params) => {
330
378
  return Promise.resolve({
@@ -342,12 +390,18 @@ export function createCommandRegistry(store) {
342
390
  return Promise.resolve({
343
391
  url: targetRequest.url,
344
392
  requestId: targetRequest.requestId,
393
+ method: targetRequest.method,
394
+ ...(targetRequest.status !== undefined && { status: targetRequest.status }),
395
+ ...(targetRequest.statusText && { statusText: targetRequest.statusText }),
396
+ ...(targetRequest.errorText && { errorText: targetRequest.errorText }),
345
397
  requestHeaders,
346
398
  responseHeaders,
347
399
  });
348
400
  },
349
401
  cdp_call: async (cdp, params) => {
350
- const result = await withBusyPageRecovery(cdp, cdp.send(params.method, params.params ?? {}));
402
+ const result = await withBusyPageRecovery(cdp, cdp.send(params.method, params.params ?? {}).catch((error) => {
403
+ throw callerError(params.method, error) ?? error;
404
+ }));
351
405
  const detectionResult = patternDetector.trackCommand(params.method);
352
406
  let hint;
353
407
  if (detectionResult.shouldShow && detectionResult.pattern) {
@@ -363,68 +417,57 @@ export function createCommandRegistry(store) {
363
417
  },
364
418
  dom_eval: async (cdp, params) => params.frame === undefined
365
419
  ? evaluateScript(cdp, params.script)
366
- : evaluateInFrame(cdp, pageWebSocketUrl(store), params.script, params.frame),
367
- dom_frames: async (cdp) => ({ frames: await listFrames(cdp, pageWebSocketUrl(store)) }),
368
- dom_fill: async (cdp, params) => interact(cdp, async () => {
369
- const target = await resolveScriptTarget(cdp, params);
370
- const fillOptions = filterDefined({
371
- index: target.index,
372
- blur: params.blur,
373
- cwd: params.cwd,
374
- });
375
- const result = withUserSelector(await fillElement(cdp, target.selector, params.value, fillOptions), params.selector);
376
- if (result.success && params.wait !== false)
377
- await waitForActionStability(cdp);
378
- return result;
379
- }),
380
- dom_click: async (cdp, params) => interact(cdp, async () => {
381
- const target = await resolveScriptTarget(cdp, params);
382
- const clickOptions = filterDefined({ index: target.index, action: params.action });
383
- const result = withUserSelector(await clickElement(cdp, target.selector, clickOptions), params.selector);
384
- if (result.success && params.wait !== false)
385
- await waitForActionStability(cdp);
386
- return result;
420
+ : evaluateInFrame(cdp, pageWebSocketUrl(store), params.script, params.frame, listedFrameIds),
421
+ dom_frames: async (cdp) => {
422
+ const { frames, frameIds } = await listFrames(cdp, pageWebSocketUrl(store));
423
+ listedFrameIds = frameIds;
424
+ return { frames };
425
+ },
426
+ dom_fill: async (cdp, params) => interact(cdp, async () => onScriptTarget(cdp, params, (target) => withActionStability(cdp, () => fillElement(target.cdp, target.selector, params.value, filterDefined({ index: target.index, blur: params.blur, cwd: params.cwd })), params.wait !== false))),
427
+ dom_click: async (cdp, params) => interact(cdp, async () => onScriptTarget(cdp, params, (target) => withActionStability(cdp, () => clickElement(target.cdp, target.selector, filterDefined({
428
+ index: target.index,
429
+ action: params.action,
430
+ strict: params.strict,
431
+ })), params.wait !== false)), {
432
+ detectNoEffect: params.wait !== false && params.action !== 'hover' && params.action !== 'right',
433
+ reportShown: params.action === 'hover',
434
+ detectUnsettled: params.wait !== false && params.action !== 'hover',
387
435
  }),
388
- dom_submit: async (cdp, params) => interact(cdp, async () => {
389
- const target = await resolveScriptTarget(cdp, params);
390
- const submitOptions = filterDefined({
436
+ dom_submit: async (cdp, params) => withTriggeredRequestCount(await interact(cdp, async () => onScriptTarget(cdp, params, (target) => submitForm(target.cdp, target.selector, {
437
+ ...filterDefined({
391
438
  index: target.index,
392
439
  waitNavigation: params.waitNavigation,
393
440
  waitNetwork: params.waitNetwork,
394
441
  timeout: params.timeout,
395
- });
396
- return withUserSelector(await submitForm(cdp, target.selector, submitOptions), params.selector);
397
- }),
398
- dom_press_key: async (cdp, params) => interact(cdp, async () => {
399
- const target = await resolveScriptTarget(cdp, params);
400
- const pressKeyOptions = filterDefined({
401
- index: target.index,
402
- times: params.times,
403
- modifiers: params.modifiers,
404
- });
405
- const result = withUserSelector(await pressKeyElement(cdp, target.selector, params.key, pressKeyOptions), params.selector);
406
- if (result.success && params.wait !== false)
407
- await waitForActionStability(cdp);
408
- return result;
409
- }),
410
- dom_scroll: async (cdp, params) => interact(cdp, async () => {
411
- const target = await resolveScriptTarget(cdp, params);
412
- const scrollOptions = filterDefined({
413
- index: target.index,
414
- down: params.down,
415
- up: params.up,
416
- left: params.left,
417
- right: params.right,
418
- top: params.top,
419
- bottom: params.bottom,
420
- });
421
- const result = withUserSelector(await scrollPage(cdp, target.selector || undefined, scrollOptions), params.selector);
422
- if (result.success && params.wait !== false)
423
- await waitForActionStability(cdp);
424
- return result;
425
- }),
442
+ }),
443
+ pendingRequests: () => store.pendingNetworkRequests.values(),
444
+ })), { detectNoEffect: params.waitNetwork !== 0 || params.waitNavigation === true })),
445
+ dom_press_key: async (cdp, params) => interact(cdp, async () => onScriptTarget(cdp, params, (target) => withActionStability(cdp, () => pressKeyElement(target.cdp, target.selector, params.key, filterDefined({
446
+ index: target.index,
447
+ times: params.times,
448
+ modifiers: params.modifiers,
449
+ })), params.wait !== false)), { reportShown: true, detectUnsettled: params.wait !== false }),
450
+ dom_scroll: async (cdp, params) => interact(cdp, async () => onScriptTarget(cdp, params, (target) => withActionStability(cdp, () => scrollPage(target.cdp, target.selector || undefined, filterDefined({
451
+ index: target.index,
452
+ down: params.down,
453
+ up: params.up,
454
+ left: params.left,
455
+ right: params.right,
456
+ top: params.top,
457
+ bottom: params.bottom,
458
+ })), params.wait !== false))),
426
459
  dom_listeners: async (cdp, params) => withBusyPageRecovery(cdp, inspectEventListeners(cdp, params)),
427
- page_navigate: async (cdp, params) => interact(cdp, () => navigatePage(cdp, params.action, filterDefined({ url: params.url, wait: params.wait }))),
460
+ dom_layout: async (cdp, params) => withBusyPageRecovery(cdp, inspectLayout(cdp, params)),
461
+ dom_inspect: async (cdp, params) => withBusyPageRecovery(cdp, inspectElement(cdp, params)),
462
+ dom_wait: async (cdp, params) => waitForCondition(cdp, params),
463
+ page_navigate: async (cdp, params) => interact(cdp, () => navigatePage(cdp, params.action, {
464
+ ...filterDefined({ url: params.url, wait: params.wait }),
465
+ pendingRequests: () => store.pendingNetworkRequests.values(),
466
+ }), { reportRequests: false, reportEffects: false }),
467
+ page_emulate: async (cdp, params) => {
468
+ const emulated = await emulatePage(cdp, emulation.get(), params, emulation.set);
469
+ return { emulated, ...(await pageAppearance(cdp)) };
470
+ },
428
471
  dom_form_discover: async (cdp) => {
429
472
  const response = await withBusyPageRecovery(cdp, cdp.send('Runtime.evaluate', {
430
473
  expression: FORM_DISCOVERY_SCRIPT,
@@ -432,7 +475,10 @@ export function createCommandRegistry(store) {
432
475
  }));
433
476
  const cdpResponse = response;
434
477
  if (cdpResponse.exceptionDetails) {
435
- throw new Error(`Form discovery failed: ${cdpResponse.exceptionDetails.text}`);
478
+ const readyState = await readDocumentReadyState(cdp);
479
+ const loading = readyState !== undefined && readyState !== 'complete';
480
+ const err = formDiscoveryFailedError(exceptionSummary(cdpResponse.exceptionDetails), readyState);
481
+ throw new CommandError(err.message, { suggestion: err.suggestion }, loading ? EXIT_CODES.RESOURCE_NOT_FOUND : EXIT_CODES.SOFTWARE_ERROR);
436
482
  }
437
483
  const rawData = cdpResponse.result?.value;
438
484
  if (!isRawFormData(rawData)) {
@@ -1,14 +1,48 @@
1
1
  /**
2
2
  * Running page interactions (fill, click, submit, pressKey, scroll) one at a
3
- * time, reporting the dialogs they caused.
3
+ * time, reporting the dialogs, network requests and page changes they caused,
4
+ * and whether the page was still changing when they returned.
4
5
  */
5
6
  import type { TelemetryStore } from './TelemetryStore.js';
6
7
  import type { CDPConnection } from '../../connection/cdp.js';
7
- import type { DialogInfo } from '../../ipc/protocol/domTypes.js';
8
- /** Runs one interaction after the previous one finished */
9
- export type InteractionRunner = <T extends object>(cdp: CDPConnection, action: () => Promise<T>) => Promise<T & {
8
+ import type { ActionEffects, DialogInfo, TriggeredRequest } from '../../ipc/protocol/domTypes.js';
9
+ /** What an interaction caused besides its own result */
10
+ interface InteractionReport extends ActionEffects {
10
11
  dialogs?: DialogInfo[];
11
- }>;
12
+ triggeredRequests?: TriggeredRequest[];
13
+ triggeredRequestsOmitted?: number;
14
+ }
15
+ /** How an interaction is reported */
16
+ export interface InteractionOptions {
17
+ /**
18
+ * List the network requests it triggered (default true; off for page
19
+ * navigation, whose requests are the whole page load)
20
+ */
21
+ reportRequests?: boolean;
22
+ /**
23
+ * Report navigation and new messages (default true; off for page
24
+ * navigation, which reports its own page)
25
+ */
26
+ reportEffects?: boolean;
27
+ /**
28
+ * Say when it had no visible effect (`effect: "none"`): only for actions
29
+ * whose effect shows in the DOM (click, submit) and that waited for it
30
+ */
31
+ detectNoEffect?: boolean;
32
+ /**
33
+ * List the elements it showed (`shown`): for hover and key presses, whose
34
+ * effect is often a menu, tooltip or new item rather than a message
35
+ */
36
+ reportShown?: boolean;
37
+ /**
38
+ * Say when the page was still changing as it returned (`settled: false`
39
+ * with `pending`): for actions that start app transitions (click, key
40
+ * press) and waited for the network
41
+ */
42
+ detectUnsettled?: boolean;
43
+ }
44
+ /** Runs one interaction after the previous one finished */
45
+ export type InteractionRunner = <T extends object>(cdp: CDPConnection, action: () => Promise<T>, options?: InteractionOptions) => Promise<T & InteractionReport>;
12
46
  /**
13
47
  * Create the runner for a session's interactions.
14
48
  *
@@ -16,12 +50,17 @@ export type InteractionRunner = <T extends object>(cdp: CDPConnection, action: (
16
50
  * commands would interleave keystrokes into each other's field, so they are
17
51
  * queued. After each one, the bound target (`window.__bdgTarget`) is removed
18
52
  * from the page (without waiting: during a pending navigation that takes until
19
- * the new page commits), and the dialogs it opened are added to its result.
20
- * Dialogs are attributed by time: one opened by a page timer or a navigation
21
- * started earlier is reported by whichever interaction is running then.
53
+ * the new page commits), and the dialogs it opened, what it changed on the
54
+ * page (see {@link watchActionEffects}) and the network requests it
55
+ * triggered (see {@link watchTriggeredRequests}) are added to its result,
56
+ * and, when asked, what the page was still working on (see
57
+ * {@link pendingChanges}). They are attributed by time: a dialog or request
58
+ * started by a page timer or a navigation started earlier is reported by
59
+ * whichever interaction is running then.
22
60
  *
23
- * @param store - Session store recording accepted dialogs
61
+ * @param store - Session store recording accepted dialogs and network requests
24
62
  * @returns Interaction runner
25
63
  */
26
64
  export declare function createInteractionRunner(store: TelemetryStore): InteractionRunner;
65
+ export {};
27
66
  //# sourceMappingURL=interactions.d.ts.map
@@ -1,12 +1,23 @@
1
1
  /**
2
2
  * Running page interactions (fill, click, submit, pressKey, scroll) one at a
3
- * time, reporting the dialogs they caused.
3
+ * time, reporting the dialogs, network requests and page changes they caused,
4
+ * and whether the page was still changing when they returned.
4
5
  */
6
+ import { pendingChanges, watchActionEffects } from '../../runtime/dom/actionEffects.js';
7
+ import { UNBIND_TARGET_SCRIPT } from '../../runtime/dom/targetNode.js';
5
8
  import { createLogger } from '../../ui/logging/index.js';
6
9
  import { getErrorMessage } from '../../utils/errors.js';
10
+ import { watchTriggeredRequests } from './triggeredRequests.js';
7
11
  const log = createLogger('dom');
8
- /** Removes the node bound for index-based commands from the page */
9
- const UNBIND_TARGET_SCRIPT = 'delete window.__bdgTarget';
12
+ /**
13
+ * Whether an action result is a success (results without a `success` flag are).
14
+ *
15
+ * @param result - Action result
16
+ * @returns False for `{ success: false }`
17
+ */
18
+ function succeeded(result) {
19
+ return !('success' in result) || result.success !== false;
20
+ }
10
21
  /**
11
22
  * Create the runner for a session's interactions.
12
23
  *
@@ -14,24 +25,50 @@ const UNBIND_TARGET_SCRIPT = 'delete window.__bdgTarget';
14
25
  * commands would interleave keystrokes into each other's field, so they are
15
26
  * queued. After each one, the bound target (`window.__bdgTarget`) is removed
16
27
  * from the page (without waiting: during a pending navigation that takes until
17
- * the new page commits), and the dialogs it opened are added to its result.
18
- * Dialogs are attributed by time: one opened by a page timer or a navigation
19
- * started earlier is reported by whichever interaction is running then.
28
+ * the new page commits), and the dialogs it opened, what it changed on the
29
+ * page (see {@link watchActionEffects}) and the network requests it
30
+ * triggered (see {@link watchTriggeredRequests}) are added to its result,
31
+ * and, when asked, what the page was still working on (see
32
+ * {@link pendingChanges}). They are attributed by time: a dialog or request
33
+ * started by a page timer or a navigation started earlier is reported by
34
+ * whichever interaction is running then.
20
35
  *
21
- * @param store - Session store recording accepted dialogs
36
+ * @param store - Session store recording accepted dialogs and network requests
22
37
  * @returns Interaction runner
23
38
  */
24
39
  export function createInteractionRunner(store) {
25
40
  let queue = Promise.resolve();
26
- return (cdp, action) => {
41
+ return (cdp, action, options = {}) => {
27
42
  const run = queue.then(async () => {
28
43
  const firstDialog = store.dialogs.length;
44
+ const collectRequests = options.reportRequests === false ? undefined : watchTriggeredRequests(store);
45
+ const effects = options.reportEffects === false ? undefined : watchActionEffects(cdp);
29
46
  try {
30
47
  const result = await action();
31
48
  const dialogs = store.dialogs.slice(firstDialog);
32
- return dialogs.length > 0 ? { ...result, dialogs } : result;
49
+ if (!succeeded(result))
50
+ return { ...result, ...(dialogs.length > 0 && { dialogs }) };
51
+ const collected = await effects?.collect({
52
+ dialogs: dialogs.length,
53
+ detectNoEffect: options.detectNoEffect === true,
54
+ reportShown: options.reportShown === true,
55
+ detectUnsettled: options.detectUnsettled === true,
56
+ });
57
+ const { work, ...changes } = collected ?? {};
58
+ const requests = collectRequests?.();
59
+ const pending = options.detectUnsettled && work
60
+ ? pendingChanges(work, requests?.triggeredRequests)
61
+ : undefined;
62
+ return {
63
+ ...result,
64
+ ...(dialogs.length > 0 && { dialogs }),
65
+ ...changes,
66
+ ...(pending && { settled: false, pending }),
67
+ ...requests,
68
+ };
33
69
  }
34
70
  finally {
71
+ effects?.dispose();
35
72
  void cdp
36
73
  .send('Runtime.evaluate', { expression: UNBIND_TARGET_SCRIPT })
37
74
  .catch((error) => log.debug(`Target not unbound: ${getErrorMessage(error)}`));
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Network requests an interaction triggered, read from the session's network
3
+ * telemetry (no CDP listeners of its own).
4
+ */
5
+ import type { TelemetryStore } from './TelemetryStore.js';
6
+ import type { TriggeredRequest } from '../../ipc/protocol/domTypes.js';
7
+ import type { NetworkRequest } from '../../types.js';
8
+ /**
9
+ * Requests listed in a result (a click that loads a page triggers its whole
10
+ * load); notable ones ({@link isNotableRequest}) are kept before assets
11
+ */
12
+ export declare const MAX_TRIGGERED_REQUESTS = 50;
13
+ /** Requests started since the watch began: the first ones, and how many more there were */
14
+ export interface CollectedRequests {
15
+ triggeredRequests: TriggeredRequest[];
16
+ triggeredRequestsOmitted?: number;
17
+ }
18
+ /** Lists the requests started since the watch began */
19
+ export type TriggeredRequestsCollector = () => CollectedRequests | undefined;
20
+ /**
21
+ * Start attributing requests to an interaction.
22
+ *
23
+ * Requests are attributed by time: every request (and WebSocket connection)
24
+ * bdg saw start between this call and the collector's call belongs to the
25
+ * interaction, including one a page timer or poller happened to start
26
+ * meanwhile. Collecting adds no wait: requests
27
+ * still running then are reported as pending.
28
+ *
29
+ * @param store - Session store holding the network telemetry
30
+ * @returns Collector (at most {@link MAX_TRIGGERED_REQUESTS} requests, plus how
31
+ * many more there were); it returns undefined when network telemetry is off
32
+ */
33
+ export declare function watchTriggeredRequests(store: TelemetryStore): TriggeredRequestsCollector;
34
+ /**
35
+ * At most `limit` requests, notable ones first (pages, API calls, sockets,
36
+ * failures), then assets, still in start order.
37
+ *
38
+ * @param requests - Requests in start order
39
+ * @param limit - How many to keep
40
+ * @returns Kept requests, in start order
41
+ */
42
+ export declare function keepNotableFirst(requests: TriggeredRequest[], limit: number): TriggeredRequest[];
43
+ /**
44
+ * Count a submission's network requests as its request list does (the
45
+ * submit watcher counts every request id CDP reported until the wait ended:
46
+ * `data:` URLs and preflights included, a redirect chain once), so
47
+ * `networkRequests`, the listed requests and JSON agree. Without network
48
+ * telemetry the watcher's count stays.
49
+ *
50
+ * @param result - Submit result with the requests it triggered
51
+ * @returns The result, `networkRequests` matching `triggeredRequests`
52
+ */
53
+ export declare function withTriggeredRequestCount<T extends {
54
+ networkRequests?: number | undefined;
55
+ triggeredRequests?: TriggeredRequest[] | undefined;
56
+ triggeredRequestsOmitted?: number | undefined;
57
+ }>(result: T): T;
58
+ /**
59
+ * Summarize a captured request.
60
+ *
61
+ * @param request - Captured request
62
+ * @param inFlight - Whether it was still in flight (a request with a response
63
+ * is then still loading its body: a stream, a slow download)
64
+ * @returns Triggered request entry
65
+ */
66
+ export declare function toTriggeredRequest(request: NetworkRequest, inFlight?: boolean): TriggeredRequest;
67
+ //# sourceMappingURL=triggeredRequests.d.ts.map