browser-debugger-cli 0.11.0 → 0.13.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 (222) hide show
  1. package/.claude/skills/bdg/SKILL.md +4 -4
  2. package/README.md +143 -79
  3. package/dist/commands/cdp.d.ts +22 -1
  4. package/dist/commands/cdp.js +100 -43
  5. package/dist/commands/console.d.ts +12 -0
  6. package/dist/commands/console.js +62 -12
  7. package/dist/commands/css.d.ts +13 -0
  8. package/dist/commands/css.js +53 -0
  9. package/dist/commands/dom/DomElementResolver.d.ts +3 -1
  10. package/dist/commands/dom/DomElementResolver.js +10 -3
  11. package/dist/commands/dom/a11y.js +3 -2
  12. package/dist/commands/dom/audit.d.ts +14 -0
  13. package/dist/commands/dom/audit.js +87 -0
  14. package/dist/commands/dom/eval.d.ts +3 -2
  15. package/dist/commands/dom/eval.js +11 -5
  16. package/dist/commands/dom/form.js +10 -9
  17. package/dist/commands/dom/formInteraction.js +42 -11
  18. package/dist/commands/dom/get.js +8 -8
  19. package/dist/commands/dom/helpers/index.d.ts +1 -1
  20. package/dist/commands/dom/helpers/index.js +1 -1
  21. package/dist/commands/dom/helpers/keyAttributes.d.ts +3 -2
  22. package/dist/commands/dom/helpers/keyAttributes.js +6 -4
  23. package/dist/commands/dom/helpers/query.d.ts +27 -3
  24. package/dist/commands/dom/helpers/query.js +152 -64
  25. package/dist/commands/dom/helpers/screenshot.d.ts +1 -0
  26. package/dist/commands/dom/helpers/screenshot.js +169 -49
  27. package/dist/commands/dom/index.js +7 -2
  28. package/dist/commands/dom/query.d.ts +19 -2
  29. package/dist/commands/dom/query.js +37 -6
  30. package/dist/commands/dom/screenshot.js +12 -7
  31. package/dist/commands/dom/semanticUtils.d.ts +3 -2
  32. package/dist/commands/dom/semanticUtils.js +40 -9
  33. package/dist/commands/dom/wait.js +5 -3
  34. package/dist/commands/helpJson.d.ts +82 -19
  35. package/dist/commands/helpJson.js +112 -41
  36. package/dist/commands/helpTopic.d.ts +16 -1
  37. package/dist/commands/helpTopic.js +59 -1
  38. package/dist/commands/installSkill.d.ts +15 -5
  39. package/dist/commands/installSkill.js +86 -16
  40. package/dist/commands/network/list.js +22 -12
  41. package/dist/commands/optionBehaviors.js +53 -16
  42. package/dist/commands/page.js +7 -4
  43. package/dist/commands/peek.d.ts +7 -0
  44. package/dist/commands/peek.js +65 -23
  45. package/dist/commands/shared/daemonErrorHandler.d.ts +5 -2
  46. package/dist/commands/shared/daemonErrorHandler.js +20 -9
  47. package/dist/commands/shared/dataFetcher.d.ts +12 -4
  48. package/dist/commands/shared/dataFetcher.js +12 -4
  49. package/dist/commands/shared/followMode.d.ts +9 -1
  50. package/dist/commands/shared/followMode.js +22 -4
  51. package/dist/commands/shared/optionTypes.d.ts +9 -2
  52. package/dist/commands/shared/outputFile.js +6 -1
  53. package/dist/commands/start.d.ts +20 -5
  54. package/dist/commands/start.js +84 -23
  55. package/dist/commands/stop.d.ts +11 -0
  56. package/dist/commands/stop.js +24 -1
  57. package/dist/commands/tail.d.ts +7 -1
  58. package/dist/commands/tail.js +13 -62
  59. package/dist/commands.js +2 -0
  60. package/dist/connection/cdp.d.ts +7 -0
  61. package/dist/connection/cdp.js +9 -0
  62. package/dist/connection/launcher.js +3 -2
  63. package/dist/daemon/SessionController.js +6 -1
  64. package/dist/daemon/launcher.d.ts +3 -2
  65. package/dist/daemon/launcher.js +47 -3
  66. package/dist/daemon/session/Session.d.ts +4 -1
  67. package/dist/daemon/session/Session.js +33 -2
  68. package/dist/daemon/session/TelemetryStore.d.ts +8 -1
  69. package/dist/daemon/session/TelemetryStore.js +13 -1
  70. package/dist/daemon/session/commandRegistry.js +36 -14
  71. package/dist/daemon/session/interactions.d.ts +2 -1
  72. package/dist/daemon/session/interactions.js +13 -1
  73. package/dist/daemon/session/plugins.js +19 -53
  74. package/dist/daemon/session/teardown.js +1 -1
  75. package/dist/daemon.js +9234 -7222
  76. package/dist/errors/messages.d.ts +88 -15
  77. package/dist/errors/messages.js +177 -27
  78. package/dist/index.js +19322 -13961
  79. package/dist/ipc/client.d.ts +22 -2
  80. package/dist/ipc/client.js +34 -5
  81. package/dist/ipc/protocol/auditTypes.d.ts +135 -0
  82. package/dist/ipc/protocol/auditTypes.js +6 -0
  83. package/dist/ipc/protocol/commands.d.ts +35 -0
  84. package/dist/ipc/protocol/commands.js +2 -0
  85. package/dist/ipc/protocol/domTypes.d.ts +16 -0
  86. package/dist/ipc/protocol/inspectTypes.d.ts +73 -8
  87. package/dist/ipc/session/types.d.ts +2 -0
  88. package/dist/runtime/css/search.d.ts +39 -0
  89. package/dist/runtime/css/search.js +122 -0
  90. package/dist/runtime/dom/actionEffects.d.ts +9 -2
  91. package/dist/runtime/dom/actionEffects.js +30 -14
  92. package/dist/runtime/dom/audit.d.ts +19 -0
  93. package/dist/runtime/dom/audit.js +37 -0
  94. package/dist/runtime/dom/auditModel.d.ts +45 -0
  95. package/dist/runtime/dom/auditModel.js +220 -0
  96. package/dist/runtime/dom/auditScripts.d.ts +113 -0
  97. package/dist/runtime/dom/auditScripts.js +148 -0
  98. package/dist/runtime/dom/elementGeometry.d.ts +16 -3
  99. package/dist/runtime/dom/elementGeometry.js +49 -10
  100. package/dist/runtime/dom/elementInfo.d.ts +74 -17
  101. package/dist/runtime/dom/elementInfo.js +187 -34
  102. package/dist/runtime/dom/evalHelpers.d.ts +12 -2
  103. package/dist/runtime/dom/evalHelpers.js +67 -7
  104. package/dist/runtime/dom/formDiscovery.d.ts +6 -2
  105. package/dist/runtime/dom/formDiscovery.js +20 -3
  106. package/dist/runtime/dom/formFillHelpers/fill.js +8 -12
  107. package/dist/runtime/dom/formFillHelpers/pressKey.js +2 -2
  108. package/dist/runtime/dom/formFillHelpers/shared.d.ts +16 -10
  109. package/dist/runtime/dom/formFillHelpers/shared.js +19 -52
  110. package/dist/runtime/dom/formSubmitHelpers.js +4 -3
  111. package/dist/runtime/dom/frameLayout.js +1 -0
  112. package/dist/runtime/dom/inspect.d.ts +7 -0
  113. package/dist/runtime/dom/inspect.js +92 -28
  114. package/dist/runtime/dom/inspectAllStyles.d.ts +16 -4
  115. package/dist/runtime/dom/inspectAllStyles.js +90 -7
  116. package/dist/runtime/dom/inspectCascade.d.ts +19 -2
  117. package/dist/runtime/dom/inspectCascade.js +214 -44
  118. package/dist/runtime/dom/inspectCascadeModel.d.ts +8 -0
  119. package/dist/runtime/dom/inspectCascadeModel.js +108 -34
  120. package/dist/runtime/dom/inspectHints.d.ts +26 -3
  121. package/dist/runtime/dom/inspectHints.js +125 -9
  122. package/dist/runtime/dom/inspectModel.d.ts +5 -1
  123. package/dist/runtime/dom/inspectModel.js +37 -10
  124. package/dist/runtime/dom/inspectPaintModel.d.ts +50 -22
  125. package/dist/runtime/dom/inspectPaintModel.js +182 -68
  126. package/dist/runtime/dom/inspectRules.d.ts +19 -0
  127. package/dist/runtime/dom/inspectRules.js +21 -5
  128. package/dist/runtime/dom/inspectScripts.d.ts +112 -12
  129. package/dist/runtime/dom/inspectScripts.js +357 -32
  130. package/dist/runtime/dom/inspectTree.js +10 -2
  131. package/dist/runtime/dom/inspectWhyModel.d.ts +2 -1
  132. package/dist/runtime/dom/inspectWhyModel.js +52 -10
  133. package/dist/runtime/dom/layout.js +40 -16
  134. package/dist/runtime/dom/reactEventHelpers.d.ts +21 -4
  135. package/dist/runtime/dom/reactEventHelpers.js +90 -36
  136. package/dist/runtime/dom/targetNode.d.ts +18 -5
  137. package/dist/runtime/dom/targetNode.js +268 -8
  138. package/dist/runtime/dom/wait.js +2 -1
  139. package/dist/runtime/page/bdgWorld.d.ts +57 -0
  140. package/dist/runtime/page/bdgWorld.js +180 -0
  141. package/dist/runtime/page/emulation.d.ts +13 -4
  142. package/dist/runtime/page/emulation.js +69 -4
  143. package/dist/runtime/page/replacedBuiltins.d.ts +28 -0
  144. package/dist/runtime/page/replacedBuiltins.js +136 -0
  145. package/dist/runtime/page/userAgent.d.ts +17 -0
  146. package/dist/runtime/page/userAgent.js +57 -0
  147. package/dist/session/QueryCacheManager.d.ts +4 -1
  148. package/dist/session/QueryCacheManager.js +5 -2
  149. package/dist/session/chrome.d.ts +4 -1
  150. package/dist/session/chrome.js +7 -1
  151. package/dist/session/cleanup/staleSession.d.ts +21 -4
  152. package/dist/session/cleanup/staleSession.js +79 -9
  153. package/dist/session/cleanup/userCommands.d.ts +4 -1
  154. package/dist/session/cleanup/userCommands.js +10 -5
  155. package/dist/session/daemonSocket.d.ts +10 -0
  156. package/dist/session/daemonSocket.js +22 -0
  157. package/dist/session/lastSession.d.ts +6 -3
  158. package/dist/session/lastSession.js +11 -5
  159. package/dist/session/paths.d.ts +3 -1
  160. package/dist/session/paths.js +5 -5
  161. package/dist/session/portClaims.js +4 -3
  162. package/dist/session/sessionList.d.ts +13 -5
  163. package/dist/session/sessionList.js +31 -7
  164. package/dist/telemetry/a11y.js +2 -2
  165. package/dist/telemetry/console.d.ts +2 -1
  166. package/dist/telemetry/console.js +30 -21
  167. package/dist/telemetry/pageCrash.d.ts +26 -0
  168. package/dist/telemetry/pageCrash.js +53 -0
  169. package/dist/types.d.ts +20 -0
  170. package/dist/ui/formatters/audit.d.ts +19 -0
  171. package/dist/ui/formatters/audit.js +115 -0
  172. package/dist/ui/formatters/cdp.d.ts +138 -0
  173. package/dist/ui/formatters/cdp.js +131 -0
  174. package/dist/ui/formatters/console/chronological.js +3 -1
  175. package/dist/ui/formatters/console/follow.d.ts +2 -1
  176. package/dist/ui/formatters/console/follow.js +2 -2
  177. package/dist/ui/formatters/console/json.d.ts +2 -2
  178. package/dist/ui/formatters/console/json.js +11 -5
  179. package/dist/ui/formatters/console/shared.d.ts +30 -0
  180. package/dist/ui/formatters/console/shared.js +16 -0
  181. package/dist/ui/formatters/console/summarize.d.ts +9 -2
  182. package/dist/ui/formatters/console/summarize.js +40 -9
  183. package/dist/ui/formatters/console.d.ts +2 -1
  184. package/dist/ui/formatters/console.js +7 -5
  185. package/dist/ui/formatters/details.js +3 -1
  186. package/dist/ui/formatters/dom.d.ts +2 -2
  187. package/dist/ui/formatters/dom.js +10 -8
  188. package/dist/ui/formatters/helpFormatters.js +1 -1
  189. package/dist/ui/formatters/inspect.js +50 -17
  190. package/dist/ui/formatters/installSkill.d.ts +9 -1
  191. package/dist/ui/formatters/installSkill.js +32 -6
  192. package/dist/ui/formatters/layout.js +2 -1
  193. package/dist/ui/formatters/networkList.d.ts +1 -1
  194. package/dist/ui/formatters/networkList.js +1 -2
  195. package/dist/ui/formatters/preview.d.ts +2 -0
  196. package/dist/ui/formatters/preview.js +17 -7
  197. package/dist/ui/formatters/sessions.d.ts +2 -2
  198. package/dist/ui/formatters/sessions.js +9 -2
  199. package/dist/ui/formatters/status.js +1 -1
  200. package/dist/ui/logging/logger.d.ts +1 -1
  201. package/dist/ui/messages/commands.d.ts +168 -11
  202. package/dist/ui/messages/commands.js +245 -18
  203. package/dist/ui/messages/consoleMessages.d.ts +24 -0
  204. package/dist/ui/messages/consoleMessages.js +32 -0
  205. package/dist/ui/messages/preview.d.ts +12 -0
  206. package/dist/ui/messages/preview.js +18 -2
  207. package/dist/ui/messages/session.d.ts +13 -2
  208. package/dist/ui/messages/session.js +22 -3
  209. package/dist/utils/cssValues.js +36 -4
  210. package/dist/utils/decisionTrees.js +0 -5
  211. package/dist/utils/directories.d.ts +34 -0
  212. package/dist/utils/directories.js +88 -0
  213. package/dist/utils/display.d.ts +16 -0
  214. package/dist/utils/display.js +42 -0
  215. package/dist/utils/exitCodes.d.ts +1 -0
  216. package/dist/utils/exitCodes.js +6 -0
  217. package/dist/utils/process.d.ts +12 -0
  218. package/dist/utils/process.js +25 -0
  219. package/dist/utils/suggestions.d.ts +4 -2
  220. package/dist/utils/suggestions.js +7 -5
  221. package/dist/utils/taskMappings.js +1 -1
  222. package/package.json +3 -2
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Human-readable output of `bdg cdp`: search results, domain and method
3
+ * lists, method schemas and method results (`--json` prints the data as is).
4
+ */
5
+ import { joinLines, pluralize } from '../formatting.js';
6
+ /**
7
+ * First sentence (or line) of a protocol description.
8
+ *
9
+ * @param description - Description, possibly multi-line
10
+ * @returns Its first sentence, or an empty string
11
+ */
12
+ function firstSentence(description) {
13
+ const [line = ''] = (description ?? '').split('\n');
14
+ const end = line.indexOf('. ');
15
+ return end === -1 ? line : line.slice(0, end + 1);
16
+ }
17
+ /**
18
+ * `(experimental, deprecated)` tags, or nothing.
19
+ *
20
+ * @param flags - Protocol flags
21
+ * @returns Tag text with a leading space, or an empty string
22
+ */
23
+ function tags(flags) {
24
+ const names = [flags.experimental && 'experimental', flags.deprecated && 'deprecated'].filter(Boolean);
25
+ return names.length > 0 ? ` (${names.join(', ')})` : '';
26
+ }
27
+ /**
28
+ * Rows of a name column padded to one width, then the rest.
29
+ *
30
+ * @param rows - Name and text of each row
31
+ * @returns Indented lines
32
+ */
33
+ function columns(rows) {
34
+ const width = Math.max(0, ...rows.map(([name]) => name.length));
35
+ return rows.map(([name, text]) => ` ${name.padEnd(width)} ${text.trim()}`.trimEnd());
36
+ }
37
+ /**
38
+ * A type as written in the schema, e.g. "array<Cookie>".
39
+ *
40
+ * @param field - Parameter or return value
41
+ * @returns Type text
42
+ */
43
+ function typeText(field) {
44
+ return field.items ? `${field.type}<${field.items}>` : field.type;
45
+ }
46
+ /**
47
+ * Format `bdg cdp --search <query>`.
48
+ *
49
+ * @param data - Search result
50
+ * @returns Matching methods with their first description sentence
51
+ */
52
+ export function formatCdpSearch(data) {
53
+ if (data.count === 0) {
54
+ return joinLines(`No CDP method matches "${data.query}"`, 'List the domains: bdg cdp --list');
55
+ }
56
+ return joinLines(`${pluralize(data.count, 'method')} ${data.count === 1 ? 'matches' : 'match'} "${data.query}":`, ...columns(data.methods.map((m) => [m.name, `${firstSentence(m.description)}${tags(m)}`])), 'Parameters and an example: bdg cdp <Domain.method> --describe');
57
+ }
58
+ /**
59
+ * Format `bdg cdp --list`.
60
+ *
61
+ * @param data - All domains
62
+ * @returns One line per domain with its method and event counts
63
+ */
64
+ export function formatCdpDomains(data) {
65
+ return joinLines(`${pluralize(data.count, 'CDP domain')}:`, ...columns(data.domains.map((d) => [
66
+ d.name,
67
+ `${pluralize(d.commands, 'method')}, ${pluralize(d.events, 'event')}${tags(d)}`,
68
+ ])), 'Methods of a domain: bdg cdp <Domain> --list');
69
+ }
70
+ /**
71
+ * Format `bdg cdp <Domain> --list`.
72
+ *
73
+ * @param data - The domain's methods
74
+ * @returns One line per method with the first sentence of its description
75
+ */
76
+ export function formatCdpDomainMethods(data) {
77
+ return joinLines(`${data.domain}: ${pluralize(data.count, 'method')}`, firstSentence(data.description) || undefined, ...columns(data.methods.map((m) => [m.name, `${firstSentence(m.description)}${tags(m)}`])), `Parameters and an example: bdg cdp ${data.domain}.<method> --describe`);
78
+ }
79
+ /**
80
+ * Lines of a parameter or return value list.
81
+ *
82
+ * @param title - Section title
83
+ * @param fields - Parameters or return values
84
+ * @returns Title and one line per field, or nothing for an empty list
85
+ */
86
+ function fieldSection(title, fields) {
87
+ if (fields.length === 0)
88
+ return [];
89
+ return [
90
+ `${title}:`,
91
+ ...columns(fields.map((f) => [
92
+ `${f.name}${f.optional ? '?' : ''}: ${typeText(f)}`,
93
+ (f.description ?? '').replace(/\s*\n\s*/g, ' '),
94
+ ])),
95
+ ];
96
+ }
97
+ /**
98
+ * Format `bdg cdp <Domain.method> --describe` or `bdg cdp <Domain> --describe`.
99
+ *
100
+ * @param data - Method or domain description
101
+ * @returns Description, parameters (`?` = optional), returns, note and example
102
+ */
103
+ export function formatCdpDescription(data) {
104
+ if (data.type === 'domain') {
105
+ return joinLines(`${data.domain}: ${pluralize(data.commands, 'method')}, ${pluralize(data.events, 'event')}${tags(data)}`, data.description, data.note, data.nextStep);
106
+ }
107
+ return joinLines(`${data.name}${tags(data)}`, data.description, ...fieldSection('Parameters', data.parameters.map((p) => ({ ...p, optional: !p.required }))), ...fieldSection('Returns', data.returns), data.note && `Note: ${data.note}`, data.example && `Example: ${data.example.command}`);
108
+ }
109
+ /**
110
+ * Whether a CDP method returned nothing (null, undefined or an empty object).
111
+ *
112
+ * @param result - Method result
113
+ * @returns True for an empty result
114
+ */
115
+ export function isEmptyCdpResult(result) {
116
+ return (result === null ||
117
+ result === undefined ||
118
+ (typeof result === 'object' && Object.keys(result).length === 0));
119
+ }
120
+ /**
121
+ * Format a CDP method's result: the result object as indented JSON.
122
+ *
123
+ * @param data - Method and its result
124
+ * @returns The result, or a line saying the method returned nothing
125
+ */
126
+ export function formatCdpResult(data) {
127
+ return isEmptyCdpResult(data.result)
128
+ ? `${data.method}: done (no result data)`
129
+ : JSON.stringify(data.result, null, 2);
130
+ }
131
+ //# sourceMappingURL=cdp.js.map
@@ -3,7 +3,7 @@
3
3
  * level prefixes, and navigation reload markers.
4
4
  */
5
5
  import { OutputFormatter } from '../../formatting.js';
6
- import { consoleIndexGapNote } from '../../messages/consoleMessages.js';
6
+ import { consoleDroppedNote, consoleIndexGapNote } from '../../messages/consoleMessages.js';
7
7
  import { truncateByLength } from '../../../utils/strings.js';
8
8
  import { formatSourceLocation, formatTimestamp } from './shared.js';
9
9
  const MAX_LIST_TEXT_LENGTH = 200;
@@ -22,6 +22,8 @@ export function formatConsoleChronological(messages, options) {
22
22
  : `Console Messages (last ${displayMessages.length} of ${messages.length})${headerSuffix}`;
23
23
  fmt.text(header);
24
24
  fmt.separator('━', 50);
25
+ if (options.dropped)
26
+ fmt.text(consoleDroppedNote(options.dropped));
25
27
  if (displayMessages.length === 0) {
26
28
  fmt.text('No console messages');
27
29
  return fmt.build();
@@ -5,7 +5,8 @@
5
5
  import type { ConsoleMessage } from '../../../types.js';
6
6
  /**
7
7
  * Lines of the console stream: the new messages since the last poll, with
8
- * the stream header the first time and a separator after a navigation.
8
+ * a rule the first time (the stream banner is on stderr) and a separator
9
+ * after a navigation.
9
10
  *
10
11
  * @param messages - New messages
11
12
  * @param options - `header` the first time; `navigationId` when the page changed
@@ -6,7 +6,8 @@ import { OutputFormatter } from '../../formatting.js';
6
6
  import { formatSourceLocation, formatTimestamp } from './shared.js';
7
7
  /**
8
8
  * Lines of the console stream: the new messages since the last poll, with
9
- * the stream header the first time and a separator after a navigation.
9
+ * a rule the first time (the stream banner is on stderr) and a separator
10
+ * after a navigation.
10
11
  *
11
12
  * @param messages - New messages
12
13
  * @param options - `header` the first time; `navigationId` when the page changed
@@ -15,7 +16,6 @@ import { formatSourceLocation, formatTimestamp } from './shared.js';
15
16
  export function formatConsoleFollowLines(messages, options = {}) {
16
17
  const fmt = new OutputFormatter();
17
18
  if (options.header) {
18
- fmt.text('Streaming console... (Ctrl+C to stop)');
19
19
  fmt.separator('━', 40);
20
20
  if (messages.length === 0)
21
21
  fmt.text('Waiting for messages...');
@@ -6,8 +6,8 @@
6
6
  import type { ConsoleMessage } from '../../../types.js';
7
7
  import { type ConsoleFormatOptions, type ConsoleJsonOutput } from './shared.js';
8
8
  /**
9
- * Build the rich JSON output shape (summary + deduped errors/warnings, plus
10
- * the full message list when --list is requested).
9
+ * Build the rich JSON output shape (summary + the newest deduped
10
+ * errors/warnings, plus the message list when --list is requested).
11
11
  *
12
12
  * Returns a plain object so callers (e.g. runCommand's JSON envelope) can
13
13
  * embed it without re-parsing a stringified payload.
@@ -4,7 +4,7 @@
4
4
  * --list is set.
5
5
  */
6
6
  import { lastMessages } from './chronological.js';
7
- import { analyzeMessages, } from './shared.js';
7
+ import { analyzeMessages, newestGroups, } from './shared.js';
8
8
  function toJsonError(dedup, includeStackTrace) {
9
9
  const source = dedup.message.stackTrace?.[0];
10
10
  return {
@@ -25,18 +25,24 @@ function toJsonError(dedup, includeStackTrace) {
25
25
  };
26
26
  }
27
27
  /**
28
- * Build the rich JSON output shape (summary + deduped errors/warnings, plus
29
- * the full message list when --list is requested).
28
+ * Build the rich JSON output shape (summary + the newest deduped
29
+ * errors/warnings, plus the message list when --list is requested).
30
30
  *
31
31
  * Returns a plain object so callers (e.g. runCommand's JSON envelope) can
32
32
  * embed it without re-parsing a stringified payload.
33
33
  */
34
34
  export function buildConsoleJsonOutput(messages, options) {
35
35
  const { grouped, summary } = analyzeMessages(messages);
36
+ const errors = newestGroups(grouped.errors, options.groupLimit);
37
+ const warnings = newestGroups(grouped.warnings, options.groupLimit);
36
38
  const output = {
37
39
  summary,
38
- errors: grouped.errors.map((d) => toJsonError(d, true)),
39
- warnings: grouped.warnings.map((d) => toJsonError(d, false)),
40
+ errors: errors.shown.map((d) => toJsonError(d, true)),
41
+ warnings: warnings.shown.map((d) => toJsonError(d, false)),
42
+ ...(errors.more > 0 && { moreErrors: errors.more }),
43
+ ...(warnings.more > 0 && { moreWarnings: warnings.more }),
44
+ ...(options.dropped && { dropped: options.dropped }),
45
+ ...(options.pageCrashedAt !== undefined && { pageCrashedAt: options.pageCrashedAt }),
40
46
  };
41
47
  if (options.list)
42
48
  output.messages = lastMessages(messages, options.last);
@@ -57,7 +57,27 @@ export interface ConsoleFormatOptions {
57
57
  level?: ConsoleLevel | undefined;
58
58
  /** Messages between the first and last listed index that the filters left out */
59
59
  skipped?: ConsoleSkipped | undefined;
60
+ /** Distinct errors and warnings listed, the newest (0 = all; default {@link DEFAULT_GROUP_LIMIT}) */
61
+ groupLimit?: number | undefined;
62
+ /** Oldest messages the session dropped at its limit */
63
+ dropped?: number | undefined;
64
+ /** When the page crashed (epoch ms), while it is not loaded again */
65
+ pageCrashedAt?: number | undefined;
60
66
  }
67
+ /** Distinct errors and warnings the summary lists without `--last` */
68
+ export declare const DEFAULT_GROUP_LIMIT = 50;
69
+ /**
70
+ * The newest distinct messages of a level, and how many earlier ones are not
71
+ * listed.
72
+ *
73
+ * @param groups - Distinct messages, in order of first appearance
74
+ * @param limit - How many to keep (0 = all; undefined: {@link DEFAULT_GROUP_LIMIT})
75
+ * @returns Groups to list and the count left out
76
+ */
77
+ export declare function newestGroups(groups: DeduplicatedMessage[], limit: number | undefined): {
78
+ shown: DeduplicatedMessage[];
79
+ more: number;
80
+ };
61
81
  /**
62
82
  * Messages the page and level filters left out between the first and last
63
83
  * listed index (session indices then skip numbers).
@@ -90,8 +110,18 @@ export interface JsonErrorEntry {
90
110
  */
91
111
  export interface ConsoleJsonOutput {
92
112
  summary: ConsoleSummary;
113
+ /** The newest distinct errors (see `moreErrors`) */
93
114
  errors: JsonErrorEntry[];
115
+ /** The newest distinct warnings (see `moreWarnings`) */
94
116
  warnings: Omit<JsonErrorEntry, 'stackTrace'>[];
117
+ /** Earlier distinct errors not in `errors` (`--last 0` lists all) */
118
+ moreErrors?: number;
119
+ /** Earlier distinct warnings not in `warnings` */
120
+ moreWarnings?: number;
121
+ /** Oldest messages the session dropped at its limit (10000 are kept) */
122
+ dropped?: number;
123
+ /** When the page crashed (epoch ms), while it is not loaded again */
124
+ pageCrashedAt?: number;
95
125
  messages?: ConsoleMessage[];
96
126
  }
97
127
  /**
@@ -1,6 +1,22 @@
1
1
  /**
2
2
  * Shared types, constants, and helpers used by every console formatter.
3
3
  */
4
+ /** Distinct errors and warnings the summary lists without `--last` */
5
+ export const DEFAULT_GROUP_LIMIT = 50;
6
+ /**
7
+ * The newest distinct messages of a level, and how many earlier ones are not
8
+ * listed.
9
+ *
10
+ * @param groups - Distinct messages, in order of first appearance
11
+ * @param limit - How many to keep (0 = all; undefined: {@link DEFAULT_GROUP_LIMIT})
12
+ * @returns Groups to list and the count left out
13
+ */
14
+ export function newestGroups(groups, limit) {
15
+ const keep = limit ?? DEFAULT_GROUP_LIMIT;
16
+ if (keep === 0 || groups.length <= keep)
17
+ return { shown: groups, more: 0 };
18
+ return { shown: groups.slice(-keep), more: groups.length - keep };
19
+ }
4
20
  /**
5
21
  * Mapping from console message types to level categories.
6
22
  * Exported for use by console command filtering.
@@ -3,8 +3,15 @@
3
3
  * and shows info/debug/other as count-only footer entries.
4
4
  */
5
5
  import type { ConsoleMessage } from '../../../types.js';
6
+ import { type ConsoleFormatOptions } from './shared.js';
6
7
  /**
7
- * Format console output as smart summary (default mode).
8
+ * Format console output as smart summary (default mode): the newest distinct
9
+ * errors and warnings, counts of the rest, and a note when the session
10
+ * dropped its oldest messages.
11
+ *
12
+ * @param messages - Messages to summarise
13
+ * @param options - Distinct messages listed and messages dropped
14
+ * @returns Summary
8
15
  */
9
- export declare function formatConsoleSummary(messages: ConsoleMessage[]): string;
16
+ export declare function formatConsoleSummary(messages: ConsoleMessage[], options?: Pick<ConsoleFormatOptions, 'groupLimit' | 'dropped'>): string;
10
17
  //# sourceMappingURL=summarize.d.ts.map
@@ -3,13 +3,25 @@
3
3
  * and shows info/debug/other as count-only footer entries.
4
4
  */
5
5
  import { OutputFormatter, pluralize } from '../../formatting.js';
6
- import { analyzeMessages, formatCountPrefix, formatSectionHeader, formatSourceLocation, } from './shared.js';
7
- function renderErrorSection(fmt, errors, total) {
6
+ import { consoleDroppedNote, consoleMoreGroupsNote } from '../../messages/consoleMessages.js';
7
+ import { analyzeMessages, formatCountPrefix, formatSectionHeader, formatSourceLocation, newestGroups, } from './shared.js';
8
+ /**
9
+ * The errors: the newest distinct ones, with a note for the earlier ones.
10
+ *
11
+ * @param fmt - Output
12
+ * @param errors - Distinct errors in order of first appearance
13
+ * @param total - Errors logged
14
+ * @param limit - Distinct errors listed (0 = all)
15
+ */
16
+ function renderErrorSection(fmt, errors, total, limit) {
8
17
  if (errors.length === 0)
9
18
  return;
19
+ const { shown, more } = newestGroups(errors, limit);
10
20
  fmt.text(formatSectionHeader('Errors', errors.length, total));
11
21
  fmt.separator('─', 30);
12
- for (const { message, count } of errors) {
22
+ if (more > 0)
23
+ fmt.text(consoleMoreGroupsNote(more, 'error')).blank();
24
+ for (const { message, count } of shown) {
13
25
  fmt.text(`${formatCountPrefix(count)}${message.text}`);
14
26
  const source = formatSourceLocation(message.stackTrace);
15
27
  if (source) {
@@ -18,12 +30,23 @@ function renderErrorSection(fmt, errors, total) {
18
30
  fmt.blank();
19
31
  }
20
32
  }
21
- function renderWarningSection(fmt, warnings, total) {
33
+ /**
34
+ * The warnings: the newest distinct ones, with a note for the earlier ones.
35
+ *
36
+ * @param fmt - Output
37
+ * @param warnings - Distinct warnings in order of first appearance
38
+ * @param total - Warnings logged
39
+ * @param limit - Distinct warnings listed (0 = all)
40
+ */
41
+ function renderWarningSection(fmt, warnings, total, limit) {
22
42
  if (warnings.length === 0)
23
43
  return;
44
+ const { shown, more } = newestGroups(warnings, limit);
24
45
  fmt.text(formatSectionHeader('Warnings', warnings.length, total));
25
46
  fmt.separator('─', 30);
26
- for (const { message, count } of warnings) {
47
+ if (more > 0)
48
+ fmt.text(consoleMoreGroupsNote(more, 'warning'));
49
+ for (const { message, count } of shown) {
27
50
  fmt.text(`• ${formatCountPrefix(count)}${message.text}`);
28
51
  const source = formatSourceLocation(message.stackTrace);
29
52
  if (source)
@@ -43,16 +66,24 @@ function renderOtherSummary(fmt, summary) {
43
66
  }
44
67
  }
45
68
  /**
46
- * Format console output as smart summary (default mode).
69
+ * Format console output as smart summary (default mode): the newest distinct
70
+ * errors and warnings, counts of the rest, and a note when the session
71
+ * dropped its oldest messages.
72
+ *
73
+ * @param messages - Messages to summarise
74
+ * @param options - Distinct messages listed and messages dropped
75
+ * @returns Summary
47
76
  */
48
- export function formatConsoleSummary(messages) {
77
+ export function formatConsoleSummary(messages, options = {}) {
49
78
  const fmt = new OutputFormatter();
50
79
  const { grouped, summary } = analyzeMessages(messages);
51
80
  fmt.text('Console Summary');
52
81
  fmt.separator('━', 60);
82
+ if (options.dropped)
83
+ fmt.text(consoleDroppedNote(options.dropped));
53
84
  fmt.blank();
54
- renderErrorSection(fmt, grouped.errors, summary.errors.total);
55
- renderWarningSection(fmt, grouped.warnings, summary.warnings.total);
85
+ renderErrorSection(fmt, grouped.errors, summary.errors.total, options.groupLimit);
86
+ renderWarningSection(fmt, grouped.warnings, summary.warnings.total, options.groupLimit);
56
87
  if (grouped.errors.length === 0 && grouped.warnings.length === 0) {
57
88
  fmt.text('No errors or warnings found');
58
89
  fmt.blank();
@@ -16,7 +16,8 @@ export { formatConsoleSummary } from './console/summarize.js';
16
16
  /**
17
17
  * Format console output based on options. Routes to the per-mode formatter:
18
18
  * a `--level` filter lists the matching messages (the summary only shows
19
- * errors and warnings, so it would hide e.g. `--level info`).
19
+ * errors and warnings, so it would hide e.g. `--level info`). The text
20
+ * starts with a warning when the page crashed.
20
21
  */
21
22
  export declare function formatConsole(messages: ConsoleMessage[], options: ConsoleFormatOptions): string;
22
23
  //# sourceMappingURL=console.d.ts.map
@@ -5,6 +5,7 @@
5
5
  * the `formatConsole` dispatcher. The per-mode implementations live in
6
6
  * `./console/`.
7
7
  */
8
+ import { withPageCrashedNote } from '../messages/commands.js';
8
9
  import { formatConsoleChronological } from './console/chronological.js';
9
10
  import { formatConsoleJson } from './console/json.js';
10
11
  import { formatConsoleSummary } from './console/summarize.js';
@@ -16,15 +17,16 @@ export { formatConsoleSummary } from './console/summarize.js';
16
17
  /**
17
18
  * Format console output based on options. Routes to the per-mode formatter:
18
19
  * a `--level` filter lists the matching messages (the summary only shows
19
- * errors and warnings, so it would hide e.g. `--level info`).
20
+ * errors and warnings, so it would hide e.g. `--level info`). The text
21
+ * starts with a warning when the page crashed.
20
22
  */
21
23
  export function formatConsole(messages, options) {
22
24
  if (options.json) {
23
25
  return formatConsoleJson(messages, options);
24
26
  }
25
- if (options.list || options.level) {
26
- return formatConsoleChronological(messages, options);
27
- }
28
- return formatConsoleSummary(messages);
27
+ const body = options.list || options.level
28
+ ? formatConsoleChronological(messages, options)
29
+ : formatConsoleSummary(messages, options);
30
+ return withPageCrashedNote(body, options.pageCrashedAt);
29
31
  }
30
32
  //# sourceMappingURL=console.js.map
@@ -184,13 +184,15 @@ function requestSummaryRows(request) {
184
184
  }
185
185
  /**
186
186
  * Add a header block, a header sent several times one value per line
187
- * ({@link headerValueLines}).
187
+ * ({@link headerValueLines}); nothing when there are no headers.
188
188
  *
189
189
  * @param fmt - Formatter
190
190
  * @param title - Block title
191
191
  * @param headers - Headers to list
192
192
  */
193
193
  function addHeaders(fmt, title, headers) {
194
+ if (Object.keys(headers).length === 0)
195
+ return;
194
196
  fmt.text(title).separator('━', 70);
195
197
  Object.entries(headers).forEach(([key, value]) => headerValueLines(key, value).forEach((line) => fmt.text(` ${key}: ${line}`)));
196
198
  fmt.blank();
@@ -6,7 +6,7 @@ import type { DomQueryResult, DomGetResult, ScreenshotResult } from '../../types
6
6
  * Displays found nodes with their index, tag, identifying attributes
7
7
  * ({@link queryTagAttributes}), classes, and preview text
8
8
  * (plus where they are when outside the viewport or hidden, e.g.
9
- * `(below fold)`), up to {@link QUERY_DISPLAY_LIMIT} of them (no match is an error, exit 83).
9
+ * `(below fold)`), as many as `--limit` listed, with a note for the rest (no match is an error, exit 83).
10
10
  * One line of next commands follows; they take the match's index, so they
11
11
  * work for matches in shadow roots and iframes too.
12
12
  *
@@ -127,7 +127,7 @@ export declare function formatDomFrames(data: {
127
127
  * // Output: Screenshot saved to ./page.png (viewport only - page too tall)
128
128
  *
129
129
  * // An element whose floated children overflow it
130
- * // Output: Screenshot saved to ./el.png (grown from 940×37 to 940×285 to include content overflowing the element)
130
+ * // Output: Screenshot saved to ./el.png (grown from 940×37 to 940×285 to include what it paints outside its box (…))
131
131
  * ```
132
132
  */
133
133
  export declare function formatDomScreenshot(data: ScreenshotResult): string;
@@ -1,15 +1,13 @@
1
1
  import { keyAttributeItems } from './keyAttributes.js';
2
2
  import { OutputFormatter } from '../formatting.js';
3
- import { frameLabel, moreMatchesNote, framesStillLoadingNote, noFramesMessage, queryNextSteps, screenshotGrownNote, viewportPositionHint, } from '../messages/commands.js';
4
- /** Matches listed in human output (JSON has all of them) */
5
- const QUERY_DISPLAY_LIMIT = 50;
3
+ import { frameLabel, framesStillLoadingNote, noFramesMessage, queryMoreMatchesNote, queryViewportCheckedNote, queryNextSteps, screenshotGrownNote, screenshotScaledNote, viewportPositionHint, } from '../messages/commands.js';
6
4
  /**
7
5
  * Format DOM query results for human-readable output.
8
6
  *
9
7
  * Displays found nodes with their index, tag, identifying attributes
10
8
  * ({@link queryTagAttributes}), classes, and preview text
11
9
  * (plus where they are when outside the viewport or hidden, e.g.
12
- * `(below fold)`), up to {@link QUERY_DISPLAY_LIMIT} of them (no match is an error, exit 83).
10
+ * `(below fold)`), as many as `--limit` listed, with a note for the rest (no match is an error, exit 83).
13
11
  * One line of next commands follows; they take the match's index, so they
14
12
  * work for matches in shadow roots and iframes too.
15
13
  *
@@ -35,7 +33,7 @@ const QUERY_DISPLAY_LIMIT = 50;
35
33
  export function formatDomQuery(data) {
36
34
  const { count, nodes, selector } = data;
37
35
  const fmt = new OutputFormatter();
38
- const nodeLines = nodes.slice(0, QUERY_DISPLAY_LIMIT).map((node) => {
36
+ const nodeLines = nodes.map((node) => {
39
37
  const attributes = queryTagAttributes(node)
40
38
  .map((item) => ` ${item}`)
41
39
  .join('');
@@ -49,7 +47,8 @@ export function formatDomQuery(data) {
49
47
  return fmt
50
48
  .text(`Found ${count} node${count === 1 ? '' : 's'} matching "${selector}":`)
51
49
  .list(nodeLines)
52
- .list(count > QUERY_DISPLAY_LIMIT ? [moreMatchesNote(count - QUERY_DISPLAY_LIMIT)] : [])
50
+ .list(data.omitted ? [queryMoreMatchesNote(data.omitted, data.indexed)] : [])
51
+ .list(data.viewportChecked ? [queryViewportCheckedNote(data.viewportChecked)] : [])
53
52
  .tip(queryNextSteps(exampleIndex))
54
53
  .build();
55
54
  }
@@ -212,7 +211,7 @@ export function formatDomFrames(data) {
212
211
  * // Output: Screenshot saved to ./page.png (viewport only - page too tall)
213
212
  *
214
213
  * // An element whose floated children overflow it
215
- * // Output: Screenshot saved to ./el.png (grown from 940×37 to 940×285 to include content overflowing the element)
214
+ * // Output: Screenshot saved to ./el.png (grown from 940×37 to 940×285 to include what it paints outside its box (…))
216
215
  * ```
217
216
  */
218
217
  export function formatDomScreenshot(data) {
@@ -221,7 +220,10 @@ export function formatDomScreenshot(data) {
221
220
  output += ' (viewport only - page too tall)';
222
221
  }
223
222
  if (data.element?.captured) {
224
- output += ` (${screenshotGrownNote(data.element.bounds, data.element.captured)})`;
223
+ output += ` (${screenshotGrownNote(data.element.bounds, data.element.captured, data.element.padding)})`;
224
+ }
225
+ if (data.resized && data.originalWidth !== undefined && data.originalHeight !== undefined) {
226
+ output += ` (${screenshotScaledNote(data.originalWidth, data.originalHeight, data.width, data.height)})`;
225
227
  }
226
228
  return output;
227
229
  }
@@ -17,7 +17,7 @@ import { section } from '../formatting.js';
17
17
  export function buildAgentDiscoveryHelp() {
18
18
  const counts = getProtocolCounts();
19
19
  return section('For AI Agents:', [
20
- 'bdg --help --json Machine-readable command schema',
20
+ 'bdg --help --json Commands, flags, exit codes (bdg <cmd> --help --json: details)',
21
21
  `bdg cdp --list List all ${counts.domains} CDP domains`,
22
22
  `bdg cdp --search <term> Search ${counts.methods} CDP methods`,
23
23
  ]);