browser-debugger-cli 0.12.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 (180) hide show
  1. package/.claude/skills/bdg/SKILL.md +4 -4
  2. package/README.md +1 -0
  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/dom/DomElementResolver.d.ts +3 -1
  8. package/dist/commands/dom/DomElementResolver.js +10 -3
  9. package/dist/commands/dom/a11y.js +3 -2
  10. package/dist/commands/dom/eval.d.ts +3 -2
  11. package/dist/commands/dom/eval.js +11 -5
  12. package/dist/commands/dom/form.js +10 -9
  13. package/dist/commands/dom/formInteraction.js +8 -7
  14. package/dist/commands/dom/get.js +8 -8
  15. package/dist/commands/dom/helpers/index.d.ts +1 -1
  16. package/dist/commands/dom/helpers/index.js +1 -1
  17. package/dist/commands/dom/helpers/query.d.ts +27 -3
  18. package/dist/commands/dom/helpers/query.js +152 -64
  19. package/dist/commands/dom/helpers/screenshot.js +13 -13
  20. package/dist/commands/dom/index.js +4 -2
  21. package/dist/commands/dom/query.d.ts +19 -2
  22. package/dist/commands/dom/query.js +37 -6
  23. package/dist/commands/dom/screenshot.js +2 -1
  24. package/dist/commands/dom/semanticUtils.d.ts +3 -2
  25. package/dist/commands/dom/semanticUtils.js +40 -9
  26. package/dist/commands/helpJson.d.ts +82 -19
  27. package/dist/commands/helpJson.js +111 -40
  28. package/dist/commands/helpTopic.d.ts +16 -1
  29. package/dist/commands/helpTopic.js +59 -1
  30. package/dist/commands/installSkill.d.ts +15 -5
  31. package/dist/commands/installSkill.js +86 -16
  32. package/dist/commands/network/list.js +22 -12
  33. package/dist/commands/optionBehaviors.js +33 -11
  34. package/dist/commands/shared/daemonErrorHandler.d.ts +5 -2
  35. package/dist/commands/shared/daemonErrorHandler.js +20 -9
  36. package/dist/commands/shared/dataFetcher.d.ts +12 -4
  37. package/dist/commands/shared/dataFetcher.js +12 -4
  38. package/dist/commands/shared/followMode.d.ts +9 -1
  39. package/dist/commands/shared/followMode.js +22 -4
  40. package/dist/commands/shared/optionTypes.d.ts +4 -1
  41. package/dist/commands/shared/outputFile.js +6 -1
  42. package/dist/commands/start.d.ts +7 -5
  43. package/dist/commands/start.js +65 -21
  44. package/dist/commands/stop.d.ts +11 -0
  45. package/dist/commands/stop.js +24 -1
  46. package/dist/commands.js +1 -1
  47. package/dist/connection/cdp.d.ts +7 -0
  48. package/dist/connection/cdp.js +9 -0
  49. package/dist/connection/launcher.js +3 -2
  50. package/dist/daemon/SessionController.js +6 -1
  51. package/dist/daemon/launcher.d.ts +3 -2
  52. package/dist/daemon/launcher.js +47 -3
  53. package/dist/daemon/session/Session.d.ts +4 -1
  54. package/dist/daemon/session/Session.js +33 -2
  55. package/dist/daemon/session/TelemetryStore.d.ts +8 -1
  56. package/dist/daemon/session/TelemetryStore.js +13 -1
  57. package/dist/daemon/session/commandRegistry.js +29 -13
  58. package/dist/daemon/session/interactions.d.ts +2 -1
  59. package/dist/daemon/session/interactions.js +13 -1
  60. package/dist/daemon/session/plugins.js +16 -2
  61. package/dist/daemon/session/teardown.js +1 -1
  62. package/dist/daemon.js +1622 -748
  63. package/dist/errors/messages.d.ts +54 -11
  64. package/dist/errors/messages.js +109 -22
  65. package/dist/index.js +13733 -8796
  66. package/dist/ipc/client.d.ts +18 -2
  67. package/dist/ipc/client.js +26 -5
  68. package/dist/ipc/protocol/auditTypes.d.ts +8 -2
  69. package/dist/ipc/protocol/commands.d.ts +12 -0
  70. package/dist/ipc/protocol/domTypes.d.ts +12 -0
  71. package/dist/ipc/protocol/inspectTypes.d.ts +2 -0
  72. package/dist/ipc/session/types.d.ts +2 -0
  73. package/dist/runtime/dom/actionEffects.d.ts +5 -1
  74. package/dist/runtime/dom/actionEffects.js +26 -14
  75. package/dist/runtime/dom/audit.js +3 -2
  76. package/dist/runtime/dom/auditModel.js +6 -1
  77. package/dist/runtime/dom/auditScripts.d.ts +9 -3
  78. package/dist/runtime/dom/auditScripts.js +41 -5
  79. package/dist/runtime/dom/elementGeometry.d.ts +10 -3
  80. package/dist/runtime/dom/elementGeometry.js +27 -4
  81. package/dist/runtime/dom/elementInfo.d.ts +74 -18
  82. package/dist/runtime/dom/elementInfo.js +187 -40
  83. package/dist/runtime/dom/evalHelpers.d.ts +12 -2
  84. package/dist/runtime/dom/evalHelpers.js +67 -7
  85. package/dist/runtime/dom/formDiscovery.d.ts +6 -2
  86. package/dist/runtime/dom/formDiscovery.js +20 -3
  87. package/dist/runtime/dom/formFillHelpers/fill.js +7 -11
  88. package/dist/runtime/dom/formFillHelpers/pressKey.js +2 -2
  89. package/dist/runtime/dom/formFillHelpers/shared.d.ts +16 -10
  90. package/dist/runtime/dom/formFillHelpers/shared.js +19 -52
  91. package/dist/runtime/dom/formSubmitHelpers.js +4 -3
  92. package/dist/runtime/dom/frameLayout.js +1 -0
  93. package/dist/runtime/dom/inspect.js +5 -6
  94. package/dist/runtime/dom/inspectAllStyles.js +1 -0
  95. package/dist/runtime/dom/inspectHints.d.ts +1 -1
  96. package/dist/runtime/dom/inspectModel.d.ts +2 -1
  97. package/dist/runtime/dom/inspectModel.js +7 -3
  98. package/dist/runtime/dom/inspectPaintModel.d.ts +2 -0
  99. package/dist/runtime/dom/inspectPaintModel.js +3 -1
  100. package/dist/runtime/dom/inspectScripts.d.ts +29 -2
  101. package/dist/runtime/dom/inspectScripts.js +49 -10
  102. package/dist/runtime/dom/layout.js +9 -7
  103. package/dist/runtime/dom/reactEventHelpers.d.ts +14 -4
  104. package/dist/runtime/dom/reactEventHelpers.js +63 -27
  105. package/dist/runtime/dom/targetNode.d.ts +18 -5
  106. package/dist/runtime/dom/targetNode.js +268 -8
  107. package/dist/runtime/dom/wait.js +2 -1
  108. package/dist/runtime/page/bdgWorld.d.ts +57 -0
  109. package/dist/runtime/page/bdgWorld.js +180 -0
  110. package/dist/runtime/page/replacedBuiltins.d.ts +28 -0
  111. package/dist/runtime/page/replacedBuiltins.js +136 -0
  112. package/dist/session/QueryCacheManager.d.ts +4 -1
  113. package/dist/session/QueryCacheManager.js +5 -2
  114. package/dist/session/chrome.d.ts +4 -1
  115. package/dist/session/chrome.js +7 -1
  116. package/dist/session/cleanup/staleSession.d.ts +21 -4
  117. package/dist/session/cleanup/staleSession.js +79 -9
  118. package/dist/session/cleanup/userCommands.d.ts +4 -1
  119. package/dist/session/cleanup/userCommands.js +10 -5
  120. package/dist/session/daemonSocket.d.ts +10 -0
  121. package/dist/session/daemonSocket.js +22 -0
  122. package/dist/session/lastSession.d.ts +6 -3
  123. package/dist/session/lastSession.js +11 -5
  124. package/dist/session/paths.d.ts +3 -1
  125. package/dist/session/paths.js +5 -5
  126. package/dist/session/portClaims.js +4 -3
  127. package/dist/session/sessionList.d.ts +13 -5
  128. package/dist/session/sessionList.js +31 -7
  129. package/dist/telemetry/a11y.js +2 -2
  130. package/dist/telemetry/console.d.ts +2 -1
  131. package/dist/telemetry/console.js +30 -21
  132. package/dist/telemetry/pageCrash.d.ts +26 -0
  133. package/dist/telemetry/pageCrash.js +53 -0
  134. package/dist/types.d.ts +16 -0
  135. package/dist/ui/formatters/audit.js +14 -5
  136. package/dist/ui/formatters/cdp.d.ts +138 -0
  137. package/dist/ui/formatters/cdp.js +131 -0
  138. package/dist/ui/formatters/console/chronological.js +3 -1
  139. package/dist/ui/formatters/console/follow.d.ts +2 -1
  140. package/dist/ui/formatters/console/follow.js +2 -2
  141. package/dist/ui/formatters/console/json.d.ts +2 -2
  142. package/dist/ui/formatters/console/json.js +11 -5
  143. package/dist/ui/formatters/console/shared.d.ts +30 -0
  144. package/dist/ui/formatters/console/shared.js +16 -0
  145. package/dist/ui/formatters/console/summarize.d.ts +9 -2
  146. package/dist/ui/formatters/console/summarize.js +40 -9
  147. package/dist/ui/formatters/console.d.ts +2 -1
  148. package/dist/ui/formatters/console.js +7 -5
  149. package/dist/ui/formatters/details.js +3 -1
  150. package/dist/ui/formatters/dom.d.ts +1 -1
  151. package/dist/ui/formatters/dom.js +5 -6
  152. package/dist/ui/formatters/helpFormatters.js +1 -1
  153. package/dist/ui/formatters/inspect.js +9 -3
  154. package/dist/ui/formatters/installSkill.d.ts +9 -1
  155. package/dist/ui/formatters/installSkill.js +32 -6
  156. package/dist/ui/formatters/layout.js +2 -1
  157. package/dist/ui/formatters/networkList.d.ts +1 -1
  158. package/dist/ui/formatters/networkList.js +1 -2
  159. package/dist/ui/formatters/preview.d.ts +2 -0
  160. package/dist/ui/formatters/preview.js +17 -7
  161. package/dist/ui/formatters/sessions.d.ts +2 -2
  162. package/dist/ui/formatters/sessions.js +9 -2
  163. package/dist/ui/logging/logger.d.ts +1 -1
  164. package/dist/ui/messages/commands.d.ts +124 -4
  165. package/dist/ui/messages/commands.js +162 -7
  166. package/dist/ui/messages/consoleMessages.d.ts +24 -0
  167. package/dist/ui/messages/consoleMessages.js +32 -0
  168. package/dist/ui/messages/preview.d.ts +6 -0
  169. package/dist/ui/messages/preview.js +9 -1
  170. package/dist/ui/messages/session.d.ts +13 -2
  171. package/dist/ui/messages/session.js +22 -3
  172. package/dist/utils/directories.d.ts +34 -0
  173. package/dist/utils/directories.js +88 -0
  174. package/dist/utils/display.d.ts +16 -0
  175. package/dist/utils/display.js +42 -0
  176. package/dist/utils/exitCodes.d.ts +1 -0
  177. package/dist/utils/exitCodes.js +6 -0
  178. package/dist/utils/process.d.ts +12 -0
  179. package/dist/utils/process.js +25 -0
  180. package/package.json +1 -1
@@ -26,6 +26,15 @@ export function chromeClosedMessage(pid) {
26
26
  export function orphanedDaemonsCleanedMessage(count) {
27
27
  return `Cleaned up ${count} orphaned daemon process${count === 1 ? '' : 'es'}`;
28
28
  }
29
+ /**
30
+ * Where `bdg install-skill` kept the copy it replaced.
31
+ *
32
+ * @param path - Backup path, as the user would type it
33
+ * @returns One line
34
+ */
35
+ export function skillBackupMessage(path) {
36
+ return `previous copy kept in ${path}`;
37
+ }
29
38
  /**
30
39
  * Warning shown when a click falls back from mouse events to `el.click()`.
31
40
  *
@@ -195,6 +204,27 @@ export function moreMatchesNote(hidden, jsonLimit) {
195
204
  const where = jsonLimit === undefined ? 'use --json for all' : `--json lists the first ${jsonLimit}`;
196
205
  return `... and ${hidden} more (${where})`;
197
206
  }
207
+ /**
208
+ * Note under `dom query` matches cut by `--limit`.
209
+ *
210
+ * @param omitted - Matches not listed
211
+ * @param indexed - Matches usable by index, when not all of them
212
+ * @returns e.g. `... and 49953 more (--limit 0 lists all; indices 0-999 work with other commands)`
213
+ */
214
+ export function queryMoreMatchesNote(omitted, indexed) {
215
+ const indices = indexed === undefined ? '' : `; indices 0-${indexed - 1} work with other commands`;
216
+ return `... and ${omitted} more (--limit 0 lists all${indices})`;
217
+ }
218
+ /**
219
+ * Note under `dom query` matches listed past those whose viewport position
220
+ * was checked.
221
+ *
222
+ * @param checked - First matches checked
223
+ * @returns e.g. `Visibility is checked for the first 100 matches only; bdg dom layout <index> checks any of them`
224
+ */
225
+ export function queryViewportCheckedNote(checked) {
226
+ return `Visibility is checked for the first ${checked} matches only; ${sessionCommand('bdg dom layout <index>')} checks any of them`;
227
+ }
198
228
  /**
199
229
  * Note under a list of a11y query matches cut by `--limit`.
200
230
  *
@@ -253,6 +283,37 @@ export const LAYOUT_REASONS = {
253
283
  /** Joins an invisible reason to the ancestor causing it */
254
284
  on: ' on ',
255
285
  };
286
+ /** Why an out-of-view text's contrast in `dom audit` is approximate: none of its ancestors paints a background */
287
+ export const AUDIT_OUT_OF_VIEW_RISK = 'only its ancestors were checked';
288
+ /**
289
+ * `dom audit contrast` note for text that looks below the level but cannot
290
+ * be measured exactly (over an image, blended, under or over another layer).
291
+ *
292
+ * @param count - How many such texts
293
+ * @returns e.g. `(+12 more may be below it but cannot be measured: text over images or blended layers; check them with bdg dom inspect)`
294
+ */
295
+ export function auditUncertainContrastNote(count) {
296
+ return `(+${count} more may be below it but cannot be measured: text over images or blended layers; check them with ${sessionCommand('bdg dom inspect <element>')})`;
297
+ }
298
+ /**
299
+ * `dom audit animations` note for canvas elements, whose script-drawn
300
+ * animations it cannot see.
301
+ *
302
+ * @param count - Visible canvas elements
303
+ * @returns e.g. `(+ 2 canvas elements: animations drawn by scripts on them are not listed)`
304
+ */
305
+ export function auditCanvasNote(count) {
306
+ return `(+ ${count} canvas ${count === 1 ? 'element' : 'elements'}: animations drawn by scripts on ${count === 1 ? 'it are' : 'them are'} not listed)`;
307
+ }
308
+ /**
309
+ * A mask over an element, for `dom layout` and `dom inspect`.
310
+ *
311
+ * @param masked - The mask, e.g. `mask-image on div.hero`
312
+ * @returns e.g. `masked by mask-image on div.hero`
313
+ */
314
+ export function maskedText(masked) {
315
+ return `masked by ${masked}`;
316
+ }
256
317
  /** Start of the off-screen reason of an element a scroll-locked page hides ({@link scrollLockedReason}) */
257
318
  const SCROLL_LOCKED_PREFIX = 'page scrolling is locked';
258
319
  /**
@@ -391,16 +452,63 @@ export function colorSchemeLabel(scheme, emulated) {
391
452
  const source = emulated ? 'emulated' : 'from the system setting';
392
453
  return `prefers-color-scheme: ${scheme} (${source})`;
393
454
  }
455
+ /**
456
+ * Suggestion for an action that failed on a page that replaced built-ins
457
+ * bdg's page scripts use.
458
+ *
459
+ * @param replaced - Their dotted names (all are named)
460
+ * @returns Suggestion naming them and a way around
461
+ */
462
+ export function brokenByReplacedBuiltinsSuggestion(replaced) {
463
+ return `The page replaced built-ins bdg's action script uses (${replaced.join(', ')}), which may have broken it; if so, act on the element with ${sessionCommand("bdg dom eval '…'")} instead`;
464
+ }
465
+ /**
466
+ * Warning on an action when the page replaced built-ins bdg's page scripts
467
+ * use (polyfills, old frameworks, anti-bot scripts). Names the first four;
468
+ * the action's `replacedBuiltins` (JSON) lists them all.
469
+ *
470
+ * @param replaced - Their dotted names
471
+ * @returns e.g. `the page replaced built-ins bdg's scripts use (Element.prototype.querySelectorAll); bdg found the element in its own world, but the action runs in the page's and may misbehave`
472
+ */
473
+ export function replacedBuiltinsWarning(replaced) {
474
+ const shown = replaced.slice(0, 4).join(', ') +
475
+ (replaced.length > 4 ? `, +${replaced.length - 4} more (see --json)` : '');
476
+ return `the page replaced built-ins bdg's scripts use (${shown}); bdg found the element in its own world, but the action runs in the page's and may misbehave`;
477
+ }
394
478
  /**
395
479
  * First line of `bdg status` for a running session.
396
480
  *
397
- * @param page - URL and title of the page, when the session reported them
398
- * @returns e.g. `Session active: https://example.com/ — Example Domain`
481
+ * @param page - URL and title of the page, when the session reported them,
482
+ * and when it crashed
483
+ * @returns e.g. `Session active: https://example.com/ — Example Domain`, with
484
+ * a crash warning on a second line
399
485
  */
400
486
  export function sessionActiveLine(page) {
401
487
  if (!page)
402
488
  return 'Session active';
403
- return `Session active: ${page.url}${page.title ? ` — ${page.title}` : ''}`;
489
+ const line = `Session active: ${page.url}${page.title ? ` — ${page.title}` : ''}`;
490
+ return page.crashedAt === undefined ? line : `${line}\n${pageCrashedNote(page.crashedAt)}`;
491
+ }
492
+ /**
493
+ * Warning that the session's page crashed, for `bdg status`, `bdg peek`,
494
+ * `bdg console` and `bdg network list`.
495
+ *
496
+ * @param crashedAt - When it crashed (epoch ms)
497
+ * @returns e.g. `⚠ The page crashed at 18:42:10 (renderer gone); bdg page reload brings it back`
498
+ */
499
+ export function pageCrashedNote(crashedAt) {
500
+ return `⚠ The page crashed at ${new Date(crashedAt).toLocaleTimeString()} (renderer gone); ${sessionCommand('bdg page reload')} brings it back`;
501
+ }
502
+ /**
503
+ * Put the page-crashed warning before a view of collected data, when the
504
+ * page crashed (what is shown was collected before).
505
+ *
506
+ * @param body - The view
507
+ * @param crashedAt - When the page crashed (epoch ms), if it did
508
+ * @returns The view, after the warning when the page crashed
509
+ */
510
+ export function withPageCrashedNote(body, crashedAt) {
511
+ return crashedAt === undefined ? body : `${pageCrashedNote(crashedAt)}\n\n${body}`;
404
512
  }
405
513
  /**
406
514
  * Page dimensions line of `bdg dom layout`.
@@ -524,6 +632,7 @@ export function inspectVisibilityBadges(visibility) {
524
632
  visibility.hidden && `[hidden: ${visibility.hidden}]`,
525
633
  visibility.offscreen && `[offscreen: ${visibility.offscreen}]`,
526
634
  visibility.coveredBy && `[${coverText(visibility.coveredBy, visibility.coverTransparent)}]`,
635
+ visibility.masked && `[${maskedText(visibility.masked)}]`,
527
636
  ].filter((badge) => Boolean(badge));
528
637
  }
529
638
  /**
@@ -674,6 +783,19 @@ export function dialogConsoleText(dialog) {
674
783
  const kind = dialog.type === 'beforeunload' ? 'beforeunload' : `${dialog.type}()`;
675
784
  return `${kind} dialog accepted${dialog.message ? `: "${dialog.message}"` : ''}`;
676
785
  }
786
+ /**
787
+ * How far a pointer action scrolled the page to reach its element.
788
+ *
789
+ * @param scrolledBy - Page scroll (CSS px)
790
+ * @returns e.g. `page down 1240px to reach it`, `page right 300px, up 80px to reach it`
791
+ */
792
+ export function pointerScrollText(scrolledBy) {
793
+ const parts = [
794
+ scrolledBy.y !== 0 && `${scrolledBy.y > 0 ? 'down' : 'up'} ${Math.abs(scrolledBy.y)}px`,
795
+ scrolledBy.x !== 0 && `${scrolledBy.x > 0 ? 'right' : 'left'} ${Math.abs(scrolledBy.x)}px`,
796
+ ].filter(Boolean);
797
+ return `page ${parts.join(', ')} to reach it`;
798
+ }
677
799
  /** Headline of each pointer action, e.g. "Element Double-clicked" */
678
800
  export const POINTER_ACTION_DONE = {
679
801
  click: 'Clicked',
@@ -912,13 +1034,17 @@ export function elementTextLine(text) {
912
1034
  *
913
1035
  * @param children - First child elements, e.g. `iframe#app`
914
1036
  * @param count - Number of child elements
915
- * @returns e.g. `No text; holds 1 element: iframe (see its HTML with --raw)`
1037
+ * @param inShadowRoot - The children are those of its shadow root (`--raw` does not show them)
1038
+ * @returns e.g. `No text; holds 1 element: iframe (see its HTML with --raw)`,
1039
+ * `No text; its shadow root holds 1 element: button "Close" (see it with bdg dom inspect)`
916
1040
  */
917
- export function emptyElementLine(children, count) {
1041
+ export function emptyElementLine(children, count, inShadowRoot = false) {
1042
+ const holder = inShadowRoot ? 'its shadow root holds' : 'holds';
918
1043
  if (count === 0)
919
- return 'No text and no child elements';
1044
+ return `No text and no child elements${inShadowRoot ? ' in its shadow root' : ''}`;
920
1045
  const more = count > children.length ? `, … ${count - children.length} more` : '';
921
- return `No text; holds ${pluralize(count, 'element')}: ${children.join(', ')}${more} (see its HTML with --raw)`;
1046
+ const hint = inShadowRoot ? 'see it with bdg dom inspect' : 'see its HTML with --raw';
1047
+ return `No text; ${holder} ${pluralize(count, 'element')}: ${children.join(', ')}${more} (${hint})`;
922
1048
  }
923
1049
  /**
924
1050
  * Note on a screenshot scaled down to keep its image token cost bounded.
@@ -969,6 +1095,27 @@ export function queryNextSteps(index) {
969
1095
  export function evalFrameLine(url) {
970
1096
  return `Frame: ${frameUrlLabel(url)}`;
971
1097
  }
1098
+ /**
1099
+ * Warning on a `dom eval` result the browser copied because the page
1100
+ * replaced built-ins bdg's own copy uses.
1101
+ *
1102
+ * @param replaced - Their dotted names (all are named)
1103
+ * @returns e.g. `the page replaced Object.keys, so the browser copied the result: undefined, NaN, functions, DOM nodes, dates, maps and sets inside it show as null or {}`
1104
+ */
1105
+ export function evalCopiedByBrowserWarning(replaced) {
1106
+ return `the page replaced ${replaced.join(', ')}, so the browser copied the result: undefined, NaN, functions, DOM nodes, dates, maps and sets inside it show as null or {}`;
1107
+ }
1108
+ /**
1109
+ * Warning on a `dom eval` result shown as its preview: the page replaced
1110
+ * built-ins bdg's copy uses and the browser could not copy it either (a
1111
+ * cycle or a BigInt inside).
1112
+ *
1113
+ * @param replaced - Their dotted names (all are named)
1114
+ * @returns Warning that the value is a shortened preview
1115
+ */
1116
+ export function evalPreviewWarning(replaced) {
1117
+ return `the page replaced ${replaced.join(', ')}, and the result could not be copied (it holds a cycle or a BigInt), so it is shown as a shortened preview string; return a JSON-safe value (e.g. pick the fields you need)`;
1118
+ }
972
1119
  /**
973
1120
  * Generate warning message.
974
1121
  *
@@ -1124,4 +1271,12 @@ Examples:
1124
1271
  bdg css search --limit 50 -- --brand)
1125
1272
  bdg css search "oklch(" Rules that use oklch colors
1126
1273
  bdg css search ".btn-primary" Rules of a class, in every stylesheet`;
1274
+ /**
1275
+ * `details` field of `bdg --help --json`: where the full help is.
1276
+ *
1277
+ * @returns Note
1278
+ */
1279
+ export function helpJsonDetailsNote() {
1280
+ return 'Option behaviors, defaults, choices and examples: bdg <command> --help --json (e.g. bdg dom query --help --json). Everything at once: bdg --help --json --full';
1281
+ }
1127
1282
  //# sourceMappingURL=commands.js.map
@@ -25,4 +25,28 @@ export declare function stoppedFollowingConsoleMessage(): string;
25
25
  * @returns e.g. `[n] are positions in the session's message list; not listed in between: 1 message from another page load (-H lists all)`
26
26
  */
27
27
  export declare function consoleIndexGapNote(skipped: ConsoleSkipped): string;
28
+ /**
29
+ * Note that the session dropped its oldest console messages at the limit.
30
+ *
31
+ * @param dropped - Messages dropped
32
+ * @returns e.g. `⚠ 2000 older console messages were dropped: bdg keeps the newest 10000`
33
+ */
34
+ export declare function consoleDroppedNote(dropped: number): string;
35
+ /**
36
+ * Error for `bdg details console <n>` with the index of a dropped message.
37
+ *
38
+ * @param index - Index asked for
39
+ * @param dropped - Messages dropped (the first kept has this index)
40
+ * @returns Message
41
+ */
42
+ export declare function consoleMessageDroppedError(index: number, dropped: number): string;
43
+ /**
44
+ * Note under the console summary when it lists only the newest distinct
45
+ * errors or warnings.
46
+ *
47
+ * @param more - Distinct messages not listed
48
+ * @param level - `error` or `warning`
49
+ * @returns e.g. `(+120 earlier distinct errors; bdg console --level error --last 0 lists every one)`
50
+ */
51
+ export declare function consoleMoreGroupsNote(more: number, level: 'error' | 'warning'): string;
28
52
  //# sourceMappingURL=consoleMessages.d.ts.map
@@ -3,7 +3,9 @@
3
3
  *
4
4
  * User-facing messages for the console command output and formatting.
5
5
  */
6
+ import { MAX_CONSOLE_MESSAGES } from '../../constants.js';
6
7
  import { pluralize } from '../formatting.js';
8
+ import { sessionCommand } from './sessionCommand.js';
7
9
  /**
8
10
  * Generate message for following console output.
9
11
  *
@@ -36,4 +38,34 @@ export function consoleIndexGapNote(skipped) {
36
38
  ].filter(Boolean);
37
39
  return `[n] are positions in the session's message list; not listed in between: ${reasons.join(', ')}`;
38
40
  }
41
+ /**
42
+ * Note that the session dropped its oldest console messages at the limit.
43
+ *
44
+ * @param dropped - Messages dropped
45
+ * @returns e.g. `⚠ 2000 older console messages were dropped: bdg keeps the newest 10000`
46
+ */
47
+ export function consoleDroppedNote(dropped) {
48
+ return `⚠ ${pluralize(dropped, 'older console message')} ${dropped === 1 ? 'was' : 'were'} dropped: bdg keeps the newest ${MAX_CONSOLE_MESSAGES}`;
49
+ }
50
+ /**
51
+ * Error for `bdg details console <n>` with the index of a dropped message.
52
+ *
53
+ * @param index - Index asked for
54
+ * @param dropped - Messages dropped (the first kept has this index)
55
+ * @returns Message
56
+ */
57
+ export function consoleMessageDroppedError(index, dropped) {
58
+ return `Console message ${index} was dropped: bdg keeps the newest ${MAX_CONSOLE_MESSAGES} messages (the oldest kept is ${dropped})`;
59
+ }
60
+ /**
61
+ * Note under the console summary when it lists only the newest distinct
62
+ * errors or warnings.
63
+ *
64
+ * @param more - Distinct messages not listed
65
+ * @param level - `error` or `warning`
66
+ * @returns e.g. `(+120 earlier distinct errors; bdg console --level error --last 0 lists every one)`
67
+ */
68
+ export function consoleMoreGroupsNote(more, level) {
69
+ return `(+${more} earlier distinct ${more === 1 ? level : `${level}s`}; ${sessionCommand(`bdg console --level ${level} --last 0`)} lists every one)`;
70
+ }
39
71
  //# sourceMappingURL=consoleMessages.js.map
@@ -55,6 +55,12 @@ export declare function stoppedFollowingPreviewMessage(): string;
55
55
  * @param retryLabel - Human-readable retry interval (e.g. "1s", "500ms")
56
56
  */
57
57
  export declare function connectionLostRetryMessage(timestamp: string, retryLabel: string): string;
58
+ /**
59
+ * Follow mode stops because the session it followed ended.
60
+ *
61
+ * @returns Message
62
+ */
63
+ export declare function followedSessionEndedMessage(): string;
58
64
  /**
59
65
  * Generate follow-mode stop hint.
60
66
  *
@@ -19,7 +19,7 @@ export const PREVIEW_HEADERS = {
19
19
  * @returns Single-line tip for basic peek usage
20
20
  */
21
21
  export function compactTipsMessage() {
22
- return `Tip: ${sessionCommand('bdg peek --last 50')} | ${sessionCommand('bdg peek --verbose')}`;
22
+ return `Tip: ${sessionCommand('bdg peek --last 50')} or ${sessionCommand('bdg peek --verbose')}`;
23
23
  }
24
24
  /**
25
25
  * Generate verbose mode commands section.
@@ -71,6 +71,14 @@ export function stoppedFollowingPreviewMessage() {
71
71
  export function connectionLostRetryMessage(timestamp, retryLabel) {
72
72
  return `\n[${timestamp}] ⚠️ Connection lost, retrying every ${retryLabel}...`;
73
73
  }
74
+ /**
75
+ * Follow mode stops because the session it followed ended.
76
+ *
77
+ * @returns Message
78
+ */
79
+ export function followedSessionEndedMessage() {
80
+ return 'The session ended; stopped following';
81
+ }
74
82
  /**
75
83
  * Generate follow-mode stop hint.
76
84
  *
@@ -78,7 +78,18 @@ export declare function lastSessionEndText(end: {
78
78
  endedAt: number;
79
79
  }): string;
80
80
  /**
81
- * Note after a failed start whose daemon had not exited when bdg stopped waiting.
81
+ * A session that ended without `bdg stop`, for `bdg sessions`.
82
+ *
83
+ * @param label - Session name as listed
84
+ * @param end - How and when it ended
85
+ * @returns One line
86
+ */
87
+ export declare function endedSessionText(label: string, end: {
88
+ reason: string;
89
+ endedAt: number;
90
+ }): string;
91
+ /**
92
+ * Note after a failed start or a stop whose daemon had not exited when bdg stopped waiting.
82
93
  *
83
94
  * @param pid - Daemon PID, when known
84
95
  * @param waitedMs - How long bdg waited
@@ -86,7 +97,7 @@ export declare function lastSessionEndText(end: {
86
97
  */
87
98
  export declare function daemonStillExitingHint(pid: number | undefined, waitedMs: number): string;
88
99
  /**
89
- * What to do about a daemon still shutting down after a failed start.
100
+ * What to do about a daemon still shutting down after a failed start or a stop.
90
101
  *
91
102
  * @returns Suggestion
92
103
  */
@@ -89,16 +89,35 @@ export function stopFailedError(reason) {
89
89
  * @returns One line
90
90
  */
91
91
  export function lastSessionEndText(end) {
92
+ return `The last session ended ${sessionEndText(end)}`;
93
+ }
94
+ /**
95
+ * A session that ended without `bdg stop`, for `bdg sessions`.
96
+ *
97
+ * @param label - Session name as listed
98
+ * @param end - How and when it ended
99
+ * @returns One line
100
+ */
101
+ export function endedSessionText(label, end) {
102
+ return `${label} ended ${sessionEndText(end)}`;
103
+ }
104
+ /**
105
+ * When and why a session ended without `bdg stop`.
106
+ *
107
+ * @param end - How and when it ended
108
+ * @returns `at <time>: <why>`
109
+ */
110
+ function sessionEndText(end) {
92
111
  const why = {
93
112
  crash: 'Chrome crashed or was closed',
94
113
  closed: 'its page was closed',
95
114
  timeout: 'the --timeout was reached',
96
115
  };
97
116
  const at = new Date(end.endedAt).toLocaleTimeString();
98
- return `The last session ended at ${at}: ${why[end.reason] ?? end.reason}`;
117
+ return `at ${at}: ${why[end.reason] ?? end.reason}`;
99
118
  }
100
119
  /**
101
- * Note after a failed start whose daemon had not exited when bdg stopped waiting.
120
+ * Note after a failed start or a stop whose daemon had not exited when bdg stopped waiting.
102
121
  *
103
122
  * @param pid - Daemon PID, when known
104
123
  * @param waitedMs - How long bdg waited
@@ -109,7 +128,7 @@ export function daemonStillExitingHint(pid, waitedMs) {
109
128
  return `${daemon} was still shutting down after ${waitedMs / 1000}s`;
110
129
  }
111
130
  /**
112
- * What to do about a daemon still shutting down after a failed start.
131
+ * What to do about a daemon still shutting down after a failed start or a stop.
113
132
  *
114
133
  * @returns Suggestion
115
134
  */
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Checks for a directory bdg is about to create and write (the session
3
+ * directory, a Chrome profile) before it touches it: `fs.mkdirSync` with
4
+ * `recursive` spins forever on Linux pseudo-filesystems (`/proc/x` kept a
5
+ * start at full CPU until SIGKILL), and a profile Chrome cannot use wedges
6
+ * the session.
7
+ */
8
+ /** Why a directory cannot be used, and whether it is a permission problem */
9
+ export interface DirectoryProblem {
10
+ /** e.g. `/proc is a pseudo-filesystem`, `/tmp/x is a file`, `EACCES` */
11
+ reason: string;
12
+ /** Permission denied (or a read-only file system) rather than a wrong path */
13
+ denied: boolean;
14
+ }
15
+ /**
16
+ * Why `dir` cannot be created or written: it (or the path to it) is on a
17
+ * pseudo-filesystem, a part of it is a file, or its nearest existing
18
+ * directory is not writable.
19
+ *
20
+ * @param dir - Directory path (resolved against the working directory)
21
+ * @returns The problem, or null when the directory can be used
22
+ */
23
+ export declare function directoryProblem(dir: string): DirectoryProblem | null;
24
+ /**
25
+ * Create a directory (and its parents) after {@link directoryProblem} found
26
+ * nothing wrong, so a pseudo-filesystem fails at once instead of spinning.
27
+ *
28
+ * @param dir - Directory to create
29
+ * @param mode - Permissions of the directories created
30
+ * @throws Error with `code` `EPSEUDOFS` (pseudo-filesystem), `ENOTDIR` (a
31
+ * file on the path), `EACCES` (not writable), or the `mkdir` error
32
+ */
33
+ export declare function makeDirectory(dir: string, mode?: number): void;
34
+ //# sourceMappingURL=directories.d.ts.map
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Checks for a directory bdg is about to create and write (the session
3
+ * directory, a Chrome profile) before it touches it: `fs.mkdirSync` with
4
+ * `recursive` spins forever on Linux pseudo-filesystems (`/proc/x` kept a
5
+ * start at full CPU until SIGKILL), and a profile Chrome cannot use wedges
6
+ * the session.
7
+ */
8
+ import * as fs from 'fs';
9
+ import * as path from 'path';
10
+ /** Pseudo-filesystems no directory can be created on */
11
+ const PSEUDO_FILESYSTEM = /^\/(proc|sys)(\/|$)/;
12
+ /**
13
+ * Nearest path at or above `dir` that exists.
14
+ *
15
+ * @param dir - Absolute path
16
+ * @returns The path itself, or its nearest existing ancestor
17
+ */
18
+ function nearestExisting(dir) {
19
+ let current = dir;
20
+ while (!fs.existsSync(current)) {
21
+ const parent = path.dirname(current);
22
+ if (parent === current)
23
+ return current;
24
+ current = parent;
25
+ }
26
+ return current;
27
+ }
28
+ /**
29
+ * Why `dir` cannot be created or written: it (or the path to it) is on a
30
+ * pseudo-filesystem, a part of it is a file, or its nearest existing
31
+ * directory is not writable.
32
+ *
33
+ * @param dir - Directory path (resolved against the working directory)
34
+ * @returns The problem, or null when the directory can be used
35
+ */
36
+ export function directoryProblem(dir) {
37
+ const resolved = path.resolve(dir);
38
+ const existing = nearestExisting(resolved);
39
+ let real;
40
+ try {
41
+ real = fs.realpathSync(existing);
42
+ }
43
+ catch (error) {
44
+ return { reason: error.code ?? String(error), denied: false };
45
+ }
46
+ if (PSEUDO_FILESYSTEM.test(resolved) || PSEUDO_FILESYSTEM.test(real)) {
47
+ const root = (PSEUDO_FILESYSTEM.exec(resolved) ?? PSEUDO_FILESYSTEM.exec(real))?.[0] ?? '';
48
+ return { reason: `${root.replace(/\/$/, '')} is a pseudo-filesystem`, denied: false };
49
+ }
50
+ if (!fs.statSync(real).isDirectory()) {
51
+ return {
52
+ reason: existing === resolved ? 'it is a file' : `${existing} is a file`,
53
+ denied: false,
54
+ };
55
+ }
56
+ try {
57
+ fs.accessSync(real, fs.constants.W_OK);
58
+ }
59
+ catch (error) {
60
+ const code = error.code ?? String(error);
61
+ return { reason: `${existing} is not writable (${code})`, denied: true };
62
+ }
63
+ return null;
64
+ }
65
+ /**
66
+ * Create a directory (and its parents) after {@link directoryProblem} found
67
+ * nothing wrong, so a pseudo-filesystem fails at once instead of spinning.
68
+ *
69
+ * @param dir - Directory to create
70
+ * @param mode - Permissions of the directories created
71
+ * @throws Error with `code` `EPSEUDOFS` (pseudo-filesystem), `ENOTDIR` (a
72
+ * file on the path), `EACCES` (not writable), or the `mkdir` error
73
+ */
74
+ export function makeDirectory(dir, mode) {
75
+ if (fs.existsSync(dir))
76
+ return;
77
+ const problem = directoryProblem(dir);
78
+ if (problem) {
79
+ const code = problem.denied
80
+ ? 'EACCES'
81
+ : /pseudo-filesystem/.test(problem.reason)
82
+ ? 'EPSEUDOFS'
83
+ : 'ENOTDIR';
84
+ throw Object.assign(new Error(problem.reason), { code });
85
+ }
86
+ fs.mkdirSync(dir, { recursive: true, ...(mode !== undefined && { mode }) });
87
+ }
88
+ //# sourceMappingURL=directories.js.map
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Whether Chrome can show a window, which decides the default of
3
+ * `--headless`.
4
+ */
5
+ /**
6
+ * Whether a display is available for Chrome's window: on Linux an X11 or
7
+ * Wayland display (`DISPLAY`, `WAYLAND_DISPLAY`; WSLg sets them too), on
8
+ * macOS the desktop unless the shell came in over SSH (`SSH_CONNECTION`,
9
+ * `SSH_TTY`) or runs in CI (`CI`, unless `false` or `0`). Servers, containers and CI stay headless.
10
+ *
11
+ * @param env - Environment variables
12
+ * @param platform - Operating system (`process.platform`)
13
+ * @returns True when Chrome should show a window by default
14
+ */
15
+ export declare function hasDisplay(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): boolean;
16
+ //# sourceMappingURL=display.d.ts.map
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Whether Chrome can show a window, which decides the default of
3
+ * `--headless`.
4
+ */
5
+ /**
6
+ * Whether an environment variable is set and not empty.
7
+ *
8
+ * @param env - Environment variables
9
+ * @param name - Variable name
10
+ * @returns True when set to a non-empty value
11
+ */
12
+ function isSet(env, name) {
13
+ const value = env[name];
14
+ return value !== undefined && value !== '';
15
+ }
16
+ /**
17
+ * Whether the environment says it is CI: `CI` set to anything but `false`
18
+ * or `0` (`CI=false` is how a CI flag is turned off).
19
+ *
20
+ * @param env - Environment variables
21
+ * @returns True in CI
22
+ */
23
+ function isCi(env) {
24
+ return isSet(env, 'CI') && !['false', '0'].includes(env['CI']?.toLowerCase() ?? '');
25
+ }
26
+ /**
27
+ * Whether a display is available for Chrome's window: on Linux an X11 or
28
+ * Wayland display (`DISPLAY`, `WAYLAND_DISPLAY`; WSLg sets them too), on
29
+ * macOS the desktop unless the shell came in over SSH (`SSH_CONNECTION`,
30
+ * `SSH_TTY`) or runs in CI (`CI`, unless `false` or `0`). Servers, containers and CI stay headless.
31
+ *
32
+ * @param env - Environment variables
33
+ * @param platform - Operating system (`process.platform`)
34
+ * @returns True when Chrome should show a window by default
35
+ */
36
+ export function hasDisplay(env = process.env, platform = process.platform) {
37
+ if (platform === 'darwin') {
38
+ return !isSet(env, 'SSH_CONNECTION') && !isSet(env, 'SSH_TTY') && !isCi(env);
39
+ }
40
+ return isSet(env, 'DISPLAY') || isSet(env, 'WAYLAND_DISPLAY');
41
+ }
42
+ //# sourceMappingURL=display.js.map
@@ -53,6 +53,7 @@ export declare const EXIT_CODES: {
53
53
  readonly UNHANDLED_EXCEPTION: 104;
54
54
  readonly SIGNAL_HANDLER_ERROR: 105;
55
55
  readonly SESSION_START_FAILURE: 106;
56
+ readonly PAGE_CRASHED: 107;
56
57
  readonly SOFTWARE_ERROR: 110;
57
58
  readonly INTERRUPTED: 130;
58
59
  readonly TERMINATED: 143;
@@ -53,6 +53,7 @@ export const EXIT_CODES = {
53
53
  UNHANDLED_EXCEPTION: 104,
54
54
  SIGNAL_HANDLER_ERROR: 105,
55
55
  SESSION_START_FAILURE: 106,
56
+ PAGE_CRASHED: 107,
56
57
  SOFTWARE_ERROR: 110,
57
58
  INTERRUPTED: 130,
58
59
  TERMINATED: 143,
@@ -165,6 +166,11 @@ export const EXIT_CODE_REGISTRY = [
165
166
  name: 'SESSION_START_FAILURE',
166
167
  description: 'Session failed to start (Chrome launch or CDP connection)',
167
168
  },
169
+ {
170
+ code: EXIT_CODES.PAGE_CRASHED,
171
+ name: 'PAGE_CRASHED',
172
+ description: 'The page crashed (its renderer is gone); bdg page reload brings it back',
173
+ },
168
174
  {
169
175
  code: EXIT_CODES.SOFTWARE_ERROR,
170
176
  name: 'SOFTWARE_ERROR',
@@ -52,4 +52,16 @@ export declare function killChromeProcess(pid: number, signal?: NodeJS.Signals):
52
52
  * @returns Command line, or null if unavailable (process gone, or unsupported platform)
53
53
  */
54
54
  export declare function getProcessCommand(pid: number): string | null;
55
+ /** A running process and its command line */
56
+ export interface ProcessEntry {
57
+ pid: number;
58
+ command: string;
59
+ }
60
+ /**
61
+ * Every running process with its command line: from `/proc` on Linux
62
+ * (minimal containers' BusyBox `ps` lacks `-o`), else from `ps` (macOS).
63
+ *
64
+ * @returns Processes, empty when they cannot be listed (Windows)
65
+ */
66
+ export declare function listProcesses(): ProcessEntry[];
55
67
  //# sourceMappingURL=process.d.ts.map