browser-debugger-cli 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (304) hide show
  1. package/README.md +7 -1
  2. package/dist/cdp/schema.d.ts +4 -1
  3. package/dist/cdp/schema.js +48 -7
  4. package/dist/commands/cdp.js +3 -2
  5. package/dist/commands/cleanup.d.ts +11 -0
  6. package/dist/commands/cleanup.js +161 -57
  7. package/dist/commands/console.d.ts +20 -1
  8. package/dist/commands/console.js +57 -17
  9. package/dist/commands/details.js +3 -2
  10. package/dist/commands/dom/DomElementResolver.d.ts +10 -3
  11. package/dist/commands/dom/DomElementResolver.js +35 -17
  12. package/dist/commands/dom/a11y.d.ts +10 -0
  13. package/dist/commands/dom/a11y.js +29 -6
  14. package/dist/commands/dom/eval.d.ts +3 -1
  15. package/dist/commands/dom/eval.js +29 -4
  16. package/dist/commands/dom/form.js +16 -62
  17. package/dist/commands/dom/formInteraction.js +189 -119
  18. package/dist/commands/dom/formSummary.d.ts +49 -0
  19. package/dist/commands/dom/formSummary.js +180 -0
  20. package/dist/commands/dom/frames.d.ts +2 -1
  21. package/dist/commands/dom/frames.js +17 -2
  22. package/dist/commands/dom/get.d.ts +6 -5
  23. package/dist/commands/dom/get.js +92 -82
  24. package/dist/commands/dom/helpers/index.d.ts +1 -1
  25. package/dist/commands/dom/helpers/index.js +1 -1
  26. package/dist/commands/dom/helpers/keyAttributes.d.ts +20 -0
  27. package/dist/commands/dom/helpers/keyAttributes.js +54 -0
  28. package/dist/commands/dom/helpers/query.d.ts +44 -17
  29. package/dist/commands/dom/helpers/query.js +300 -106
  30. package/dist/commands/dom/helpers/runElementCommand.d.ts +10 -2
  31. package/dist/commands/dom/helpers/runElementCommand.js +98 -30
  32. package/dist/commands/dom/helpers/screenshot.d.ts +4 -1
  33. package/dist/commands/dom/helpers/screenshot.js +239 -51
  34. package/dist/commands/dom/index.d.ts +4 -1
  35. package/dist/commands/dom/index.js +22 -7
  36. package/dist/commands/dom/inspect.d.ts +15 -0
  37. package/dist/commands/dom/inspect.js +82 -0
  38. package/dist/commands/dom/layout.d.ts +14 -0
  39. package/dist/commands/dom/layout.js +54 -0
  40. package/dist/commands/dom/listeners.d.ts +5 -1
  41. package/dist/commands/dom/listeners.js +15 -5
  42. package/dist/commands/dom/query.js +2 -3
  43. package/dist/commands/dom/screenshot.d.ts +12 -2
  44. package/dist/commands/dom/screenshot.js +27 -3
  45. package/dist/commands/dom/semanticUtils.d.ts +16 -10
  46. package/dist/commands/dom/semanticUtils.js +53 -16
  47. package/dist/commands/dom/wait.d.ts +13 -0
  48. package/dist/commands/dom/wait.js +83 -0
  49. package/dist/commands/helpJson.js +2 -2
  50. package/dist/commands/network/list.js +17 -13
  51. package/dist/commands/optionBehaviors.js +154 -21
  52. package/dist/commands/page.d.ts +3 -2
  53. package/dist/commands/page.js +100 -5
  54. package/dist/commands/peek.js +4 -11
  55. package/dist/commands/sessions.d.ts +8 -0
  56. package/dist/commands/sessions.js +19 -0
  57. package/dist/commands/shared/CommandRunner.js +4 -4
  58. package/dist/commands/shared/commonOptions.d.ts +4 -0
  59. package/dist/commands/shared/commonOptions.js +9 -0
  60. package/dist/commands/shared/dataFetcher.js +2 -2
  61. package/dist/commands/shared/followMode.d.ts +21 -1
  62. package/dist/commands/shared/followMode.js +29 -2
  63. package/dist/commands/shared/handleValidationError.d.ts +2 -2
  64. package/dist/commands/shared/handleValidationError.js +12 -3
  65. package/dist/commands/shared/optionTypes.d.ts +61 -5
  66. package/dist/commands/shared/startHelpers.d.ts +66 -0
  67. package/dist/commands/shared/startHelpers.js +103 -13
  68. package/dist/commands/shared/validation.d.ts +14 -2
  69. package/dist/commands/shared/validation.js +20 -3
  70. package/dist/commands/start.d.ts +63 -0
  71. package/dist/commands/start.js +115 -15
  72. package/dist/commands/status.js +29 -7
  73. package/dist/commands/stop.js +7 -6
  74. package/dist/commands/tail.js +4 -11
  75. package/dist/commands/types.d.ts +2 -0
  76. package/dist/commands.js +2 -0
  77. package/dist/connection/chromeIdentity.d.ts +65 -0
  78. package/dist/connection/chromeIdentity.js +143 -0
  79. package/dist/connection/launcher/profilePreferences.d.ts +47 -0
  80. package/dist/connection/launcher/profilePreferences.js +151 -0
  81. package/dist/connection/launcher.d.ts +21 -2
  82. package/dist/connection/launcher.js +42 -16
  83. package/dist/connection/portReservation.d.ts +14 -4
  84. package/dist/connection/portReservation.js +21 -6
  85. package/dist/connection/startupExit.d.ts +8 -0
  86. package/dist/connection/startupExit.js +15 -6
  87. package/dist/constants.d.ts +6 -2
  88. package/dist/constants.js +9 -2
  89. package/dist/daemon/SessionController.js +23 -7
  90. package/dist/daemon/errors.d.ts +1 -1
  91. package/dist/daemon/errors.js +1 -1
  92. package/dist/daemon/launcher.d.ts +10 -2
  93. package/dist/daemon/launcher.js +8 -7
  94. package/dist/daemon/server/SocketServer.js +1 -2
  95. package/dist/daemon/session/Session.d.ts +20 -0
  96. package/dist/daemon/session/Session.js +80 -9
  97. package/dist/daemon/session/chromeConnection.d.ts +9 -0
  98. package/dist/daemon/session/chromeConnection.js +45 -8
  99. package/dist/daemon/session/commandRegistry.d.ts +14 -1
  100. package/dist/daemon/session/commandRegistry.js +113 -67
  101. package/dist/daemon/session/interactions.d.ts +48 -9
  102. package/dist/daemon/session/interactions.js +46 -9
  103. package/dist/daemon/session/triggeredRequests.d.ts +67 -0
  104. package/dist/daemon/session/triggeredRequests.js +157 -0
  105. package/dist/daemon/session/types.d.ts +5 -1
  106. package/dist/daemon.js +10630 -3601
  107. package/dist/errors/messages.d.ts +456 -24
  108. package/dist/errors/messages.js +862 -67
  109. package/dist/index.js +6915 -3401
  110. package/dist/ipc/client.d.ts +21 -1
  111. package/dist/ipc/client.js +35 -3
  112. package/dist/ipc/protocol/commands.d.ts +145 -5
  113. package/dist/ipc/protocol/commands.js +4 -0
  114. package/dist/ipc/protocol/domTypes.d.ts +291 -7
  115. package/dist/ipc/protocol/inspectTypes.d.ts +388 -0
  116. package/dist/ipc/protocol/inspectTypes.js +10 -0
  117. package/dist/ipc/session/lifecycle.d.ts +8 -1
  118. package/dist/ipc/session/queries.d.ts +5 -1
  119. package/dist/ipc/session/types.d.ts +5 -0
  120. package/dist/ipc/transport/index.d.ts +2 -1
  121. package/dist/ipc/transport/index.js +2 -2
  122. package/dist/runtime/dom/actionEffects.d.ts +185 -0
  123. package/dist/runtime/dom/actionEffects.js +402 -0
  124. package/dist/runtime/dom/actionEffectsScripts.d.ts +90 -0
  125. package/dist/runtime/dom/actionEffectsScripts.js +426 -0
  126. package/dist/runtime/dom/elementGeometry.d.ts +170 -0
  127. package/dist/runtime/dom/elementGeometry.js +553 -0
  128. package/dist/runtime/dom/elementInfo.d.ts +103 -0
  129. package/dist/runtime/dom/elementInfo.js +256 -0
  130. package/dist/runtime/dom/evalHelpers.d.ts +51 -6
  131. package/dist/runtime/dom/evalHelpers.js +136 -26
  132. package/dist/runtime/dom/eventListeners.d.ts +2 -1
  133. package/dist/runtime/dom/eventListeners.js +184 -47
  134. package/dist/runtime/dom/formDiscovery.d.ts +1 -1
  135. package/dist/runtime/dom/formDiscovery.js +116 -16
  136. package/dist/runtime/dom/formFillHelpers/fill.d.ts +9 -0
  137. package/dist/runtime/dom/formFillHelpers/fill.js +178 -14
  138. package/dist/runtime/dom/formFillHelpers/index.d.ts +2 -2
  139. package/dist/runtime/dom/formFillHelpers/index.js +2 -2
  140. package/dist/runtime/dom/formFillHelpers/pressKey.js +14 -3
  141. package/dist/runtime/dom/formFillHelpers/scroll.d.ts +3 -0
  142. package/dist/runtime/dom/formFillHelpers/scroll.js +60 -18
  143. package/dist/runtime/dom/formFillHelpers/shared.d.ts +17 -0
  144. package/dist/runtime/dom/formFillHelpers/shared.js +25 -1
  145. package/dist/runtime/dom/formFillHelpers/stability.d.ts +20 -6
  146. package/dist/runtime/dom/formFillHelpers/stability.js +50 -19
  147. package/dist/runtime/dom/formSubmitHelpers.d.ts +3 -0
  148. package/dist/runtime/dom/formSubmitHelpers.js +89 -15
  149. package/dist/runtime/dom/frameLayout.d.ts +60 -0
  150. package/dist/runtime/dom/frameLayout.js +140 -0
  151. package/dist/runtime/dom/frameOrigin.d.ts +50 -0
  152. package/dist/runtime/dom/frameOrigin.js +62 -0
  153. package/dist/runtime/dom/frameScopedConnection.d.ts +92 -0
  154. package/dist/runtime/dom/frameScopedConnection.js +252 -0
  155. package/dist/runtime/dom/frameSelection.d.ts +12 -1
  156. package/dist/runtime/dom/frameSelection.js +22 -3
  157. package/dist/runtime/dom/frames.d.ts +61 -5
  158. package/dist/runtime/dom/frames.js +329 -75
  159. package/dist/runtime/dom/inspect.d.ts +28 -0
  160. package/dist/runtime/dom/inspect.js +557 -0
  161. package/dist/runtime/dom/inspectAllStyles.d.ts +62 -0
  162. package/dist/runtime/dom/inspectAllStyles.js +385 -0
  163. package/dist/runtime/dom/inspectCascade.d.ts +94 -0
  164. package/dist/runtime/dom/inspectCascade.js +371 -0
  165. package/dist/runtime/dom/inspectCascadeModel.d.ts +39 -0
  166. package/dist/runtime/dom/inspectCascadeModel.js +232 -0
  167. package/dist/runtime/dom/inspectHints.d.ts +62 -0
  168. package/dist/runtime/dom/inspectHints.js +305 -0
  169. package/dist/runtime/dom/inspectLayoutModel.d.ts +87 -0
  170. package/dist/runtime/dom/inspectLayoutModel.js +346 -0
  171. package/dist/runtime/dom/inspectModel.d.ts +74 -0
  172. package/dist/runtime/dom/inspectModel.js +184 -0
  173. package/dist/runtime/dom/inspectPaintModel.d.ts +157 -0
  174. package/dist/runtime/dom/inspectPaintModel.js +461 -0
  175. package/dist/runtime/dom/inspectRules.d.ts +37 -0
  176. package/dist/runtime/dom/inspectRules.js +101 -0
  177. package/dist/runtime/dom/inspectScripts.d.ts +132 -0
  178. package/dist/runtime/dom/inspectScripts.js +263 -0
  179. package/dist/runtime/dom/inspectTree.d.ts +40 -0
  180. package/dist/runtime/dom/inspectTree.js +134 -0
  181. package/dist/runtime/dom/inspectVariables.d.ts +33 -0
  182. package/dist/runtime/dom/inspectVariables.js +94 -0
  183. package/dist/runtime/dom/inspectWhyModel.d.ts +20 -0
  184. package/dist/runtime/dom/inspectWhyModel.js +134 -0
  185. package/dist/runtime/dom/layout.d.ts +71 -0
  186. package/dist/runtime/dom/layout.js +340 -0
  187. package/dist/runtime/dom/listenerPageScripts.d.ts +72 -0
  188. package/dist/runtime/dom/listenerPageScripts.js +365 -0
  189. package/dist/runtime/dom/listenerSummary.d.ts +136 -11
  190. package/dist/runtime/dom/listenerSummary.js +361 -22
  191. package/dist/runtime/dom/pageActivity.d.ts +41 -0
  192. package/dist/runtime/dom/pageActivity.js +123 -0
  193. package/dist/runtime/dom/reactEventHelpers.d.ts +63 -2
  194. package/dist/runtime/dom/reactEventHelpers.js +220 -41
  195. package/dist/runtime/dom/targetNode.d.ts +80 -27
  196. package/dist/runtime/dom/targetNode.js +249 -33
  197. package/dist/runtime/dom/wait.d.ts +25 -0
  198. package/dist/runtime/dom/wait.js +199 -0
  199. package/dist/runtime/dom/waitCondition.d.ts +71 -0
  200. package/dist/runtime/dom/waitCondition.js +75 -0
  201. package/dist/runtime/page/emulation.d.ts +71 -0
  202. package/dist/runtime/page/emulation.js +117 -0
  203. package/dist/runtime/page/loadingState.d.ts +36 -0
  204. package/dist/runtime/page/loadingState.js +86 -0
  205. package/dist/runtime/page/navigation.d.ts +46 -2
  206. package/dist/runtime/page/navigation.js +69 -33
  207. package/dist/session/QueryCacheManager.d.ts +11 -1
  208. package/dist/session/QueryCacheManager.js +25 -3
  209. package/dist/session/chromeOwners.d.ts +34 -0
  210. package/dist/session/chromeOwners.js +51 -0
  211. package/dist/session/cleanup/staleSession.d.ts +11 -1
  212. package/dist/session/cleanup/staleSession.js +17 -6
  213. package/dist/session/cleanup/userCommands.js +2 -4
  214. package/dist/session/metadata.d.ts +5 -1
  215. package/dist/session/metadata.js +2 -1
  216. package/dist/session/paths.d.ts +77 -3
  217. package/dist/session/paths.js +111 -5
  218. package/dist/session/port.d.ts +31 -7
  219. package/dist/session/port.js +50 -43
  220. package/dist/session/portClaims.d.ts +66 -0
  221. package/dist/session/portClaims.js +284 -0
  222. package/dist/session/sessionList.d.ts +58 -0
  223. package/dist/session/sessionList.js +199 -0
  224. package/dist/session/sessionName.d.ts +46 -0
  225. package/dist/session/sessionName.js +97 -0
  226. package/dist/telemetry/a11y.d.ts +18 -3
  227. package/dist/telemetry/a11y.js +170 -29
  228. package/dist/telemetry/console.d.ts +1 -0
  229. package/dist/telemetry/console.js +100 -5
  230. package/dist/telemetry/network.js +3 -1
  231. package/dist/telemetry/requestKinds.d.ts +32 -0
  232. package/dist/telemetry/requestKinds.js +61 -0
  233. package/dist/telemetry/requestState.d.ts +31 -0
  234. package/dist/telemetry/requestState.js +38 -0
  235. package/dist/types.d.ts +112 -3
  236. package/dist/ui/formatters/a11y.js +3 -0
  237. package/dist/ui/formatters/console/chronological.d.ts +8 -0
  238. package/dist/ui/formatters/console/chronological.js +17 -4
  239. package/dist/ui/formatters/console/json.js +3 -4
  240. package/dist/ui/formatters/console/shared.d.ts +12 -0
  241. package/dist/ui/formatters/console.d.ts +2 -2
  242. package/dist/ui/formatters/console.js +1 -1
  243. package/dist/ui/formatters/details.d.ts +8 -0
  244. package/dist/ui/formatters/details.js +61 -4
  245. package/dist/ui/formatters/dom.d.ts +27 -14
  246. package/dist/ui/formatters/dom.js +88 -59
  247. package/dist/ui/formatters/form.js +29 -18
  248. package/dist/ui/formatters/inspect.d.ts +39 -0
  249. package/dist/ui/formatters/inspect.js +596 -0
  250. package/dist/ui/formatters/keyAttributes.d.ts +19 -0
  251. package/dist/ui/formatters/keyAttributes.js +84 -0
  252. package/dist/ui/formatters/layout.d.ts +31 -0
  253. package/dist/ui/formatters/layout.js +53 -0
  254. package/dist/ui/formatters/listeners.d.ts +3 -2
  255. package/dist/ui/formatters/listeners.js +73 -9
  256. package/dist/ui/formatters/networkHeaders.d.ts +13 -0
  257. package/dist/ui/formatters/networkHeaders.js +36 -3
  258. package/dist/ui/formatters/networkList.d.ts +29 -1
  259. package/dist/ui/formatters/networkList.js +86 -20
  260. package/dist/ui/formatters/preview.js +2 -1
  261. package/dist/ui/formatters/requestStatus.d.ts +1 -17
  262. package/dist/ui/formatters/requestStatus.js +2 -30
  263. package/dist/ui/formatters/sessions.d.ts +12 -0
  264. package/dist/ui/formatters/sessions.js +40 -0
  265. package/dist/ui/formatters/status.d.ts +21 -2
  266. package/dist/ui/formatters/status.js +47 -10
  267. package/dist/ui/formatters/triggeredRequests.d.ts +36 -0
  268. package/dist/ui/formatters/triggeredRequests.js +65 -0
  269. package/dist/ui/formatting.d.ts +19 -0
  270. package/dist/ui/formatting.js +31 -36
  271. package/dist/ui/messages/chrome.d.ts +9 -0
  272. package/dist/ui/messages/chrome.js +17 -5
  273. package/dist/ui/messages/commands.d.ts +504 -14
  274. package/dist/ui/messages/commands.js +835 -21
  275. package/dist/ui/messages/consoleMessages.d.ts +10 -0
  276. package/dist/ui/messages/consoleMessages.js +17 -0
  277. package/dist/ui/messages/hints.js +2 -1
  278. package/dist/ui/messages/networkMessages.d.ts +14 -0
  279. package/dist/ui/messages/networkMessages.js +18 -0
  280. package/dist/ui/messages/preview.js +5 -4
  281. package/dist/ui/messages/session.d.ts +30 -21
  282. package/dist/ui/messages/session.js +48 -26
  283. package/dist/ui/messages/sessionCommand.d.ts +43 -0
  284. package/dist/ui/messages/sessionCommand.js +52 -0
  285. package/dist/utils/async.d.ts +17 -0
  286. package/dist/utils/async.js +36 -0
  287. package/dist/utils/color.d.ts +84 -0
  288. package/dist/utils/color.js +376 -0
  289. package/dist/utils/cssValues.d.ts +109 -0
  290. package/dist/utils/cssValues.js +236 -0
  291. package/dist/utils/http.d.ts +22 -1
  292. package/dist/utils/http.js +28 -9
  293. package/dist/utils/selectorFilters.d.ts +48 -8
  294. package/dist/utils/selectorFilters.js +296 -53
  295. package/dist/utils/shellDetection.d.ts +8 -2
  296. package/dist/utils/shellDetection.js +120 -33
  297. package/dist/utils/suggestions.d.ts +26 -0
  298. package/dist/utils/suggestions.js +73 -0
  299. package/dist/utils/taskMappings.js +10 -0
  300. package/dist/utils/url.d.ts +12 -2
  301. package/dist/utils/url.js +69 -7
  302. package/package.json +1 -1
  303. package/dist/ui/formatters/sessionFormatters.d.ts +0 -58
  304. package/dist/ui/formatters/sessionFormatters.js +0 -121
@@ -4,16 +4,16 @@
4
4
  import { Option } from 'commander';
5
5
  import { runCommand } from '../shared/CommandRunner.js';
6
6
  import { jsonOption } from '../shared/commonOptions.js';
7
- import { handleDaemonConnectionError, noteFollowConnected, } from '../shared/daemonErrorHandler.js';
7
+ import { noteFollowConnected } from '../shared/daemonErrorHandler.js';
8
8
  import { fetchNetworkRequests, createErrorResult } from '../shared/dataFetcher.js';
9
- import { setupFollowMode } from '../shared/followMode.js';
9
+ import { followFetchFailure, setupFollowMode, } from '../shared/followMode.js';
10
10
  import { handleValidationError } from '../shared/handleValidationError.js';
11
11
  import { positiveIntRule, resourceTypeRule } from '../shared/validation.js';
12
12
  import { applyFilters, getFilterHelpText, validateFilterString } from '../../telemetry/filterDsl.js';
13
13
  import { resolvePreset, FILTER_PRESETS } from '../../telemetry/filterPresets.js';
14
14
  import { filterByResourceType } from '../../telemetry/filters.js';
15
15
  import { buildSuccessResponse } from '../../ui/OutputBuilder.js';
16
- import { formatNetworkFollowRows, formatNetworkList, } from '../../ui/formatters/networkList.js';
16
+ import { formatNetworkFollowRows, formatNetworkList, pageStartOf, } from '../../ui/formatters/networkList.js';
17
17
  import { followingNetworkMessage, stoppedFollowingNetworkMessage, } from '../../ui/messages/networkMessages.js';
18
18
  import { EXIT_CODES } from '../../utils/exitCodes.js';
19
19
  import { validateFilterOption } from './shared.js';
@@ -110,6 +110,7 @@ function buildFormatOptions(options, result, lastLimit) {
110
110
  last: lastLimit,
111
111
  totalCount: result.totalCount,
112
112
  filteredCount: result.filteredCount,
113
+ ...(result.pageStart && { pageStart: result.pageStart }),
113
114
  };
114
115
  }
115
116
  /**
@@ -127,15 +128,7 @@ async function runFollowMode(options, resourceTypes, lastN) {
127
128
  const showNetwork = async () => {
128
129
  const result = await fetchNetworkRequests(filtersNeedHeaders(options));
129
130
  if (!result.success) {
130
- const errorResult = handleDaemonConnectionError(result.error, {
131
- json: options.json,
132
- follow: true,
133
- retryIntervalMs: FOLLOW_INTERVAL,
134
- exitCode: result.exitCode,
135
- });
136
- if (errorResult.shouldExit)
137
- process.exit(errorResult.exitCode);
138
- return;
131
+ return followFetchFailure(result, { json: options.json, retryIntervalMs: FOLLOW_INTERVAL });
139
132
  }
140
133
  noteFollowConnected();
141
134
  const finished = filterRequests(result.data, options, resourceTypes).filter((request) => request.duration !== undefined && !shown.has(request.requestId));
@@ -156,14 +149,17 @@ async function runFollowMode(options, resourceTypes, lastN) {
156
149
  }
157
150
  }
158
151
  else {
152
+ const pageStart = pageStartOf(result.data);
159
153
  const text = formatNetworkFollowRows(fresh, {
160
154
  header: !started,
161
155
  verbose: options.verbose ?? false,
156
+ ...(pageStart && { pageStart }),
162
157
  });
163
158
  if (text)
164
159
  console.log(text);
165
160
  }
166
161
  started = true;
162
+ return undefined;
167
163
  };
168
164
  await setupFollowMode(showNetwork, {
169
165
  startMessage: followingNetworkMessage,
@@ -171,6 +167,12 @@ async function runFollowMode(options, resourceTypes, lastN) {
171
167
  intervalMs: FOLLOW_INTERVAL,
172
168
  });
173
169
  }
170
+ /** What the less obvious columns of the list mean */
171
+ const COLUMNS_HELP = `Columns:
172
+ START When the request started, from the start of the current page (its document
173
+ request): +1.2s. Requests of earlier pages are negative. --json has the
174
+ absolute time (timestamp, epoch ms) and data.pageStart.
175
+ TIME How long it took (to its last byte or failure); - while pending`;
174
176
  function formatPresetHelp() {
175
177
  return Object.entries(FILTER_PRESETS)
176
178
  .map(([name, preset]) => ` ${name.padEnd(12)} ${preset.description}`)
@@ -187,7 +189,7 @@ export function registerListCommand(networkCmd) {
187
189
  .addOption(networkLastOption)
188
190
  .addOption(new Option('-f, --follow', 'Stream network requests in real-time').default(false))
189
191
  .addOption(new Option('-v, --verbose', 'Show full URLs and additional details').default(false))
190
- .addHelpText('after', `\n${getFilterHelpText()}\n\nPresets:\n${formatPresetHelp()}`)
192
+ .addHelpText('after', `\n${COLUMNS_HELP}\n\n${getFilterHelpText()}\n\nPresets:\n${formatPresetHelp()}`)
191
193
  .action(async (options) => {
192
194
  let resourceTypes;
193
195
  let lastN;
@@ -218,12 +220,14 @@ export function registerListCommand(networkCmd) {
218
220
  return createErrorResult(result.error, result.exitCode, result.suggestion);
219
221
  }
220
222
  const filtered = filterRequests(result.data, options, resourceTypes);
223
+ const pageStart = pageStartOf(result.data);
221
224
  return {
222
225
  success: true,
223
226
  data: {
224
227
  requests: lastN === 0 ? filtered : filtered.slice(-lastN),
225
228
  totalCount: result.data.length,
226
229
  filteredCount: filtered.length,
230
+ ...(pageStart && { pageStart }),
227
231
  },
228
232
  };
229
233
  }, options, (data) => formatNetworkList(data.requests, buildFormatOptions(options, data, lastN)));
@@ -7,12 +7,27 @@
7
7
  * @see docs/principles/SELF_DOCUMENTING_SYSTEMS.md
8
8
  */
9
9
  import { MAX_EDGE_PX, PIXELS_PER_TOKEN, TALL_PAGE_THRESHOLD, } from './dom/screenshotResize.js';
10
+ /** What DOM actions report about the network requests they triggered */
11
+ const TRIGGERED_REQUESTS_BEHAVIOR = 'Requests (and WebSocket connections) that start after the action begins are returned as triggeredRequests (method, url, status, durationMs; pending when still running at return, loading when the response arrived but its body is still streaming; with resourceType; human output lists documents, XHR/fetch and WebSockets first (up to 10) and counts static assets on one line; absent when network telemetry is off). Attribution is by time: requests a page timer or poller starts meanwhile are listed too, whether or not the action caused them';
12
+ /** What every DOM action reports about the page besides its requests */
13
+ const ACTION_EFFECTS_BEHAVIOR = 'The result also says what changed on the page: a navigation (Page: navigated to <url> (status), or URL changed to <url> (same document); JSON navigation { url, sameDocument, status }), and messages that appeared or changed in alert/status/aria-live elements or flash/error/toast-like classes (New text: "…" (element); JSON messages [{ text, element }], at most 3; after a navigation every message on the new page counts; texts of only digits and time units, such as clocks and counters, are left out, but other text that changes on its own, such as a rotating banner, can show up). Both are absent when nothing changed. Cost: one page script sent before the action without waiting for it and one read after it, a few ms; when the page does not answer (a pending navigation) bdg waits at most 200 ms for the snapshot and 250 ms per read, and the navigation is still reported from CDP events';
14
+ /** What click and pressKey report when the page was still changing as they returned */
15
+ const STILL_CHANGING_BEHAVIOR = 'When the page was still changing as the action returned, the status line says (page still changing), a note below it says what was pending and suggests bdg dom wait <selector>, and JSON has settled: false with pending { requests (document, fetch/XHR and script requests still running), navigation (a new page still loading), loading (a loading indicator that appeared, e.g. "div#loading"), domChanging (DOM changes kept coming in bursts over a second look 250 ms later; a single render, ticking text and style animations do not count), busy (the page did not answer within 250 ms: a long script) }; absent when the page looked settled (exit code stays 0). A result a timer renders later, with no DOM change, request or loading indicator before it, is not detected. Cost: nothing extra, except 250 ms plus one read when the DOM looked busy. Not checked with --no-wait';
16
+ /** What hover and pressKey report about elements they showed */
17
+ const SHOWN_BEHAVIOR = 'Elements the action showed are listed (Shown: <element> "<text>"; JSON shown [{ text, element }], at most 3, outermost first): elements with visible text added inside the target\'s form, search box, dialog or combobox (else its grandparent, or its parent when that is the body), and popups and messages added anywhere (tooltip, menu, listbox, dialog, alert, status roles, popover, aria-live, message-like classes); widgets elsewhere on the page and re-rendered elements whose text was there before do not count';
18
+ /** What `--no-wait` does to a DOM action's triggered requests */
19
+ const NO_WAIT_TRIGGERED_REQUESTS = 'Returns immediately without waiting for network; triggeredRequests lists only requests bdg saw start before returning (often none yet; check bdg network list later)';
10
20
  /**
11
21
  * Behavioral metadata registry.
12
22
  *
13
23
  * Keyed by "command:flag" to support same flag names across different commands.
14
24
  */
15
25
  const OPTION_BEHAVIORS = {
26
+ 'screenshot:--selector': {
27
+ default: 'Captures the page (full page unless --no-full-page)',
28
+ whenEnabled: 'Captures one element; the selector (or a query index) can also be given as the second argument: bdg dom screenshot out.png "#sel". Both given and naming different elements exits 81',
29
+ automaticBehavior: 'The capture covers the border box plus content overflowing it (uncleared floats, positioned children; not what an overflow: hidden ancestor cuts off, nor fixed descendants); JSON element.bounds is the border box and element.captured the larger area when it grew, which human output notes',
30
+ },
16
31
  'screenshot:--no-resize': {
17
32
  default: `Images auto-resized to max ${MAX_EDGE_PX}px longest edge for Claude Vision optimization (~1,600 tokens)`,
18
33
  whenDisabled: `Full resolution capture preserved (may use 10,000+ tokens for large pages)`,
@@ -42,18 +57,30 @@ const OPTION_BEHAVIORS = {
42
57
  whenEnabled: 'Returns full HTML with all attributes and classes',
43
58
  tokenImpact: 'Semantic output uses 70-99% fewer tokens than raw HTML. Use --raw only when you need exact HTML structure.',
44
59
  },
60
+ 'get:--full': {
61
+ default: 'Semantic output shows the element text up to 500 characters (whitespace collapsed; close buttons such as "×" and aria-hidden icons left out)',
62
+ whenEnabled: 'Shows all of the element text; cannot be combined with --raw or --node-id',
63
+ tokenImpact: 'A page-sized container can add thousands of tokens; target the element you need',
64
+ },
45
65
  'get:--all': {
46
66
  default: 'Returns first matching element only',
47
67
  whenEnabled: 'Returns all matching elements (only works with --raw)',
48
68
  },
49
- 'get:--nth': {
50
- default: 'Returns first matching element',
51
- whenEnabled: 'Returns the nth matching element (0-based index, only works with --raw)',
69
+ 'get:--index': {
70
+ default: 'Returns the first matching element (body without a selector)',
71
+ whenEnabled: 'Returns that match of the selector (0-based), in semantic and --raw output; --nth is an alias',
72
+ automaticBehavior: 'Out of range exits 81; with a numeric index argument (a cached query index) it exits 81',
73
+ },
74
+ 'query:--limit': {
75
+ default: 'dom a11y query lists the first 50 matches and says how many more there are; --json returns all of them',
76
+ whenEnabled: 'Lists that many matches (0 = all), in human and JSON output; count is always the total, JSON omitted the rest',
77
+ automaticBehavior: 'All matches are cached for index-based access (bdg dom click 55 works even when 50 are listed); an element the page and frame trees both report is listed once. Indices work with click, fill, hover, pressKey, scroll, submit, layout, get and listeners, also for elements of a cross-origin iframe of the same site (a consent dialog), whose scripts then run in that frame',
78
+ tokenImpact: 'About one line per match; a page can have hundreds of links',
52
79
  },
53
80
  'eval:--frame': {
54
81
  default: "Evaluates in the page's main frame",
55
82
  whenEnabled: "Evaluates in one iframe's main world (its own globals), including cross-origin (out-of-process) iframes; output gains a frame field (its URL)",
56
- automaticBehavior: 'The value is matched as: a 0-based index (bdg dom frames order, main page not counted), else an exact name/id attribute, else a case-insensitive part of the URL. Several matches fail with 81 listing them; none fails with 83 listing all frames. Frames are looked up on every call (a reloaded iframe is found again).',
83
+ automaticBehavior: 'The value is matched as: a 0-based index (bdg dom frames order: document order of the <iframe> elements, nested ones depth-first, main page not counted), else an exact name/id attribute, else a case-insensitive part of the name, id or URL. Several matches fail with 81 listing them; none fails with 83 listing all frames. Frames are looked up on every call (a reloaded iframe is found again). An index that names another frame than in the last bdg dom frames listing (iframes added, removed or moved, or the page navigated) fails with 87 STALE_CACHE: re-run bdg dom frames or pick the frame by name.',
57
84
  },
58
85
  'console:-H': {
59
86
  default: 'Shows messages from current page load only (most recent navigation)',
@@ -73,14 +100,19 @@ const OPTION_BEHAVIORS = {
73
100
  default: 'Smart summary with errors deduplicated and warnings grouped',
74
101
  whenEnabled: 'Lists all messages chronologically without deduplication',
75
102
  },
103
+ 'console:--last': {
104
+ default: 'Smart summary (without --list); a list shows the last 100 messages',
105
+ whenEnabled: 'Lists the last N messages (0 = all) chronologically, also without --list; JSON gets messages',
106
+ automaticBehavior: 'The [n] shown are positions in the session message list (what bdg details console <n> takes); when the page or level filter left messages out between the listed ones, a note says how many and why',
107
+ },
76
108
  'console:--level': {
77
109
  default: 'Shows all log levels (error, warning, log, info, debug)',
78
110
  whenEnabled: 'Filters to specific level: error, warning, log, info, or debug',
79
111
  },
80
112
  'fill:--no-wait': {
81
- default: 'Waits for network stability after filling input (200ms idle)',
82
- whenDisabled: 'Returns immediately without waiting for network',
83
- automaticBehavior: 'Network wait helps ensure React/Vue state updates complete before next action',
113
+ default: 'Waits for network stability after filling input (150ms idle, up to 2s)',
114
+ whenDisabled: NO_WAIT_TRIGGERED_REQUESTS,
115
+ automaticBehavior: `Network wait helps ensure React/Vue state updates complete before next action. The value is read back after filling: when the page rejected or moved it, the output starts with a warning and JSON has valueMismatch { expected, actual } (exit code stays 0), plus movedTo naming the field of the form that got the value instead. ${TRIGGERED_REQUESTS_BEHAVIOR}. ${ACTION_EFFECTS_BEHAVIOR}`,
84
116
  },
85
117
  'fill:--no-blur': {
86
118
  default: 'Triggers blur event after filling (validates most form fields)',
@@ -88,9 +120,10 @@ const OPTION_BEHAVIORS = {
88
120
  automaticBehavior: 'Blur triggers validation in most frameworks - disable only if you need to continue typing',
89
121
  },
90
122
  'click:--no-wait': {
91
- default: 'Waits for network stability after click (200ms idle)',
92
- whenDisabled: 'Returns immediately without waiting for network',
93
- automaticBehavior: 'Network wait helps ensure AJAX requests triggered by click complete. The click itself uses real mouse events in the visible part of the element (method "mouse"); if the element is covered or has no size it falls back to DOM events (method "dom", with a warning)',
123
+ default: 'Waits for network stability after click (150ms idle, up to 2s)',
124
+ whenDisabled: NO_WAIT_TRIGGERED_REQUESTS,
125
+ automaticBehavior: `Network wait helps ensure AJAX requests triggered by click complete. ${TRIGGERED_REQUESTS_BEHAVIOR}. The click itself uses real mouse events in the visible part of the element (method "mouse"); if the element is covered or has no size it falls back to DOM events (method "dom", with a warning; --strict refuses instead). Results the page shows later (timers, spinners, slow renders) are not waited for but reported as pending work: use bdg dom wait <selector> --visible. ${ACTION_EFFECTS_BEHAVIOR}. ${STILL_CHANGING_BEHAVIOR}. A click with no DOM change, no request and no navigation (checked again 300 ms later, which adds 300 ms plus at most 250 ms for the read) is reported as ⚠ Element Clicked (no visible effect observed: no DOM change, requests or navigation within 300 ms) and effect: "none" in JSON (exit code stays 0); not claimed with --no-wait, for hover or right-click, after a copy or cut, or when the click hit a form control, label, media, iframe, popover button, a mailto:/tel:/javascript: or other non-http link, a link to another window or a custom element with a closed shadow root, or moved focus to an element that is not a button or link. Effects outside the DOM (CSS :hover/:focus-within styles, canvas, clipboard without a copy event) are not seen`,
126
+ tokenImpact: 'A click that navigates lists the whole page load in JSON triggeredRequests',
94
127
  },
95
128
  'click:--double': {
96
129
  default: 'Single click',
@@ -100,19 +133,29 @@ const OPTION_BEHAVIORS = {
100
133
  default: 'Left click',
101
134
  whenEnabled: 'Right-click: the page gets contextmenu (custom context menus open); cannot be combined with --double',
102
135
  },
136
+ 'click:--strict': {
137
+ default: 'A covered, hidden or zero-size element is clicked with DOM events (method "dom", with a warning, exit 0)',
138
+ whenEnabled: 'Refuses with exit 90 (RESOURCE_CONFLICT: the page state blocks the request) when a real mouse cannot reach the element, naming what covers it and suggesting bdg dom layout <selector>; also when the mouse press never reached the element (it is released, no further presses for --double)',
139
+ automaticBehavior: 'Applies to --double and --right too. Nothing is dispatched when the element is unreachable, so the page is unchanged',
140
+ },
103
141
  'hover:--no-wait': {
104
142
  default: 'Waits for network stability after moving the mouse (menus may load content)',
105
- whenDisabled: 'Returns immediately without waiting for network',
106
- automaticBehavior: 'The mouse stays over the element afterwards, so hover menus stay open until the next mouse action',
143
+ whenDisabled: NO_WAIT_TRIGGERED_REQUESTS,
144
+ automaticBehavior: `The mouse stays over the element afterwards, so hover menus stay open until the next mouse action. ${TRIGGERED_REQUESTS_BEHAVIOR}. ${ACTION_EFFECTS_BEHAVIOR}. ${SHOWN_BEHAVIOR}; for a hover also elements around it (its parent's subtree) and tooltips, menus, listboxes, dialogs and popovers anywhere that were hidden before, so captions shown by CSS :hover count (hidden elements noted by identity right before the mouse moves: up to 1500, within 8 ms). A hover never claims "no visible effect" and does not check whether the page was still changing`,
145
+ },
146
+ 'hover:--strict': {
147
+ default: 'A covered, hidden or zero-size element gets synthetic mouseover/mouseenter events (method "dom", with a warning)',
148
+ whenEnabled: 'Refuses with exit 90 when a real mouse cannot reach the element, naming what covers it and suggesting bdg dom layout <selector>',
107
149
  },
108
150
  'navigate:--no-wait': {
109
151
  default: 'Waits until the new page has loaded and the network and DOM are idle (up to 15 s)',
110
152
  whenDisabled: 'Returns as soon as the navigation has started',
111
- automaticBehavior: 'Also applies to page reload/back/forward; indices from earlier queries become stale (87)',
153
+ automaticBehavior: 'Also applies to page reload/back/forward; indices from earlier queries become stale (87). Triggered requests are not listed (they are the page load; see bdg network list)',
112
154
  },
113
155
  'pressKey:--no-wait': {
114
- default: 'Waits for network stability after key press (200ms idle)',
115
- whenDisabled: 'Returns immediately without waiting for network',
156
+ default: 'Waits for network stability after key press (150ms idle, up to 2s)',
157
+ whenDisabled: NO_WAIT_TRIGGERED_REQUESTS,
158
+ automaticBehavior: `${TRIGGERED_REQUESTS_BEHAVIOR}. ${ACTION_EFFECTS_BEHAVIOR}. ${SHOWN_BEHAVIOR}, such as the item Enter added to a list. ${STILL_CHANGING_BEHAVIOR}. A key press never claims "no visible effect"`,
116
159
  },
117
160
  'pressKey:--times': {
118
161
  default: 'Presses key once',
@@ -124,16 +167,81 @@ const OPTION_BEHAVIORS = {
124
167
  'submit:--wait-navigation': {
125
168
  default: 'Waits for network stability only',
126
169
  whenEnabled: 'Waits for page navigation to complete (use for forms that redirect)',
170
+ automaticBehavior: 'A navigation is a new document loading in the main frame, also at the same URL (a POST that redirects back to the form after a login error). When the new page loaded but requests were still running at --timeout, the submit succeeds with a warning; without a navigation it exits 102, and the hint says whether a page request was sent (slow server) or not (the form may submit via fetch)',
127
171
  },
128
172
  'submit:--wait-network': {
129
173
  default: 'Default network idle timeout',
130
174
  whenEnabled: 'Custom network idle timeout in ms (use for slow APIs)',
175
+ automaticBehavior: `${TRIGGERED_REQUESTS_BEHAVIOR}. ${ACTION_EFFECTS_BEHAVIOR}. A submit with no DOM change, request or navigation reports effect: "none" like dom click`,
176
+ },
177
+ 'wait:--timeout': {
178
+ default: 'Gives up after 10000 ms',
179
+ whenEnabled: 'Gives up after the given milliseconds (1 to 600000)',
180
+ automaticBehavior: 'The page is watched (DOM mutations plus a 100 ms poll for style changes) and answers as soon as the matches change; a navigation during the wait continues it on the new document. A timeout exits 102 (CDP_TIMEOUT) with what the page showed last, e.g. "2 matches, none visible" or "document.readyState: loading", and a next step (dom query for no matches, dom layout for hidden ones, peek for a page still loading)',
181
+ },
182
+ 'wait:--visible': {
183
+ default: 'Counts every match, hidden ones included',
184
+ whenEnabled: 'Counts only visible matches (rendered, non-empty box, visibility: visible; opacity 0 counts as visible, as with :visible)',
185
+ automaticBehavior: 'With --gone, waits until no match is visible (the element may stay in the DOM hidden)',
186
+ },
187
+ 'wait:--gone': {
188
+ default: 'Waits for at least one match',
189
+ whenEnabled: 'Waits until nothing matches (nothing visible with --visible, nothing containing the text with --text), seen twice in a row in the same document once it is no longer loading, so the empty document right after a navigation does not count (a page stuck in readyState loading never meets it)',
190
+ },
191
+ 'wait:--text': {
192
+ default: 'Any match counts',
193
+ whenEnabled: 'Only matches whose text contains the given text count (case-insensitive, whitespace collapsed; hidden elements are matched by their text nodes, as with :has-text)',
194
+ automaticBehavior: 'Needs a selector; use body to look in the whole page',
195
+ },
196
+ 'wait:--load': {
197
+ default: 'Does not look at document.readyState',
198
+ whenEnabled: 'Also waits for document.readyState "complete"; without a selector waits only for that (a script whose server never answers keeps it "loading")',
131
199
  },
132
200
  'listeners:--type': {
133
201
  default: 'Lists listeners of every event type on the element, its ancestors (through open shadow roots), its document and window',
134
- whenEnabled: 'Lists only these event types (comma-separated, case-sensitive like addEventListener)',
135
- automaticBehavior: 'Listeners are grouped by type, nearest first; JSON line/column numbers are 0-based (human output shows them 1-based like DevTools). The Debugger domain is not enabled',
136
- tokenImpact: 'Pages with many window/document listeners produce long lists; --type keeps the output short',
202
+ whenEnabled: 'Lists only these event types (comma-separated or repeated, case-sensitive like addEventListener); when none match, typeSuggestions names close types (Click, onclick → click)',
203
+ automaticBehavior: 'Listeners are grouped by type, the element\'s own handlers first (types handled on the element before delegated ones; nearest first, no-ops last); JSON line/column numbers are 0-based (human output shows them 1-based like DevTools). React\'s on… props that run for the element (its own and its React parents\', through portals and from a nested root into the outer root; onFocus/onBlur as focusin/focusout; parents\' props for non-bubbling events left out) are listed with source and location (framework: "React", reactProp: "onClick"; at most 50 of the requested types, reactHandlersSkipped counts the rest). jQuery handlers are shown instead of jQuery\'s dispatcher (framework: "jQuery", delegateSelector for delegates the element matches; a dispatcher with no handler for the element is omitted; at most 50 are resolved, jqueryHandlersSkipped counts the rest). Preact\'s event proxy is replaced by the handler Preact runs (framework: "Preact"). Empty handlers (React\'s onclick placeholder) are marked noop and do not count as the element\'s own handler. The Debugger domain is not enabled',
204
+ tokenImpact: 'Framework roots are already collapsed; --type keeps the output short on pages with many listeners',
205
+ },
206
+ 'listeners:--all': {
207
+ default: 'Collapses React roots: on an ancestor recognised as a React root container (React\'s keys on the node, or dispatchers named dispatchDiscreteEvent/dispatchContinuousEvent/dispatchEvent), the function objects that each listen for several event types become one line / one "collapsed" entry with its types, phases and dispatchers. Other multi-type handlers are never collapsed',
208
+ whenEnabled: 'Lists every listener of framework roots individually',
209
+ tokenImpact: 'On React pages --all adds a row per event type and phase (about 140 rows, 60 KB of JSON)',
210
+ },
211
+ 'layout:--index': {
212
+ default: 'Reports every match of the selector (human output lists the first 20, JSON up to 100 plus an omitted count); a numeric argument reports that cached query element',
213
+ whenEnabled: 'Reports only the nth match (0-based); out of range exits 81',
214
+ automaticBehavior: 'Coordinates are CSS px: bounds relative to the top-level page (iframe offsets and page scroll included), viewport relative to the visible area. Iframes and overflow containers (scroll lists, overflow: hidden) clip what counts as visible (clippedBy names the one cutting it off). scrollBy brings the whole element into view and is limited to how far the page can scroll: for an element out of view it centres it (aligns its start when it is larger than the viewport; human output says "to centre it"), for a partly visible one it is the smallest scroll that shows all of it (the part cut off at the top or bottom; the start of one larger than the viewport; "partly visible (87%); scroll up 5px to see all of it"). Elements a page script moves on scroll (floating menus) may move again after it; fixed and sticky elements (page scroll does not move them, or only until they stick) and ones beyond that range get offScreenReason instead, which says "page scrolling is locked (…)" when the page cannot scroll because body/html is position: fixed or overflow: hidden, so in-flow content is not called fixed; a visible dialog (dialog[open], [aria-modal=true], [role=dialog|alertdialog]) is named as the likely cause ("likely by dialog div#consent"). page.viewport is the layout viewport without scrollbars, as dom scroll reports it; page.colorScheme is the prefers-color-scheme media feature the page sees (not the theme it renders). Content in a closed <details> or under content-visibility: hidden is hidden. coveredBy is the topmost element at the center of the largest visible box (none for pointer-events: none, nor for an element of the same click target: an overlay inside the link, button or label the element is in, a link to the same URL, or the textless absolutely positioned overlay link spanning the card that holds plain content); inert elements are flagged, not hidden',
215
+ tokenImpact: 'About one line per element; a cheap alternative to screenshots for "where is it?"',
216
+ },
217
+ 'inspect:--index': {
218
+ default: 'Inspects the first rendered match (the first when none is rendered) and notes how many matched; a numeric argument inspects that cached element (from dom query, dom form or dom a11y query)',
219
+ whenEnabled: 'Inspects the nth match (0-based); out of range exits 81',
220
+ automaticBehavior: 'Answers "what does it look like" without a screenshot, grouped like Figma Dev Mode: header (element, text, size and page position, [flex]/[grid], [not rendered]/[hidden]/[offscreen]/[covered by …], prefers-color-scheme), box (margin, padding, border widths, box-sizing, overflow, scroll size), layout (display, position, flex/grid container and item settings), parent (its display and layout, distances to its content edges, gaps to the neighbouring siblings), text (first font family → the font Chrome rendered, (webfont) or local; weight size/line-height; color; WCAG contrast against the composited background; only for elements with text), fill, border (sides, radius, outline), fx (shadow, transform, filter, opacity, blend), state (cursor, pointer-events, user-select, appearance), pseudo (::before/::after with content, ::placeholder) and a child tree (depth 2, 20 rows, identical siblings grouped). Values that change nothing (0, none, transparent, normal) are left out; colors are hex (lab/oklch from Tailwind converted), lengths px without the unit, rounded to 0.1. Secrets are never shown. JSON uses Figma-aligned names (rect, box, layout.sizing hug/fill/fixed, text, fills, strokes, radius, effects, children). Also by default: hints, the element\'s own declarations that have no effect (justify-content on a block, width on an inline element, top on a static one, var() of an unset custom property) with the reason, the fix and the rule\'s file:line',
221
+ tokenImpact: 'About 60–80 tokens for the styles and 30–70 more for the child tree (--tree 0 drops it), against about 1,500 for a screenshot or 3,000+ for raw computed styles',
222
+ },
223
+ 'inspect:--all': {
224
+ default: 'Shows the curated groups (the properties that define the look)',
225
+ whenEnabled: 'Lists every computed property that differs from the default of the same element type, longhands collapsed into shorthands, noise (logical duplicates, currentColor echoes, custom properties) dropped',
226
+ tokenImpact: 'About 80 tokens instead of 60–80',
227
+ },
228
+ 'inspect:--props': {
229
+ default: 'Shows the curated groups',
230
+ whenEnabled: 'Shows only the named properties (custom properties like --brand included, "(not set)" when no rule sets one; --* lists every custom property the element has, --bs-btn-* those with a prefix), each computed and normalized; an unknown name exits 81 with a suggestion',
231
+ },
232
+ 'inspect:--rules': {
233
+ default: 'Shows the values, not where they come from',
234
+ whenEnabled: "Adds a rules group: for each shown property the page's CSS sets, the value as written (with the computed value when it uses var()), selector, file:line (column for minified files), @media/@container condition, cascade layer, how many ancestors up it is inherited from, and the rules it beats. Sides one declaration sets are one row; browser defaults are left out. With --props, only those properties",
235
+ automaticBehavior: 'The cascade is computed by bdg from CSS.getMatchedStylesForNode (origin, !important, style attribute, layers, specificity and order); reading it waits up to 5 s, then the output notes the cascade was not read',
236
+ tokenImpact: 'About 15–25 tokens per row, 5–20 rows',
237
+ },
238
+ 'inspect:--why': {
239
+ default: 'Not shown',
240
+ whenEnabled: "Adds why <property> = computed value, then every declaration of it on the element, highest precedence first: ✓ the winner (or the inherited ancestor's), ✗ the ones it beats, browser defaults included. Each rule shows its selector specificity [ids,classes,types]. var() values are shown substituted (or invalid: --x not set), with where the winner's custom properties are set, followed up to :root. Logical names map to physical ones (margin-inline-start → margin-left); a shorthand (padding, border) gives one answer when one declaration sets all its sides, else one per side",
241
+ },
242
+ 'inspect:--no-hints': {
243
+ default: "Hints at the element's own author declarations that have no effect (flex/grid properties without flex or grid, item properties without a flex or grid parent, offsets on static elements, sizes on inline ones, var() of an unset custom property, form controls in the browser's font), within a 1 s budget; hints none when nothing was found",
244
+ whenEnabled: 'Skips the hints and does not read the matched rules',
137
245
  },
138
246
  'scroll:--down': {
139
247
  whenEnabled: 'Scrolls page down by specified pixel amount',
@@ -152,11 +260,12 @@ const OPTION_BEHAVIORS = {
152
260
  },
153
261
  'scroll:--bottom': {
154
262
  whenEnabled: 'Scrolls to the very bottom of the page',
263
+ automaticBehavior: 'A page scroll (--down/--up/--left/--right/--top/--bottom) that moved nothing still exits 0 but starts with a warning saying why: the document is no taller (wider) than the viewport, the page was already at that edge, or scrolling is locked; while document.readyState is not complete it adds that the page is still loading (bdg dom wait --load)',
155
264
  },
156
265
  'scroll:--no-wait': {
157
- default: 'Waits for lazy-loaded content to stabilize after scroll (200ms network idle)',
158
- whenDisabled: 'Returns immediately without waiting for lazy-loaded content',
159
- automaticBehavior: 'Wait helps ensure images and infinite scroll content load before next action',
266
+ default: 'Waits for lazy-loaded content to stabilize after scroll (150ms network idle, up to 2s)',
267
+ whenDisabled: NO_WAIT_TRIGGERED_REQUESTS,
268
+ automaticBehavior: `Wait helps ensure images and infinite scroll content load before next action. ${TRIGGERED_REQUESTS_BEHAVIOR}. ${ACTION_EFFECTS_BEHAVIOR}`,
160
269
  },
161
270
  'scroll:--index': {
162
271
  whenEnabled: 'If selector matches multiple elements, scrolls to the nth element (0-based)',
@@ -196,6 +305,11 @@ const OPTION_BEHAVIORS = {
196
305
  default: 'Compact output (truncated URLs, no resource types)',
197
306
  whenEnabled: 'Verbose output with full URLs and resource types',
198
307
  },
308
+ 'bdg:--session': {
309
+ default: 'The default session in ~/.bdg (or $BDG_SESSION_DIR); BDG_SESSION=<name> selects a named session like the flag',
310
+ whenEnabled: 'Uses the named session in ~/.bdg/sessions/<name>/ (or $BDG_SESSION_DIR/sessions/<name>/) with its own daemon, Chrome, profile and port; every command (status, stop, cleanup, ...) acts on that session only',
311
+ automaticBehavior: 'Accepted before or after any subcommand; --session wins over BDG_SESSION. Names are case-insensitive (lower-cased: ALPHA is alpha). Without --port a named session takes the first free port above 9222 not claimed by another running session, and keeps it in port.txt. Names: 1-40 letters, digits, "-" or "_", starting with a letter or digit (exit 81 otherwise, also when the socket path would be too long). Hints and suggestions in its output carry --session <name>',
312
+ },
199
313
  'cleanup:-f': {
200
314
  default: 'Refuses to run while a session is active; removes files left by a crashed session',
201
315
  whenEnabled: 'Kills the running daemon and its Chrome first (use when a session is stuck)',
@@ -203,6 +317,25 @@ const OPTION_BEHAVIORS = {
203
317
  'cleanup:--aggressive': {
204
318
  whenEnabled: 'Alias for --force, kept for compatibility',
205
319
  },
320
+ 'cleanup:--purge': {
321
+ default: "A named session's directory (Chrome profile, ~60 MB; logs; port.txt) is kept for its next start",
322
+ whenEnabled: 'After cleaning up, deletes the directory of the session named by --session (exit 81 without --session); a running session is refused unless --force is given, and the directory is kept (exit 90) if the daemon still answers, cleanup reported a problem, or its Chrome has not exited',
323
+ },
324
+ 'bdg:--viewport': {
325
+ default: 'A launched Chrome opens a 1920x1080 window (the viewport is smaller by the scrollbar, and in a visible window by the browser UI); an attached Chrome keeps its window',
326
+ whenEnabled: 'The page gets exactly that viewport (CSS px, e.g. 1280x800) for the whole session, through navigations and reloads (Emulation.setDeviceMetricsOverride at the display pixel ratio); a launched Chrome also opens its window at that size, so tabs the page opens get it too',
327
+ automaticBehavior: 'Works with --chrome-ws-url: the override belongs to the session, and Chrome drops it when the session ends, so the attached browser gets its own size back. bdg status shows the resulting layout viewport without the scrollbar (Viewport: 1265×800 (emulated 1280x800)). Invalid sizes (not WxH, a side outside 1-10000) exit 81',
328
+ },
329
+ 'bdg:--color-scheme': {
330
+ default: 'The page sees the system setting for prefers-color-scheme (headless Chrome follows the OS, so a dark OS renders dark pages); bdg status and dom layout show which one',
331
+ whenEnabled: 'Emulates prefers-color-scheme: light or dark for the whole session (Emulation.setEmulatedMedia); other values exit 81 with a suggestion',
332
+ automaticBehavior: 'Applies to the session page (and its same-process iframes); Chrome drops it when the session ends, also for an attached Chrome (--chrome-ws-url)',
333
+ },
334
+ 'bdg:--chrome-ws-url': {
335
+ default: 'bdg launches its own Chrome (closed on stop)',
336
+ whenEnabled: 'Attaches to a running Chrome instead; it keeps running after stop. --port, -u and --[no-]headless cannot be combined with it (exit 81)',
337
+ automaticBehavior: 'A port (9222), host:port or http://host:port is turned into the browser WebSocket URL via /json/version; a browser URL uses the first open tab. Refused with exit 90 when another running bdg session launched that Chrome or drives that tab (sessions of this BDG_SESSION_DIR, and of others that claimed a port)',
338
+ },
206
339
  'stop:--kill-chrome': {
207
340
  default: 'Chrome launched by bdg is always closed on stop; an attached Chrome (--chrome-ws-url) is left running',
208
341
  whenEnabled: 'No additional effect; kept for compatibility',
@@ -1,7 +1,8 @@
1
1
  /**
2
- * `bdg page navigate|reload|back|forward` — move the session's page.
2
+ * `bdg page navigate|reload|back|forward` — move the session's page;
3
+ * `bdg page info` — where it is.
3
4
  */
4
- import type { Command } from 'commander';
5
+ import { type Command } from 'commander';
5
6
  /**
6
7
  * Register the `page` command group.
7
8
  *
@@ -1,12 +1,16 @@
1
1
  /**
2
- * `bdg page navigate|reload|back|forward` — move the session's page.
2
+ * `bdg page navigate|reload|back|forward` — move the session's page;
3
+ * `bdg page info` — where it is.
3
4
  */
4
- import { runCommand } from './shared/CommandRunner.js';
5
+ import { Option } from 'commander';
6
+ import { noActiveSessionError, runCommand } from './shared/CommandRunner.js';
5
7
  import { jsonOption } from './shared/commonOptions.js';
8
+ import { parseColorScheme, parseViewport } from './start.js';
9
+ import { CommandError } from '../errors/index.js';
6
10
  import { javascriptNavigationError } from '../errors/messages.js';
7
- import { pageNavigate } from '../ipc/client.js';
11
+ import { getStatus, pageEmulate, pageNavigate } from '../ipc/client.js';
8
12
  import { OutputFormatter } from '../ui/formatting.js';
9
- import { PAGE_ACTION_DESCRIPTIONS, PAGE_ACTION_DONE } from '../ui/messages/commands.js';
13
+ import { PAGE_ACTION_DESCRIPTIONS, PAGE_ACTION_DONE, PAGE_EMULATE_DESCRIPTION, PAGE_INFO_DESCRIPTION, pageEmulateNothingError, pageEmulationLines, pageLoadingWarning, } from '../ui/messages/commands.js';
10
14
  import { EXIT_CODES } from '../utils/exitCodes.js';
11
15
  import { validateUrl } from '../utils/url.js';
12
16
  /**
@@ -27,6 +31,8 @@ function formatPageResult(result) {
27
31
  ], 8);
28
32
  if (result.warning)
29
33
  fmt.text(`⚠ ${result.warning}`);
34
+ if (result.loading)
35
+ fmt.text(`⚠ ${pageLoadingWarning(result.loading)}`);
30
36
  return fmt.build();
31
37
  }
32
38
  /**
@@ -85,6 +91,75 @@ async function runPageAction(action, options, url) {
85
91
  return { success: true, data: response.data };
86
92
  }, options, formatPageResult);
87
93
  }
94
+ /**
95
+ * `bdg page info`: URL and title of the session page.
96
+ *
97
+ * @param options - Command options
98
+ */
99
+ async function showPageInfo(options) {
100
+ await runCommand(async () => {
101
+ const response = await getStatus();
102
+ if (response.status === 'error') {
103
+ return {
104
+ success: false,
105
+ error: response.error ?? 'Failed to read the page',
106
+ exitCode: EXIT_CODES.SOFTWARE_ERROR,
107
+ };
108
+ }
109
+ const page = response.data?.sessionPid ? response.data.pageState : undefined;
110
+ if (!page)
111
+ throw noActiveSessionError();
112
+ return { success: true, data: { url: page.url, title: page.title } };
113
+ }, options, (page) => new OutputFormatter()
114
+ .keyValueList([
115
+ ['URL', page.url],
116
+ ['Title', page.title],
117
+ ], 8)
118
+ .build());
119
+ }
120
+ /**
121
+ * The emulation request from the options.
122
+ *
123
+ * @param options - Command options
124
+ * @returns Request
125
+ * @throws CommandError (81) for nothing to change or an invalid value
126
+ */
127
+ function emulationRequest(options) {
128
+ if (options.reset)
129
+ return { reset: true };
130
+ if (options.viewport === undefined && options.colorScheme === undefined) {
131
+ const err = pageEmulateNothingError();
132
+ throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.INVALID_ARGUMENTS);
133
+ }
134
+ return {
135
+ ...(options.viewport !== undefined && { viewport: parseViewport(options.viewport) }),
136
+ ...(options.colorScheme !== undefined && {
137
+ colorScheme: parseColorScheme(options.colorScheme),
138
+ }),
139
+ };
140
+ }
141
+ /**
142
+ * `bdg page emulate`: change the viewport or color scheme mid-session.
143
+ *
144
+ * @param options - Command options
145
+ */
146
+ async function emulate(options) {
147
+ await runCommand(async () => {
148
+ const response = await pageEmulate(emulationRequest(options));
149
+ if (response.status === 'error' || !response.data) {
150
+ return {
151
+ success: false,
152
+ error: response.error ?? 'Failed to change the emulation',
153
+ exitCode: response.exitCode ?? EXIT_CODES.SOFTWARE_ERROR,
154
+ ...(response.suggestion && { errorContext: { suggestion: response.suggestion } }),
155
+ };
156
+ }
157
+ return { success: true, data: response.data };
158
+ }, options, (result) => new OutputFormatter()
159
+ .text('✓ Page emulation changed')
160
+ .keyValueList(pageEmulationLines(result), 10)
161
+ .build());
162
+ }
88
163
  /**
89
164
  * Register the `page` command group.
90
165
  *
@@ -93,7 +168,14 @@ async function runPageAction(action, options, url) {
93
168
  export function registerPageCommands(program) {
94
169
  const page = program
95
170
  .command('page')
96
- .description('Navigate the session page: navigate <url>, reload, back, forward');
171
+ .description('The session page: info (URL and title), navigate <url>, reload, back, forward, emulate (viewport, color scheme)');
172
+ page
173
+ .command('info')
174
+ .description(PAGE_INFO_DESCRIPTION)
175
+ .addOption(jsonOption())
176
+ .action(async (options) => {
177
+ await showPageInfo(options);
178
+ });
97
179
  const withCommon = (command) => command
98
180
  .option('--no-wait', 'Return without waiting for the page to load')
99
181
  .addOption(jsonOption());
@@ -103,6 +185,19 @@ export function registerPageCommands(program) {
103
185
  .argument('<url>', 'URL to load')).action(async (url, options) => {
104
186
  await runPageAction('navigate', options, url);
105
187
  });
188
+ page
189
+ .command('emulate')
190
+ .description(PAGE_EMULATE_DESCRIPTION)
191
+ .option('--viewport <WxH>', 'Viewport size in CSS px, e.g. 900x700')
192
+ .option('--color-scheme <scheme>', 'Emulate prefers-color-scheme: light or dark')
193
+ .addOption(new Option('--reset', 'Back to the browser window size and the system setting').conflicts([
194
+ 'viewport',
195
+ 'colorScheme',
196
+ ]))
197
+ .addOption(jsonOption())
198
+ .action(async (options) => {
199
+ await emulate(options);
200
+ });
106
201
  for (const action of ['reload', 'back', 'forward']) {
107
202
  withCommon(page.command(action).description(PAGE_ACTION_DESCRIPTIONS[action])).action(async (options) => {
108
203
  await runPageAction(action, options);
@@ -3,9 +3,9 @@
3
3
  */
4
4
  import { runCommand } from './shared/CommandRunner.js';
5
5
  import { jsonOption, showBothSectionsWhenBothRequested } from './shared/commonOptions.js';
6
- import { handleDaemonConnectionError, noteFollowConnected, } from './shared/daemonErrorHandler.js';
6
+ import { noteFollowConnected } from './shared/daemonErrorHandler.js';
7
7
  import { fetchPreviewOutput, createErrorResult, } from './shared/dataFetcher.js';
8
- import { setupFollowMode } from './shared/followMode.js';
8
+ import { followFetchFailure, setupFollowMode, } from './shared/followMode.js';
9
9
  import { handleValidationError } from './shared/handleValidationError.js';
10
10
  import { MAX_LAST_ITEMS, positiveIntRule, resourceTypeRule } from './shared/validation.js';
11
11
  import { filterByResourceType } from '../telemetry/filters.js';
@@ -79,21 +79,14 @@ async function runFollowMode(options, lastN, resourceTypes, baseOptions) {
79
79
  const showPreview = async () => {
80
80
  const result = await fetchAndFilterPreview(lastN, resourceTypes, peekSection(options));
81
81
  if (!result.success) {
82
- const errorResult = handleDaemonConnectionError(result.error, {
83
- json: options.json,
84
- follow: true,
85
- retryIntervalMs: 1000,
86
- exitCode: result.exitCode,
87
- });
88
- if (errorResult.shouldExit)
89
- process.exit(errorResult.exitCode);
90
- return;
82
+ return followFetchFailure(result, { json: options.json, retryIntervalMs: 1000 });
91
83
  }
92
84
  noteFollowConnected();
93
85
  if (!options.json)
94
86
  console.clear();
95
87
  const previewOptions = createPreviewOptions(baseOptions, resourceTypes, result.data.unfilteredNetworkCount);
96
88
  console.log(formatPreview(result.data.output, previewOptions));
89
+ return undefined;
97
90
  };
98
91
  await setupFollowMode(showPreview, {
99
92
  startMessage: followingPreviewMessage,
@@ -0,0 +1,8 @@
1
+ import type { Command } from 'commander';
2
+ /**
3
+ * Register the sessions command (lists running default and named sessions).
4
+ *
5
+ * @param program - Commander.js Command instance to register commands on
6
+ */
7
+ export declare function registerSessionsCommand(program: Command): void;
8
+ //# sourceMappingURL=sessions.d.ts.map
@@ -0,0 +1,19 @@
1
+ import { runCommand } from './shared/CommandRunner.js';
2
+ import { jsonOption } from './shared/commonOptions.js';
3
+ import { listRunningSessions } from '../session/sessionList.js';
4
+ import { formatSessionList } from '../ui/formatters/sessions.js';
5
+ /**
6
+ * Register the sessions command (lists running default and named sessions).
7
+ *
8
+ * @param program - Commander.js Command instance to register commands on
9
+ */
10
+ export function registerSessionsCommand(program) {
11
+ program
12
+ .command('sessions')
13
+ .description('List sessions (default and named) with their state, URL, port and PID, including crashed ones to clean up')
14
+ .addOption(jsonOption())
15
+ .action(async (options) => {
16
+ await runCommand(async () => ({ success: true, data: { sessions: await listRunningSessions() } }), options, formatSessionList);
17
+ });
18
+ }
19
+ //# sourceMappingURL=sessions.js.map