chrome-devtools-mcp 0.23.0 → 0.24.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 (77) hide show
  1. package/build/src/DevToolsConnectionAdapter.js +1 -0
  2. package/build/src/DevtoolsUtils.js +1 -0
  3. package/build/src/HeapSnapshotManager.js +16 -0
  4. package/build/src/McpContext.js +57 -4
  5. package/build/src/McpPage.js +6 -0
  6. package/build/src/McpResponse.js +38 -4
  7. package/build/src/Mutex.js +1 -0
  8. package/build/src/PageCollector.js +1 -0
  9. package/build/src/SlimMcpResponse.js +1 -0
  10. package/build/src/TextSnapshot.js +11 -5
  11. package/build/src/WaitForHelper.js +6 -0
  12. package/build/src/bin/check-latest-version.js +1 -0
  13. package/build/src/bin/chrome-devtools-cli-options.js +206 -46
  14. package/build/src/bin/chrome-devtools-mcp-cli-options.js +3 -1
  15. package/build/src/bin/chrome-devtools-mcp-main.js +1 -0
  16. package/build/src/bin/chrome-devtools-mcp.js +1 -0
  17. package/build/src/bin/chrome-devtools.js +5 -13
  18. package/build/src/browser.js +1 -0
  19. package/build/src/daemon/client.js +4 -2
  20. package/build/src/daemon/daemon.js +1 -0
  21. package/build/src/daemon/types.js +1 -0
  22. package/build/src/daemon/utils.js +1 -0
  23. package/build/src/formatters/ConsoleFormatter.js +48 -1
  24. package/build/src/formatters/HeapSnapshotFormatter.js +18 -2
  25. package/build/src/formatters/IssueFormatter.js +1 -0
  26. package/build/src/formatters/NetworkFormatter.js +1 -0
  27. package/build/src/formatters/SnapshotFormatter.js +2 -1
  28. package/build/src/index.js +114 -51
  29. package/build/src/issue-descriptions.js +1 -0
  30. package/build/src/logger.js +1 -0
  31. package/build/src/polyfill.js +1 -0
  32. package/build/src/telemetry/ClearcutLogger.js +13 -1
  33. package/build/src/telemetry/WatchdogClient.js +1 -0
  34. package/build/src/telemetry/flagUtils.js +1 -0
  35. package/build/src/telemetry/metricUtils.js +1 -0
  36. package/build/src/telemetry/persistence.js +1 -0
  37. package/build/src/telemetry/toolMetricsUtils.js +2 -1
  38. package/build/src/telemetry/types.js +1 -0
  39. package/build/src/telemetry/watchdog/ClearcutSender.js +1 -0
  40. package/build/src/telemetry/watchdog/main.js +1 -0
  41. package/build/src/third_party/THIRD_PARTY_NOTICES +5 -5
  42. package/build/src/third_party/bundled-packages.json +2 -2
  43. package/build/src/third_party/devtools-formatter-worker.js +2451 -2933
  44. package/build/src/third_party/devtools-heap-snapshot-worker.js +32 -26
  45. package/build/src/third_party/index.js +535 -135
  46. package/build/src/third_party/lighthouse-devtools-mcp-bundle.js +21717 -20261
  47. package/build/src/tools/ToolDefinition.js +1 -0
  48. package/build/src/tools/categories.js +6 -2
  49. package/build/src/tools/console.js +3 -0
  50. package/build/src/tools/emulation.js +2 -0
  51. package/build/src/tools/extensions.js +6 -0
  52. package/build/src/tools/inPage.js +3 -2
  53. package/build/src/tools/input.js +13 -2
  54. package/build/src/tools/lighthouse.js +17 -9
  55. package/build/src/tools/memory.js +34 -1
  56. package/build/src/tools/network.js +5 -0
  57. package/build/src/tools/pages.js +9 -0
  58. package/build/src/tools/performance.js +6 -0
  59. package/build/src/tools/screencast.js +6 -2
  60. package/build/src/tools/screenshot.js +3 -0
  61. package/build/src/tools/script.js +2 -0
  62. package/build/src/tools/slim/tools.js +4 -0
  63. package/build/src/tools/snapshot.js +5 -1
  64. package/build/src/tools/tools.js +1 -0
  65. package/build/src/tools/webmcp.js +3 -0
  66. package/build/src/trace-processing/parse.js +1 -0
  67. package/build/src/types.js +1 -0
  68. package/build/src/utils/check-for-updates.js +1 -0
  69. package/build/src/utils/files.js +5 -10
  70. package/build/src/utils/id.js +1 -0
  71. package/build/src/utils/keyboard.js +1 -0
  72. package/build/src/utils/pagination.js +1 -0
  73. package/build/src/utils/string.js +1 -0
  74. package/build/src/utils/types.js +1 -0
  75. package/build/src/version.js +2 -1
  76. package/package.json +9 -9
  77. package/build/src/bin/cliDefinitions.js +0 -621
@@ -28,6 +28,36 @@ export const commands = {
28
28
  },
29
29
  },
30
30
  },
31
+ click_at: {
32
+ description: 'Clicks at the provided coordinates (requires flag: --experimentalVision=true)',
33
+ category: 'Input automation',
34
+ args: {
35
+ x: {
36
+ name: 'x',
37
+ type: 'number',
38
+ description: 'The x coordinate',
39
+ required: true,
40
+ },
41
+ y: {
42
+ name: 'y',
43
+ type: 'number',
44
+ description: 'The y coordinate',
45
+ required: true,
46
+ },
47
+ dblClick: {
48
+ name: 'dblClick',
49
+ type: 'boolean',
50
+ description: 'Set to true for double clicks. Default is false.',
51
+ required: false,
52
+ },
53
+ includeSnapshot: {
54
+ name: 'includeSnapshot',
55
+ type: 'boolean',
56
+ description: 'Whether to include a snapshot in the response. Default is false.',
57
+ required: false,
58
+ },
59
+ },
60
+ },
31
61
  close_page: {
32
62
  description: 'Closes the page by its index. The last open page cannot be closed.',
33
63
  category: 'Navigation automation',
@@ -84,7 +114,7 @@ export const commands = {
84
114
  geolocation: {
85
115
  name: 'geolocation',
86
116
  type: 'string',
87
- description: 'Geolocation (`<latitude>x<longitude>`) to emulate. Latitude between -90 and 90. Longitude between -180 and 180. Omit clear the geolocation override.',
117
+ description: 'Geolocation (`<latitude>x<longitude>`) to emulate. Latitude between -90 and 90. Longitude between -180 and 180. Omit to clear the geolocation override.',
88
118
  required: false,
89
119
  },
90
120
  userAgent: {
@@ -124,10 +154,34 @@ export const commands = {
124
154
  description: 'An optional list of arguments to pass to the function.',
125
155
  required: false,
126
156
  },
157
+ dialogAction: {
158
+ name: 'dialogAction',
159
+ type: 'string',
160
+ description: 'Handle dialogs while execution. "accept", "dismiss", or string for response of window.prompt. Defaults to accept.',
161
+ required: false,
162
+ },
163
+ },
164
+ },
165
+ execute_webmcp_tool: {
166
+ description: 'Executes a WebMCP tool exposed by the page. (requires flag: --experimentalWebmcp=true)',
167
+ category: 'Debugging',
168
+ args: {
169
+ toolName: {
170
+ name: 'toolName',
171
+ type: 'string',
172
+ description: 'The name of the WebMCP tool to execute',
173
+ required: true,
174
+ },
175
+ input: {
176
+ name: 'input',
177
+ type: 'string',
178
+ description: 'The JSON-stringified parameters to pass to the WebMCP tool',
179
+ required: false,
180
+ },
127
181
  },
128
182
  },
129
183
  fill: {
130
- description: 'Type text into a input, text area or select an option from a <select> element.',
184
+ description: 'Type text into an input, text area or select an option from a <select> element.',
131
185
  category: 'Input automation',
132
186
  args: {
133
187
  uid: {
@@ -150,24 +204,6 @@ export const commands = {
150
204
  },
151
205
  },
152
206
  },
153
- fill_form: {
154
- description: 'Fill out multiple form elements at once',
155
- category: 'Input automation',
156
- args: {
157
- elements: {
158
- name: 'elements',
159
- type: 'array',
160
- description: 'Elements from snapshot to fill out.',
161
- required: true,
162
- },
163
- includeSnapshot: {
164
- name: 'includeSnapshot',
165
- type: 'boolean',
166
- description: 'Whether to include a snapshot in the response. Default is false.',
167
- required: false,
168
- },
169
- },
170
- },
171
207
  get_console_message: {
172
208
  description: 'Gets a console message by its ID. You can get all messages by calling list_console_messages.',
173
209
  category: 'Debugging',
@@ -180,6 +216,30 @@ export const commands = {
180
216
  },
181
217
  },
182
218
  },
219
+ get_memory_snapshot_details: {
220
+ description: 'Loads a memory heapsnapshot and returns all available information including statistics, static data, and aggregated node information. Supports pagination for aggregates. (requires flag: --experimentalMemory=true)',
221
+ category: 'Memory',
222
+ args: {
223
+ filePath: {
224
+ name: 'filePath',
225
+ type: 'string',
226
+ description: 'A path to a .heapsnapshot file to read.',
227
+ required: true,
228
+ },
229
+ pageIdx: {
230
+ name: 'pageIdx',
231
+ type: 'number',
232
+ description: 'The page index for pagination of aggregates.',
233
+ required: false,
234
+ },
235
+ pageSize: {
236
+ name: 'pageSize',
237
+ type: 'number',
238
+ description: 'The page size for pagination of aggregates.',
239
+ required: false,
240
+ },
241
+ },
242
+ },
183
243
  get_network_request: {
184
244
  description: 'Gets a network request by an optional reqid, if omitted returns the currently selected request in the DevTools Network panel.',
185
245
  category: 'Network',
@@ -193,13 +253,43 @@ export const commands = {
193
253
  requestFilePath: {
194
254
  name: 'requestFilePath',
195
255
  type: 'string',
196
- description: 'The absolute or relative path to save the request body to. If omitted, the body is returned inline.',
256
+ description: 'The absolute or relative path to a .network-request file to save the request body to. If omitted, the body is returned inline.',
197
257
  required: false,
198
258
  },
199
259
  responseFilePath: {
200
260
  name: 'responseFilePath',
201
261
  type: 'string',
202
- description: 'The absolute or relative path to save the response body to. If omitted, the body is returned inline.',
262
+ description: 'The absolute or relative path to a .network-response file to save the response body to. If omitted, the body is returned inline.',
263
+ required: false,
264
+ },
265
+ },
266
+ },
267
+ get_nodes_by_class: {
268
+ description: 'Loads a memory heapsnapshot and returns instances of a specific class with their stable IDs. (requires flag: --experimentalMemory=true)',
269
+ category: 'Memory',
270
+ args: {
271
+ filePath: {
272
+ name: 'filePath',
273
+ type: 'string',
274
+ description: 'A path to a .heapsnapshot file to read.',
275
+ required: true,
276
+ },
277
+ uid: {
278
+ name: 'uid',
279
+ type: 'number',
280
+ description: 'The unique UID for the class, obtained from aggregates listing.',
281
+ required: true,
282
+ },
283
+ pageIdx: {
284
+ name: 'pageIdx',
285
+ type: 'number',
286
+ description: 'The page index for pagination.',
287
+ required: false,
288
+ },
289
+ pageSize: {
290
+ name: 'pageSize',
291
+ type: 'number',
292
+ description: 'The page size for pagination.',
203
293
  required: false,
204
294
  },
205
295
  },
@@ -241,8 +331,20 @@ export const commands = {
241
331
  },
242
332
  },
243
333
  },
334
+ install_extension: {
335
+ description: 'Installs a Chrome extension from the given path. (requires flag: --categoryExtensions=true)',
336
+ category: 'Extensions',
337
+ args: {
338
+ path: {
339
+ name: 'path',
340
+ type: 'string',
341
+ description: 'Absolute path to the unpacked extension folder.',
342
+ required: true,
343
+ },
344
+ },
345
+ },
244
346
  lighthouse_audit: {
245
- description: 'Get Lighthouse score and reports for accessibility, SEO and best practices. This excludes performance. For performance audits, run performance_start_trace',
347
+ description: 'Get Lighthouse score and reports for accessibility, SEO, best practices, and agentic browsing. This excludes performance. For performance audits, run performance_start_trace',
246
348
  category: 'Debugging',
247
349
  args: {
248
350
  mode: {
@@ -276,7 +378,7 @@ export const commands = {
276
378
  pageSize: {
277
379
  name: 'pageSize',
278
380
  type: 'integer',
279
- description: 'Maximum number of messages to return. When omitted, returns all requests.',
381
+ description: 'Maximum number of messages to return. When omitted, returns all messages.',
280
382
  required: false,
281
383
  },
282
384
  pageIdx: {
@@ -300,6 +402,11 @@ export const commands = {
300
402
  },
301
403
  },
302
404
  },
405
+ list_extensions: {
406
+ description: 'Lists all the Chrome extensions installed in the browser. This includes their name, ID, version, and enabled status. (requires flag: --categoryExtensions=true)',
407
+ category: 'Extensions',
408
+ args: {},
409
+ },
303
410
  list_network_requests: {
304
411
  description: 'List all requests for the currently selected page since the last navigation.',
305
412
  category: 'Network',
@@ -332,10 +439,27 @@ export const commands = {
332
439
  },
333
440
  },
334
441
  list_pages: {
335
- description: 'Get a list of pages open in the browser.',
442
+ description: 'Get a list of pages open in the browser.',
336
443
  category: 'Navigation automation',
337
444
  args: {},
338
445
  },
446
+ list_webmcp_tools: {
447
+ description: 'Lists all WebMCP tools the page exposes. (requires flag: --experimentalWebmcp=true)',
448
+ category: 'Debugging',
449
+ args: {},
450
+ },
451
+ load_memory_snapshot: {
452
+ description: 'Loads a memory heapsnapshot and returns snapshot summary stats. (requires flag: --experimentalMemory=true)',
453
+ category: 'Memory',
454
+ args: {
455
+ filePath: {
456
+ name: 'filePath',
457
+ type: 'string',
458
+ description: 'A path to a .heapsnapshot file to read.',
459
+ required: true,
460
+ },
461
+ },
462
+ },
339
463
  navigate_page: {
340
464
  description: 'Go to a URL, or back, forward, or reload. Use project URL if not specified otherwise.',
341
465
  category: 'Navigation automation',
@@ -484,6 +608,18 @@ export const commands = {
484
608
  },
485
609
  },
486
610
  },
611
+ reload_extension: {
612
+ description: 'Reloads an unpacked Chrome extension by its ID. (requires flag: --categoryExtensions=true)',
613
+ category: 'Extensions',
614
+ args: {
615
+ id: {
616
+ name: 'id',
617
+ type: 'string',
618
+ description: 'ID of the extension to reload.',
619
+ required: true,
620
+ },
621
+ },
622
+ },
487
623
  resize_page: {
488
624
  description: "Resizes the selected page's window so that the page has specified dimension",
489
625
  category: 'Emulation',
@@ -502,6 +638,23 @@ export const commands = {
502
638
  },
503
639
  },
504
640
  },
641
+ screencast_start: {
642
+ description: 'Starts recording a screencast (video) of the selected page in specified format. (requires flag: --experimentalScreencast=true)',
643
+ category: 'Debugging',
644
+ args: {
645
+ filePath: {
646
+ name: 'filePath',
647
+ type: 'string',
648
+ description: 'Output file path (.webm,.mp4 are supported). Uses mkdtemp to generate a unique path if not provided.',
649
+ required: false,
650
+ },
651
+ },
652
+ },
653
+ screencast_stop: {
654
+ description: 'Stops the active screencast recording on the selected page. (requires flag: --experimentalScreencast=true)',
655
+ category: 'Debugging',
656
+ args: {},
657
+ },
505
658
  select_page: {
506
659
  description: 'Select a page as a context for future tool calls.',
507
660
  category: 'Navigation automation',
@@ -521,8 +674,8 @@ export const commands = {
521
674
  },
522
675
  },
523
676
  take_memory_snapshot: {
524
- description: 'Capture a memory heapsnapshot of the currently selected page to memory leak debugging',
525
- category: 'Performance',
677
+ description: 'Capture a heap snapshot of the currently selected page. Use to analyze the memory distribution of JavaScript objects and debug memory leaks.',
678
+ category: 'Memory',
526
679
  args: {
527
680
  filePath: {
528
681
  name: 'filePath',
@@ -553,7 +706,7 @@ export const commands = {
553
706
  uid: {
554
707
  name: 'uid',
555
708
  type: 'string',
556
- description: 'The uid of an element on the page from the page content snapshot. If omitted takes a pages screenshot.',
709
+ description: 'The uid of an element on the page from the page content snapshot. If omitted, takes a page screenshot.',
557
710
  required: false,
558
711
  },
559
712
  fullPage: {
@@ -588,6 +741,18 @@ export const commands = {
588
741
  },
589
742
  },
590
743
  },
744
+ trigger_extension_action: {
745
+ description: 'Triggers the default action of an extension by its ID. (requires flag: --categoryExtensions=true)',
746
+ category: 'Extensions',
747
+ args: {
748
+ id: {
749
+ name: 'id',
750
+ type: 'string',
751
+ description: 'ID of the extension to trigger the action for.',
752
+ required: true,
753
+ },
754
+ },
755
+ },
591
756
  type_text: {
592
757
  description: 'Type text using keyboard into a previously focused input',
593
758
  category: 'Input automation',
@@ -606,6 +771,18 @@ export const commands = {
606
771
  },
607
772
  },
608
773
  },
774
+ uninstall_extension: {
775
+ description: 'Uninstalls a Chrome extension by its ID. (requires flag: --categoryExtensions=true)',
776
+ category: 'Extensions',
777
+ args: {
778
+ id: {
779
+ name: 'id',
780
+ type: 'string',
781
+ description: 'ID of the extension to uninstall.',
782
+ required: true,
783
+ },
784
+ },
785
+ },
609
786
  upload_file: {
610
787
  description: 'Upload a file through a provided element.',
611
788
  category: 'Input automation',
@@ -630,22 +807,5 @@ export const commands = {
630
807
  },
631
808
  },
632
809
  },
633
- wait_for: {
634
- description: 'Wait for the specified text to appear on the selected page.',
635
- category: 'Navigation automation',
636
- args: {
637
- text: {
638
- name: 'text',
639
- type: 'array',
640
- description: 'Non-empty list of texts. Resolves when any value appears on the page.',
641
- required: true,
642
- },
643
- timeout: {
644
- name: 'timeout',
645
- type: 'integer',
646
- description: 'Maximum wait time in milliseconds. If set to 0, the default timeout will be used.',
647
- required: false,
648
- },
649
- },
650
- },
651
810
  };
811
+ //# sourceMappingURL=chrome-devtools-cli-options.js.map
@@ -216,9 +216,10 @@ export const cliOptions = {
216
216
  default: false,
217
217
  describe: 'Set to true to include tools related to extensions. Note: This feature is currently only supported with a pipe connection. autoConnect, browserUrl, and wsEndpoint are not supported with this feature until 149 will be released.',
218
218
  },
219
- categoryInPageTools: {
219
+ categoryExperimentalInPage: {
220
220
  type: 'boolean',
221
221
  hidden: true,
222
+ default: false,
222
223
  describe: 'Set to true to enable tools exposed by the inspected page itself',
223
224
  },
224
225
  performanceCrux: {
@@ -344,3 +345,4 @@ export function parseArguments(version, argv = process.argv) {
344
345
  .version(version)
345
346
  .parseSync();
346
347
  }
348
+ //# sourceMappingURL=chrome-devtools-mcp-cli-options.js.map
@@ -35,3 +35,4 @@ logger('Chrome DevTools MCP Server connected');
35
35
  logDisclaimers(args);
36
36
  void clearcutLogger?.logDailyActiveIfNeeded();
37
37
  void clearcutLogger?.logServerStart(computeFlagUsage(args, cliOptions));
38
+ //# sourceMappingURL=chrome-devtools-mcp-main.js.map
@@ -20,3 +20,4 @@ if (major < 20) {
20
20
  process.exit(1);
21
21
  }
22
22
  await import('./chrome-devtools-mcp-main.js');
23
+ //# sourceMappingURL=chrome-devtools-mcp.js.map
@@ -28,20 +28,10 @@ const startCliOptions = {
28
28
  delete startCliOptions.autoConnect;
29
29
  // Missing CLI serialization.
30
30
  delete startCliOptions.viewport;
31
- // CLI is generated based on the default tool definitions. To enable conditional
32
- // tools, they need to be enabled during CLI generation.
33
- delete startCliOptions.experimentalPageIdRouting;
34
- delete startCliOptions.experimentalVision;
35
- delete startCliOptions.experimentalWebmcp;
36
- delete startCliOptions.experimentalInteropTools;
37
- delete startCliOptions.experimentalScreencast;
38
- delete startCliOptions.categoryEmulation;
39
- delete startCliOptions.categoryPerformance;
40
- delete startCliOptions.categoryNetwork;
41
- delete startCliOptions.categoryExtensions;
42
- // Always on in CLI.
31
+ // Change the defaults for the CLI.
43
32
  delete startCliOptions.experimentalStructuredContent;
44
- // Change the defaults.
33
+ delete startCliOptions.experimentalInteropTools;
34
+ delete startCliOptions.experimentalPageIdRouting;
45
35
  if (!('default' in cliOptions.headless)) {
46
36
  throw new Error('headless cli option unexpectedly does not have a default');
47
37
  }
@@ -51,6 +41,7 @@ if ('default' in cliOptions.isolated) {
51
41
  startCliOptions.headless.default = true;
52
42
  startCliOptions.isolated.description =
53
43
  'If specified, creates a temporary user-data-dir that is automatically cleaned up after the browser is closed. Defaults to true unless userDataDir is provided.';
44
+ startCliOptions.categoryExtensions.default = true;
54
45
  const y = yargs(hideBin(process.argv))
55
46
  .scriptName('chrome-devtools')
56
47
  .showHelpOnFail(true)
@@ -197,3 +188,4 @@ for (const [commandName, commandDef] of Object.entries(commands)) {
197
188
  });
198
189
  }
199
190
  await y.parse();
191
+ //# sourceMappingURL=chrome-devtools.js.map
@@ -201,3 +201,4 @@ export async function ensureBrowserLaunched(options) {
201
201
  browser = await launch(options);
202
202
  return browser;
203
203
  }
204
+ //# sourceMappingURL=browser.js.map
@@ -8,7 +8,7 @@ import fs from 'node:fs';
8
8
  import net from 'node:net';
9
9
  import { logger } from '../logger.js';
10
10
  import { PipeTransport } from '../third_party/index.js';
11
- import { saveTemporaryFile } from '../utils/files.js';
11
+ import { getTempFilePath } from '../utils/files.js';
12
12
  import { DAEMON_SCRIPT_PATH, getSocketPath, getPidFilePath, isDaemonRunning, } from './utils.js';
13
13
  const FILE_TIMEOUT = 10_000;
14
14
  /**
@@ -141,7 +141,8 @@ export async function handleResponse(response, format) {
141
141
  }
142
142
  const data = Buffer.from(imageData, 'base64');
143
143
  const name = crypto.randomUUID();
144
- const { filepath } = await saveTemporaryFile(data, `${name}${extension}`);
144
+ const filepath = await getTempFilePath(`${name}${extension}`);
145
+ fs.writeFileSync(filepath, data);
145
146
  chunks.push(`Saved to ${filepath}.`);
146
147
  }
147
148
  else {
@@ -150,3 +151,4 @@ export async function handleResponse(response, format) {
150
151
  }
151
152
  return format === 'md' ? chunks.join(' ') : JSON.stringify(chunks);
152
153
  }
154
+ //# sourceMappingURL=client.js.map
@@ -201,3 +201,4 @@ const started = startSocketServer().catch(error => {
201
201
  logger('Failed to start daemon server:', error);
202
202
  process.exit(1);
203
203
  });
204
+ //# sourceMappingURL=daemon.js.map
@@ -4,3 +4,4 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
  export {};
7
+ //# sourceMappingURL=types.js.map
@@ -112,3 +112,4 @@ export function serializeArgs(options, argv) {
112
112
  }
113
113
  return args;
114
114
  }
115
+ //# sourceMappingURL=utils.js.map
@@ -131,6 +131,36 @@ export class ConsoleFormatter {
131
131
  id: this.#id,
132
132
  };
133
133
  }
134
+ /**
135
+ * Groups consecutive messages with the same type, text, and argument count.
136
+ * Similar to Chrome DevTools' console grouping behavior.
137
+ */
138
+ static groupConsecutive(messages) {
139
+ const grouped = [];
140
+ for (const msg of messages) {
141
+ const prev = grouped[grouped.length - 1];
142
+ if (prev &&
143
+ prev.message instanceof ConsoleFormatter &&
144
+ msg instanceof ConsoleFormatter &&
145
+ prev.message.#type === msg.#type &&
146
+ prev.message.#text === msg.#text &&
147
+ prev.message.#argCount === msg.#argCount) {
148
+ prev.count++;
149
+ }
150
+ else {
151
+ grouped.push({ message: msg, count: 1 });
152
+ }
153
+ }
154
+ return grouped.map(({ message, count }) => count > 1 && message instanceof ConsoleFormatter
155
+ ? new GroupedConsoleFormatter({
156
+ id: message.#id,
157
+ type: message.#type,
158
+ text: message.#text,
159
+ argCount: message.#argCount,
160
+ isIgnored: message.isIgnored,
161
+ }, count)
162
+ : message);
163
+ }
134
164
  toJSONDetailed() {
135
165
  return {
136
166
  id: this.#id,
@@ -144,8 +174,24 @@ export class ConsoleFormatter {
144
174
  };
145
175
  }
146
176
  }
177
+ export class GroupedConsoleFormatter extends ConsoleFormatter {
178
+ #count;
179
+ constructor(params, count) {
180
+ super(params);
181
+ this.#count = count;
182
+ }
183
+ toString() {
184
+ return convertConsoleMessageConciseToString(this.toJSON());
185
+ }
186
+ toJSON() {
187
+ const json = super.toJSON();
188
+ json.count = this.#count;
189
+ return json;
190
+ }
191
+ }
147
192
  function convertConsoleMessageConciseToString(msg) {
148
- return `msgid=${msg.id} [${msg.type}] ${msg.text} (${msg.argsCount} args)`;
193
+ const countSuffix = msg.count && msg.count > 1 ? ` [${msg.count} times]` : '';
194
+ return `msgid=${msg.id} [${msg.type}] ${msg.text} (${msg.argsCount} args)${countSuffix}`;
149
195
  }
150
196
  function convertConsoleMessageConciseDetailedToString(msg) {
151
197
  const result = [
@@ -239,3 +285,4 @@ function formatCause(cause, formatter) {
239
285
  ...formatStackTraceInner(cause.stackTrace, cause.cause, formatter),
240
286
  ];
241
287
  }
288
+ //# sourceMappingURL=ConsoleFormatter.js.map
@@ -4,13 +4,28 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
  import { stableIdSymbol } from '../utils/id.js';
7
+ export function isNodeLike(item) {
8
+ return (typeof item === 'object' && item !== null && 'id' in item && 'name' in item);
9
+ }
7
10
  export class HeapSnapshotFormatter {
8
11
  #aggregates;
9
12
  constructor(aggregates) {
10
13
  this.#aggregates = aggregates;
11
14
  }
15
+ static formatNodes(items) {
16
+ const lines = [];
17
+ if (items.length > 0 && isNodeLike(items[0])) {
18
+ lines.push('id,name,type,distance,selfSize,retainedSize');
19
+ }
20
+ for (const item of items) {
21
+ if (isNodeLike(item)) {
22
+ lines.push(`${item.id},"${item.name}",${item.type},${item.distance},${item.selfSize},${item.retainedSize}`);
23
+ }
24
+ }
25
+ return lines.join('\n');
26
+ }
12
27
  #getSortedAggregates() {
13
- return Object.values(this.#aggregates).sort((a, b) => b.self - a.self);
28
+ return Object.values(this.#aggregates).sort((a, b) => b.maxRet - a.maxRet);
14
29
  }
15
30
  toString() {
16
31
  const sorted = this.#getSortedAggregates();
@@ -33,6 +48,7 @@ export class HeapSnapshotFormatter {
33
48
  }));
34
49
  }
35
50
  static sort(aggregates) {
36
- return Object.entries(aggregates).sort((a, b) => b[1].self - a[1].self);
51
+ return Object.entries(aggregates).sort((a, b) => b[1].maxRet - a[1].maxRet);
37
52
  }
38
53
  }
54
+ //# sourceMappingURL=HeapSnapshotFormatter.js.map
@@ -190,3 +190,4 @@ function convertIssueDetailedToString(issue) {
190
190
  result.push(`Message: issue> ${bodyParts.join('\n')}`);
191
191
  return result.join('\n');
192
192
  }
193
+ //# sourceMappingURL=IssueFormatter.js.map
@@ -233,3 +233,4 @@ function converNetworkRequestDetailedToStringDetailed(data) {
233
233
  }
234
234
  return response.join('\n');
235
235
  }
236
+ //# sourceMappingURL=NetworkFormatter.js.map
@@ -12,7 +12,7 @@ export class SnapshotFormatter {
12
12
  const chunks = [];
13
13
  const root = this.#snapshot.root;
14
14
  // Top-level content of the snapshot.
15
- if (this.#snapshot.verbose &&
15
+ if (!this.#snapshot.verbose &&
16
16
  this.#snapshot.hasSelectedElement &&
17
17
  !this.#snapshot.selectedElementUid) {
18
18
  chunks.push(`Note: there is a selected element in the DevTools Elements panel but it is not included into the current a11y tree snapshot.
@@ -132,3 +132,4 @@ const excludedAttributes = new Set([
132
132
  'backendNodeId',
133
133
  'loaderId',
134
134
  ]);
135
+ //# sourceMappingURL=SnapshotFormatter.js.map