browser-debugger-cli 0.14.0 → 0.16.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 (167) hide show
  1. package/.claude/skills/bdg/SKILL.md +3 -2
  2. package/dist/cdp/methodTarget.d.ts +92 -0
  3. package/dist/cdp/methodTarget.js +159 -0
  4. package/dist/cdp/protocol.d.ts +16 -1
  5. package/dist/cdp/protocol.js +21 -0
  6. package/dist/cdp/schema.d.ts +55 -1
  7. package/dist/cdp/schema.js +134 -25
  8. package/dist/cdp/types.d.ts +3 -1
  9. package/dist/commands/cdp.d.ts +38 -1
  10. package/dist/commands/cdp.js +201 -133
  11. package/dist/commands/cleanup.js +21 -4
  12. package/dist/commands/dom/eval.d.ts +2 -1
  13. package/dist/commands/dom/eval.js +6 -21
  14. package/dist/commands/dom/formInteraction.js +8 -4
  15. package/dist/commands/dom/helpers/evalResult.d.ts +36 -0
  16. package/dist/commands/dom/helpers/evalResult.js +59 -0
  17. package/dist/commands/dom/helpers/index.d.ts +4 -4
  18. package/dist/commands/dom/helpers/index.js +3 -3
  19. package/dist/commands/dom/helpers/query.d.ts +2 -2
  20. package/dist/commands/dom/helpers/query.js +2 -2
  21. package/dist/commands/dom/helpers/screenshot.d.ts +21 -26
  22. package/dist/commands/dom/helpers/screenshot.js +50 -668
  23. package/dist/commands/dom/screenshot.js +56 -36
  24. package/dist/commands/helpJson.d.ts +1 -1
  25. package/dist/commands/helpJson.js +3 -3
  26. package/dist/commands/helpTopic.js +10 -4
  27. package/dist/commands/network/har.js +18 -14
  28. package/dist/commands/optionBehaviors.js +24 -9
  29. package/dist/commands/shared/CommandRunner.d.ts +5 -0
  30. package/dist/commands/shared/CommandRunner.js +18 -3
  31. package/dist/commands/shared/interrupt.d.ts +40 -0
  32. package/dist/commands/shared/interrupt.js +73 -0
  33. package/dist/commands/shared/optionTypes.d.ts +3 -0
  34. package/dist/commands/shared/outputFile.d.ts +2 -1
  35. package/dist/commands/shared/outputFile.js +7 -4
  36. package/dist/commands/shared/startHelpers.d.ts +26 -3
  37. package/dist/commands/shared/startHelpers.js +145 -23
  38. package/dist/commands/status.js +3 -1
  39. package/dist/commands/stop.js +2 -1
  40. package/dist/commands/types.d.ts +5 -0
  41. package/dist/connection/cdp.js +1 -16
  42. package/dist/connection/chromeIdentity.d.ts +24 -5
  43. package/dist/connection/chromeIdentity.js +53 -22
  44. package/dist/connection/launcher/flagsBuilder.d.ts +46 -0
  45. package/dist/connection/launcher/flagsBuilder.js +107 -23
  46. package/dist/connection/launcher.d.ts +35 -2
  47. package/dist/connection/launcher.js +99 -12
  48. package/dist/connection/typed-cdp.d.ts +3 -2
  49. package/dist/constants.d.ts +3 -5
  50. package/dist/constants.js +3 -5
  51. package/dist/daemon/SessionController.d.ts +10 -5
  52. package/dist/daemon/SessionController.js +15 -8
  53. package/dist/daemon/ipcServer.js +1 -1
  54. package/dist/daemon/launcher.d.ts +22 -3
  55. package/dist/daemon/launcher.js +45 -8
  56. package/dist/daemon/session/Session.d.ts +5 -1
  57. package/dist/daemon/session/Session.js +9 -8
  58. package/dist/daemon/session/TelemetryStore.d.ts +5 -0
  59. package/dist/daemon/session/TelemetryStore.js +4 -0
  60. package/dist/daemon/session/captureGate.d.ts +59 -0
  61. package/dist/daemon/session/captureGate.js +96 -0
  62. package/dist/daemon/session/chromeConnection.d.ts +16 -1
  63. package/dist/daemon/session/chromeConnection.js +34 -4
  64. package/dist/daemon/session/collectors.d.ts +15 -0
  65. package/dist/daemon/session/collectors.js +39 -2
  66. package/dist/daemon/session/commandRegistry.d.ts +14 -1
  67. package/dist/daemon/session/commandRegistry.js +48 -13
  68. package/dist/daemon/session/downloads.d.ts +32 -0
  69. package/dist/daemon/session/downloads.js +96 -0
  70. package/dist/daemon/session/interactions.d.ts +3 -2
  71. package/dist/daemon/session/interactions.js +7 -2
  72. package/dist/daemon/session/plugins.js +6 -0
  73. package/dist/daemon.js +18520 -17014
  74. package/dist/errors/CommandError.d.ts +2 -0
  75. package/dist/errors/issues.d.ts +1 -1
  76. package/dist/errors/messages.d.ts +81 -0
  77. package/dist/errors/messages.js +198 -6
  78. package/dist/index.js +1446 -1078
  79. package/dist/ipc/client.d.ts +20 -2
  80. package/dist/ipc/client.js +32 -6
  81. package/dist/ipc/protocol/commands.d.ts +36 -2
  82. package/dist/ipc/protocol/commands.js +1 -0
  83. package/dist/ipc/protocol/domTypes.d.ts +24 -1
  84. package/dist/ipc/session/queries.d.ts +3 -0
  85. package/dist/ipc/session/types.d.ts +5 -0
  86. package/dist/ipc/transport/IPCError.d.ts +9 -0
  87. package/dist/ipc/transport/IPCError.js +12 -0
  88. package/dist/ipc/transport/errors.d.ts +2 -1
  89. package/dist/ipc/transport/errors.js +4 -1
  90. package/dist/ipc/transport/index.d.ts +10 -2
  91. package/dist/ipc/transport/index.js +29 -4
  92. package/dist/runtime/dom/actionEffects.d.ts +48 -9
  93. package/dist/runtime/dom/actionEffects.js +269 -34
  94. package/dist/runtime/dom/actionEffectsScripts.d.ts +45 -0
  95. package/dist/runtime/dom/actionEffectsScripts.js +101 -2
  96. package/dist/runtime/dom/captureArea.d.ts +35 -0
  97. package/dist/runtime/dom/captureArea.js +203 -0
  98. package/dist/runtime/dom/elementInfo.d.ts +13 -4
  99. package/dist/runtime/dom/elementInfo.js +12 -3
  100. package/dist/runtime/dom/evalHelpers.d.ts +24 -4
  101. package/dist/runtime/dom/evalHelpers.js +40 -12
  102. package/dist/runtime/dom/formDiscovery.d.ts +1 -1
  103. package/dist/runtime/dom/frames.d.ts +2 -1
  104. package/dist/runtime/dom/frames.js +3 -1
  105. package/dist/runtime/page/bdgWorld.d.ts +9 -0
  106. package/dist/runtime/page/bdgWorld.js +11 -0
  107. package/dist/runtime/page/captureEmulation.d.ts +119 -0
  108. package/dist/runtime/page/captureEmulation.js +189 -0
  109. package/dist/runtime/page/captureScroll.d.ts +24 -0
  110. package/dist/runtime/page/captureScroll.js +124 -0
  111. package/dist/runtime/page/emulation.js +6 -5
  112. package/dist/runtime/page/screenshot.d.ts +41 -0
  113. package/dist/runtime/page/screenshot.js +394 -0
  114. package/dist/runtime/page/userAgent.d.ts +86 -2
  115. package/dist/runtime/page/userAgent.js +154 -33
  116. package/dist/session/paths.d.ts +52 -3
  117. package/dist/session/paths.js +179 -7
  118. package/dist/session/portClaims.d.ts +0 -8
  119. package/dist/session/portClaims.js +1 -22
  120. package/dist/session/sessionList.d.ts +5 -1
  121. package/dist/session/sessionList.js +5 -1
  122. package/dist/telemetry/downloads.d.ts +127 -0
  123. package/dist/telemetry/downloads.js +265 -0
  124. package/dist/telemetry/har/builder.d.ts +12 -1
  125. package/dist/telemetry/har/builder.js +32 -9
  126. package/dist/telemetry/har/sanitize.d.ts +28 -0
  127. package/dist/telemetry/har/sanitize.js +184 -0
  128. package/dist/telemetry/har/sanitizeBody.d.ts +78 -0
  129. package/dist/telemetry/har/sanitizeBody.js +541 -0
  130. package/dist/telemetry/har/types.d.ts +2 -0
  131. package/dist/telemetry/network.d.ts +4 -4
  132. package/dist/telemetry/network.js +38 -4
  133. package/dist/telemetry/networkRetention.d.ts +35 -14
  134. package/dist/telemetry/networkRetention.js +62 -26
  135. package/dist/types.d.ts +9 -14
  136. package/dist/ui/OutputBuilder.d.ts +3 -2
  137. package/dist/ui/OutputBuilder.js +4 -3
  138. package/dist/ui/formatters/cdp.d.ts +32 -9
  139. package/dist/ui/formatters/cdp.js +77 -6
  140. package/dist/ui/formatters/details.js +7 -15
  141. package/dist/ui/formatters/preview.d.ts +2 -0
  142. package/dist/ui/formatters/preview.js +7 -1
  143. package/dist/ui/formatters/sessions.d.ts +3 -2
  144. package/dist/ui/formatters/sessions.js +10 -3
  145. package/dist/ui/formatters/status.js +6 -1
  146. package/dist/ui/formatting.d.ts +7 -0
  147. package/dist/ui/formatting.js +13 -0
  148. package/dist/ui/logging/logger.d.ts +1 -1
  149. package/dist/ui/messages/chrome.d.ts +27 -6
  150. package/dist/ui/messages/chrome.js +78 -12
  151. package/dist/ui/messages/commands.d.ts +71 -3
  152. package/dist/ui/messages/commands.js +98 -3
  153. package/dist/ui/messages/networkMessages.d.ts +50 -5
  154. package/dist/ui/messages/networkMessages.js +50 -6
  155. package/dist/ui/messages/session.d.ts +8 -0
  156. package/dist/ui/messages/session.js +10 -0
  157. package/dist/utils/async.d.ts +3 -2
  158. package/dist/utils/async.js +16 -3
  159. package/dist/utils/atomicFile.d.ts +2 -1
  160. package/dist/utils/atomicFile.js +5 -2
  161. package/dist/utils/directories.d.ts +41 -0
  162. package/dist/utils/directories.js +48 -0
  163. package/dist/utils/http.d.ts +11 -4
  164. package/dist/utils/http.js +5 -3
  165. package/package.json +18 -4
  166. /package/dist/{commands/dom → runtime/page}/screenshotResize.d.ts +0 -0
  167. /package/dist/{commands/dom → runtime/page}/screenshotResize.js +0 -0
@@ -1,9 +1,10 @@
1
+ import { missingMethodCause } from '../../cdp/methodTarget.js';
1
2
  import { CDPConnectionError, CDPProtocolError } from '../../connection/errors.js';
2
3
  import { PatternDetector } from '../patternDetector.js';
3
4
  import { createInteractionRunner } from './interactions.js';
4
5
  import { withTriggeredRequestCount } from './triggeredRequests.js';
5
6
  import { CommandError } from '../../errors/index.js';
6
- import { cdpCallError, formDiscoveryFailedError } from '../../errors/messages.js';
7
+ import { cdpCallError, cdpMethodNotImplementedError, formDiscoveryFailedError, } from '../../errors/messages.js';
7
8
  import { searchStyleSheets } from '../../runtime/css/search.js';
8
9
  import { auditPage } from '../../runtime/dom/audit.js';
9
10
  import { evaluateScript, withBusyPageRecovery } from '../../runtime/dom/evalHelpers.js';
@@ -21,6 +22,8 @@ import { evaluateInBdgWorld, sendForBdgScript } from '../../runtime/page/bdgWorl
21
22
  import { emulatePage, pageAppearance } from '../../runtime/page/emulation.js';
22
23
  import { readDocumentReadyState } from '../../runtime/page/loadingState.js';
23
24
  import { navigatePage } from '../../runtime/page/navigation.js';
25
+ import { takeScreenshot } from '../../runtime/page/screenshot.js';
26
+ import { toDownloadInfo } from '../../telemetry/downloads.js';
24
27
  import { skippedBodyReason } from '../../telemetry/networkRetention.js';
25
28
  import { consoleMessageDroppedError } from '../../ui/messages/consoleMessages.js';
26
29
  import { sessionCommand } from '../../ui/messages/sessionCommand.js';
@@ -140,6 +143,15 @@ function webSocketAsRequest(connection) {
140
143
  webSocket: { frames: connection.frames, ...(closedTime !== undefined && { closedTime }) },
141
144
  };
142
145
  }
146
+ /**
147
+ * The session's downloads for `status` and `peek`, left out when none began.
148
+ *
149
+ * @param store - Telemetry store
150
+ * @returns `downloads`, oldest first, as they stand now
151
+ */
152
+ function sessionDownloads(store) {
153
+ return store.downloads.length > 0 ? { downloads: store.downloads.map(toDownloadInfo) } : {};
154
+ }
143
155
  /**
144
156
  * Status activity counts of what the network capture let go at its limits,
145
157
  * left out when nothing was.
@@ -169,18 +181,28 @@ function allNetworkRequests(store) {
169
181
  ].sort((a, b) => a.timestamp - b.timestamp);
170
182
  }
171
183
  /**
172
- * Present a body bdg chose not to fetch as `bodyNotCaptured` (the reason)
173
- * instead of a placeholder string in `responseBody`.
184
+ * Present bodies bdg did not fetch or keep as their reasons
185
+ * (`requestBodyNotCaptured`, `bodyNotCaptured`) instead of placeholder
186
+ * strings in `requestBody` and `responseBody`.
174
187
  *
175
188
  * @param request - Captured request
176
189
  * @returns The request as `details` reports it
177
190
  */
178
191
  function withBodyNotCaptured(request) {
179
- const reason = skippedBodyReason(request.responseBody);
180
- if (reason === undefined)
192
+ const requestReason = skippedBodyReason(request.requestBody);
193
+ const responseReason = skippedBodyReason(request.responseBody);
194
+ if (requestReason === undefined && responseReason === undefined)
181
195
  return request;
182
- const { responseBody: _placeholder, ...rest } = request;
183
- return { ...rest, bodyNotCaptured: reason };
196
+ const result = { ...request };
197
+ if (requestReason !== undefined) {
198
+ delete result.requestBody;
199
+ result.requestBodyNotCaptured = requestReason;
200
+ }
201
+ if (responseReason !== undefined) {
202
+ delete result.responseBody;
203
+ result.bodyNotCaptured = responseReason;
204
+ }
205
+ return result;
184
206
  }
185
207
  /**
186
208
  * Find a network request by ID: finished, in flight, or a WebSocket.
@@ -287,18 +309,25 @@ function pageWebSocketUrl(store) {
287
309
  }
288
310
  /** Chrome's "server error" code, used for failures on the page's state (a missing node, a bad id…) */
289
311
  const CDP_SERVER_ERROR = -32000;
312
+ /** JSON-RPC "method not found": Chrome has no such method (for the target) */
313
+ const CDP_METHOD_NOT_FOUND = -32601;
290
314
  /**
291
- * A `bdg cdp` failure that is the caller's: wrong parameters (81), or an
292
- * id of a node, target or frame that does not exist (83). Other failures
293
- * (internal errors, a detached page) stay software errors.
315
+ * A `bdg cdp` failure that is the caller's: a method this Chrome doesn't
316
+ * implement (83), wrong parameters (81), or an id of a node, target or frame
317
+ * that does not exist (83). Other failures (internal errors, a detached
318
+ * page) stay software errors.
294
319
  *
295
320
  * @param method - CDP method
296
321
  * @param error - What the call threw
297
322
  * @returns The error to report, or undefined to keep the original
298
323
  */
299
- function callerError(method, error) {
324
+ export function callerError(method, error) {
300
325
  if (!(error instanceof CDPProtocolError))
301
326
  return undefined;
327
+ if (error.code === CDP_METHOD_NOT_FOUND) {
328
+ const missing = cdpMethodNotImplementedError(method, error.message, missingMethodCause(method));
329
+ return new CommandError(missing.message, { suggestion: missing.suggestion }, EXIT_CODES.RESOURCE_NOT_FOUND);
330
+ }
302
331
  const err = cdpCallError(method, error.message);
303
332
  if (error.isRequestError()) {
304
333
  return new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.INVALID_ARGUMENTS);
@@ -364,6 +393,7 @@ export function createCommandRegistry(store, emulation) {
364
393
  ...(dropped > 0 && { droppedConsole: dropped }),
365
394
  ...(requestsDropped > 0 && { droppedNetwork: requestsDropped }),
366
395
  ...(bodiesEvicted > 0 && { evictedNetworkBodies: bodiesEvicted }),
396
+ ...sessionDownloads(store),
367
397
  hasMoreNetwork: networkBounds.start > 0,
368
398
  hasMoreConsole: consoleBounds.start > 0,
369
399
  });
@@ -400,6 +430,10 @@ export function createCommandRegistry(store, emulation) {
400
430
  ...(lastNetworkRequest && { lastNetworkRequestAt: lastNetworkRequest.timestamp }),
401
431
  ...(lastConsoleMessage && { lastConsoleMessageAt: lastConsoleMessage.timestamp }),
402
432
  ...networkEvictionActivity(store),
433
+ ...sessionDownloads(store),
434
+ ...(store.downloadsWarning !== undefined && {
435
+ downloadsWarning: store.downloadsWarning,
436
+ }),
403
437
  },
404
438
  navigationId: store.getCurrentNavigationId?.() ?? 0,
405
439
  };
@@ -452,8 +486,8 @@ export function createCommandRegistry(store, emulation) {
452
486
  return { result, ...(hint !== undefined && { hint }) };
453
487
  },
454
488
  dom_eval: async (cdp, params) => params.frame === undefined
455
- ? evaluateScript(cdp, params.script)
456
- : evaluateInFrame(cdp, pageWebSocketUrl(store), params.script, params.frame, listedFrameIds),
489
+ ? evaluateScript(cdp, params.script, { full: params.full ?? false })
490
+ : evaluateInFrame(cdp, pageWebSocketUrl(store), params.script, params.frame, listedFrameIds, params.full ?? false),
457
491
  dom_frames: async (cdp) => {
458
492
  const { frames, frameIds } = await listFrames(cdp, pageWebSocketUrl(store));
459
493
  listedFrameIds = frameIds;
@@ -499,6 +533,7 @@ export function createCommandRegistry(store, emulation) {
499
533
  dom_inspect: async (cdp, params) => withBusyPageRecovery(cdp, inspectElement(cdp, params).then((result) => result.theme === 'dark' && emulation.get().colorScheme === 'dark'
500
534
  ? { ...result, themeFrom: 'emulation' }
501
535
  : result)),
536
+ dom_screenshot: async (cdp, params, abandoned) => takeScreenshot(cdp, params, () => emulation.get().viewport, { abandoned }),
502
537
  dom_wait: async (cdp, params) => waitForCondition(cdp, params),
503
538
  page_navigate: async (cdp, params) => interact(cdp, () => navigatePage(cdp, params.action, {
504
539
  ...filterDefined({ url: params.url, wait: params.wait }),
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Setting up a session's download tracking: where downloads go, and the
3
+ * connection their events arrive on.
4
+ */
5
+ import type { TelemetryPluginContext } from './plugins.js';
6
+ import type { SessionConfig } from './types.js';
7
+ import { CDPConnection } from '../../connection/cdp.js';
8
+ import type { CleanupFunction } from '../../types.js';
9
+ import type { Logger } from '../../ui/logging/index.js';
10
+ /**
11
+ * Opens a browser-level connection to the session's Chrome, or null when
12
+ * there is none; `onLost` is called if it closes while the session runs.
13
+ */
14
+ export type BrowserConnector = (config: SessionConfig, logger: Logger, onLost: () => void) => Promise<CDPConnection | null>;
15
+ /**
16
+ * Track the session's downloads (see {@link DownloadTracker}).
17
+ *
18
+ * For a Chrome bdg launched, on a browser-level connection, so downloads of
19
+ * tabs the page opens are named and recorded too; on the page's connection
20
+ * when there is none, or once it is lost (Chrome drops a connection's
21
+ * download behavior with it, so it is applied again there). An attached
22
+ * Chrome (`--chrome-ws-url`) is followed on the page's connection only: a
23
+ * browser-level one would count the user's other tabs' downloads as the
24
+ * session's, and a Chrome that allows one connection (toggle mode) would
25
+ * ask again for each.
26
+ *
27
+ * @param context - Plugin context
28
+ * @param connectBrowser - Opens the browser-level connection
29
+ * @returns Cleanup: stops tracking and closes the browser-level connection
30
+ */
31
+ export declare function startSessionDownloads(context: TelemetryPluginContext, connectBrowser?: BrowserConnector): Promise<CleanupFunction>;
32
+ //# sourceMappingURL=downloads.d.ts.map
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Setting up a session's download tracking: where downloads go, and the
3
+ * connection their events arrive on.
4
+ */
5
+ import { CDPConnection } from '../../connection/cdp.js';
6
+ import { browserWebSocketUrl } from './chromeConnection.js';
7
+ import { ensureSessionDownloadsDir } from '../../session/paths.js';
8
+ import { DownloadTracker } from '../../telemetry/downloads.js';
9
+ import { downloadsDirUnavailableReason } from '../../ui/messages/commands.js';
10
+ import { getErrorMessage } from '../../utils/errors.js';
11
+ /** Connection attempts for the browser-level connection (Chrome is already running) */
12
+ const BROWSER_CONNECT_ATTEMPTS = 2;
13
+ /**
14
+ * Track the session's downloads (see {@link DownloadTracker}).
15
+ *
16
+ * For a Chrome bdg launched, on a browser-level connection, so downloads of
17
+ * tabs the page opens are named and recorded too; on the page's connection
18
+ * when there is none, or once it is lost (Chrome drops a connection's
19
+ * download behavior with it, so it is applied again there). An attached
20
+ * Chrome (`--chrome-ws-url`) is followed on the page's connection only: a
21
+ * browser-level one would count the user's other tabs' downloads as the
22
+ * session's, and a Chrome that allows one connection (toggle mode) would
23
+ * ask again for each.
24
+ *
25
+ * @param context - Plugin context
26
+ * @param connectBrowser - Opens the browser-level connection
27
+ * @returns Cleanup: stops tracking and closes the browser-level connection
28
+ */
29
+ export async function startSessionDownloads(context, connectBrowser = openBrowserConnection) {
30
+ const { cdp, config, store, logger } = context;
31
+ const tracker = new DownloadTracker(store, downloadDestination(config, logger));
32
+ const browser = config.chromeWsUrl
33
+ ? null
34
+ : await connectBrowser(config, logger, () => void followOnPage(tracker, cdp, logger));
35
+ await tracker.attach(browser ?? cdp);
36
+ return () => {
37
+ tracker.stop();
38
+ browser?.close();
39
+ };
40
+ }
41
+ /**
42
+ * Follow downloads on the page's connection after the browser-level one was lost.
43
+ *
44
+ * @param tracker - Session download tracker
45
+ * @param cdp - Page connection
46
+ * @param logger - Logger
47
+ */
48
+ async function followOnPage(tracker, cdp, logger) {
49
+ logger.info('Browser connection for downloads lost; download behavior set again on the page');
50
+ await tracker.attach(cdp);
51
+ }
52
+ /**
53
+ * Open a browser-level connection to the session's Chrome.
54
+ *
55
+ * @param config - Session configuration
56
+ * @param logger - Logger
57
+ * @param onLost - Called if the connection closes while the session runs
58
+ * @returns Open connection, or null when there is none to open
59
+ */
60
+ async function openBrowserConnection(config, logger, onLost) {
61
+ const url = await browserWebSocketUrl(config, logger);
62
+ if (!url)
63
+ return null;
64
+ const browser = new CDPConnection(logger);
65
+ try {
66
+ await browser.connect(url, { maxRetries: BROWSER_CONNECT_ATTEMPTS, onDisconnect: onLost });
67
+ return browser;
68
+ }
69
+ catch (error) {
70
+ logger.debug(`No browser-level connection for downloads: ${getErrorMessage(error)}`);
71
+ return null;
72
+ }
73
+ }
74
+ /**
75
+ * Where the session's downloads go: the session's `downloads/` for a Chrome
76
+ * bdg launched (refused when it cannot be created, rather than saved to the
77
+ * browser's default `~/Downloads`), the browser's own place for an attached
78
+ * Chrome.
79
+ *
80
+ * @param config - Session configuration
81
+ * @param logger - Logger
82
+ * @returns Destination
83
+ */
84
+ function downloadDestination(config, logger) {
85
+ if (config.chromeWsUrl)
86
+ return { kind: 'browser' };
87
+ try {
88
+ return { kind: 'directory', dir: ensureSessionDownloadsDir() };
89
+ }
90
+ catch (error) {
91
+ const reason = downloadsDirUnavailableReason(getErrorMessage(error));
92
+ logger.info(reason);
93
+ return { kind: 'refused', reason };
94
+ }
95
+ }
96
+ //# sourceMappingURL=downloads.js.map
@@ -52,8 +52,9 @@ export type InteractionRunner = <T extends object>(cdp: CDPConnection, action: (
52
52
  * from the page (without waiting: during a pending navigation that takes until
53
53
  * the new page commits), and the dialogs it opened, what it changed on the
54
54
  * page (see {@link watchActionEffects}; a console message logged meanwhile
55
- * counts as an effect) and the network requests it
56
- * triggered (see {@link watchTriggeredRequests}) are added to its result,
55
+ * counts as an effect), the network requests it
56
+ * triggered (see {@link watchTriggeredRequests}) and the downloads that began
57
+ * meanwhile (as they stand when it returns) are added to its result,
57
58
  * and, when asked, what the page was still working on (see
58
59
  * {@link pendingChanges}). They are attributed by time: a dialog or request
59
60
  * started by a page timer or a navigation started earlier is reported by
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import { pendingChanges, watchActionEffects } from '../../runtime/dom/actionEffects.js';
7
7
  import { UNBIND_TARGET_SCRIPT } from '../../runtime/dom/targetNode.js';
8
+ import { toDownloadInfo } from '../../telemetry/downloads.js';
8
9
  import { createLogger } from '../../ui/logging/index.js';
9
10
  import { getErrorMessage } from '../../utils/errors.js';
10
11
  import { watchTriggeredRequests } from './triggeredRequests.js';
@@ -36,8 +37,9 @@ function consoleMessagesLogged(store) {
36
37
  * from the page (without waiting: during a pending navigation that takes until
37
38
  * the new page commits), and the dialogs it opened, what it changed on the
38
39
  * page (see {@link watchActionEffects}; a console message logged meanwhile
39
- * counts as an effect) and the network requests it
40
- * triggered (see {@link watchTriggeredRequests}) are added to its result,
40
+ * counts as an effect), the network requests it
41
+ * triggered (see {@link watchTriggeredRequests}) and the downloads that began
42
+ * meanwhile (as they stand when it returns) are added to its result,
41
43
  * and, when asked, what the page was still working on (see
42
44
  * {@link pendingChanges}). They are attributed by time: a dialog or request
43
45
  * started by a page timer or a navigation started earlier is reported by
@@ -51,6 +53,7 @@ export function createInteractionRunner(store) {
51
53
  return (cdp, action, options = {}) => {
52
54
  const run = queue.then(async () => {
53
55
  const firstDialog = store.dialogs.length;
56
+ const firstDownload = store.downloads.length;
54
57
  const firstConsoleMessage = consoleMessagesLogged(store);
55
58
  const collectRequests = options.reportRequests === false ? undefined : watchTriggeredRequests(store);
56
59
  const effects = options.reportEffects === false ? undefined : watchActionEffects(cdp);
@@ -68,6 +71,7 @@ export function createInteractionRunner(store) {
68
71
  });
69
72
  const { work, ...changes } = collected ?? {};
70
73
  const requests = collectRequests?.();
74
+ const downloads = store.downloads.slice(firstDownload).map(toDownloadInfo);
71
75
  const pending = options.detectUnsettled && work
72
76
  ? pendingChanges(work, requests?.triggeredRequests)
73
77
  : undefined;
@@ -76,6 +80,7 @@ export function createInteractionRunner(store) {
76
80
  ...(dialogs.length > 0 && { dialogs }),
77
81
  ...changes,
78
82
  ...(pending && { settled: false, pending }),
83
+ ...(downloads.length > 0 && { downloads }),
79
84
  ...requests,
80
85
  };
81
86
  }
@@ -1,3 +1,4 @@
1
+ import { startSessionDownloads } from './downloads.js';
1
2
  import { hideHeadlessUserAgent } from '../../runtime/page/userAgent.js';
2
3
  import { startConsoleCollection } from '../../telemetry/console.js';
3
4
  import { startDialogHandling } from '../../telemetry/dialogs.js';
@@ -94,6 +95,11 @@ export function createDefaultTelemetryPlugins() {
94
95
  return startDialogHandling(cdp, (dialog) => store.recordDialog(dialog));
95
96
  },
96
97
  },
98
+ {
99
+ name: 'downloads',
100
+ runAlways: true,
101
+ start: startSessionDownloads,
102
+ },
97
103
  {
98
104
  name: 'page-identity',
99
105
  runAlways: true,