browser-debugger-cli 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/bdg/SKILL.md +2 -1
- package/dist/cdp/methodTarget.d.ts +92 -0
- package/dist/cdp/methodTarget.js +159 -0
- package/dist/cdp/protocol.d.ts +16 -1
- package/dist/cdp/protocol.js +21 -0
- package/dist/cdp/schema.d.ts +55 -1
- package/dist/cdp/schema.js +134 -25
- package/dist/cdp/types.d.ts +3 -1
- package/dist/commands/cdp.d.ts +38 -1
- package/dist/commands/cdp.js +200 -133
- package/dist/commands/cleanup.js +18 -4
- package/dist/commands/dom/formInteraction.js +8 -4
- package/dist/commands/dom/helpers/index.d.ts +4 -4
- package/dist/commands/dom/helpers/index.js +3 -3
- package/dist/commands/dom/helpers/query.d.ts +2 -2
- package/dist/commands/dom/helpers/query.js +2 -2
- package/dist/commands/dom/helpers/screenshot.d.ts +21 -26
- package/dist/commands/dom/helpers/screenshot.js +50 -668
- package/dist/commands/dom/screenshot.js +56 -36
- package/dist/commands/optionBehaviors.js +18 -8
- package/dist/commands/shared/CommandRunner.d.ts +5 -0
- package/dist/commands/shared/CommandRunner.js +18 -3
- package/dist/commands/shared/interrupt.d.ts +40 -0
- package/dist/commands/shared/interrupt.js +73 -0
- package/dist/commands/shared/optionTypes.d.ts +2 -0
- package/dist/commands/shared/startHelpers.d.ts +26 -3
- package/dist/commands/shared/startHelpers.js +145 -23
- package/dist/commands/types.d.ts +5 -0
- package/dist/connection/cdp.js +1 -16
- package/dist/connection/chromeIdentity.d.ts +24 -5
- package/dist/connection/chromeIdentity.js +53 -22
- package/dist/connection/launcher.d.ts +34 -1
- package/dist/connection/launcher.js +98 -10
- package/dist/connection/typed-cdp.d.ts +3 -2
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/daemon/SessionController.d.ts +10 -5
- package/dist/daemon/SessionController.js +15 -8
- package/dist/daemon/ipcServer.js +1 -1
- package/dist/daemon/launcher.d.ts +5 -0
- package/dist/daemon/launcher.js +8 -1
- package/dist/daemon/session/Session.d.ts +5 -1
- package/dist/daemon/session/Session.js +9 -8
- package/dist/daemon/session/TelemetryStore.d.ts +5 -0
- package/dist/daemon/session/TelemetryStore.js +4 -0
- package/dist/daemon/session/captureGate.d.ts +59 -0
- package/dist/daemon/session/captureGate.js +96 -0
- package/dist/daemon/session/chromeConnection.d.ts +16 -1
- package/dist/daemon/session/chromeConnection.js +34 -4
- package/dist/daemon/session/collectors.d.ts +15 -0
- package/dist/daemon/session/collectors.js +39 -2
- package/dist/daemon/session/commandRegistry.d.ts +14 -1
- package/dist/daemon/session/commandRegistry.js +46 -11
- package/dist/daemon/session/downloads.d.ts +32 -0
- package/dist/daemon/session/downloads.js +96 -0
- package/dist/daemon/session/interactions.d.ts +3 -2
- package/dist/daemon/session/interactions.js +7 -2
- package/dist/daemon/session/plugins.js +6 -0
- package/dist/daemon.js +12843 -11482
- package/dist/errors/CommandError.d.ts +2 -0
- package/dist/errors/issues.d.ts +1 -1
- package/dist/errors/messages.d.ts +58 -0
- package/dist/errors/messages.js +112 -0
- package/dist/index.js +999 -1020
- package/dist/ipc/client.d.ts +14 -1
- package/dist/ipc/client.js +21 -4
- package/dist/ipc/protocol/commands.d.ts +32 -2
- package/dist/ipc/protocol/commands.js +1 -0
- package/dist/ipc/protocol/domTypes.d.ts +24 -1
- package/dist/ipc/session/queries.d.ts +3 -0
- package/dist/ipc/session/types.d.ts +5 -0
- package/dist/ipc/transport/IPCError.d.ts +9 -0
- package/dist/ipc/transport/IPCError.js +12 -0
- package/dist/ipc/transport/errors.d.ts +2 -1
- package/dist/ipc/transport/errors.js +4 -1
- package/dist/ipc/transport/index.d.ts +4 -2
- package/dist/ipc/transport/index.js +13 -3
- package/dist/runtime/dom/actionEffects.d.ts +48 -9
- package/dist/runtime/dom/actionEffects.js +269 -34
- package/dist/runtime/dom/actionEffectsScripts.d.ts +45 -0
- package/dist/runtime/dom/actionEffectsScripts.js +101 -2
- package/dist/runtime/dom/captureArea.d.ts +35 -0
- package/dist/runtime/dom/captureArea.js +203 -0
- package/dist/runtime/dom/elementInfo.d.ts +10 -8
- package/dist/runtime/dom/elementInfo.js +8 -6
- package/dist/runtime/dom/formDiscovery.d.ts +1 -1
- package/dist/runtime/page/bdgWorld.d.ts +9 -0
- package/dist/runtime/page/bdgWorld.js +11 -0
- package/dist/runtime/page/captureEmulation.d.ts +119 -0
- package/dist/runtime/page/captureEmulation.js +189 -0
- package/dist/runtime/page/captureScroll.d.ts +24 -0
- package/dist/runtime/page/captureScroll.js +124 -0
- package/dist/runtime/page/screenshot.d.ts +41 -0
- package/dist/runtime/page/screenshot.js +394 -0
- package/dist/session/paths.d.ts +14 -0
- package/dist/session/paths.js +25 -0
- package/dist/telemetry/downloads.d.ts +127 -0
- package/dist/telemetry/downloads.js +265 -0
- package/dist/telemetry/har/builder.js +22 -7
- package/dist/telemetry/har/sanitize.d.ts +7 -3
- package/dist/telemetry/har/sanitize.js +52 -6
- package/dist/telemetry/har/sanitizeBody.d.ts +47 -7
- package/dist/telemetry/har/sanitizeBody.js +429 -56
- package/dist/telemetry/har/types.d.ts +2 -0
- package/dist/telemetry/network.d.ts +4 -4
- package/dist/telemetry/network.js +38 -4
- package/dist/telemetry/networkRetention.d.ts +35 -14
- package/dist/telemetry/networkRetention.js +62 -26
- package/dist/types.d.ts +9 -14
- package/dist/ui/OutputBuilder.d.ts +3 -2
- package/dist/ui/OutputBuilder.js +4 -3
- package/dist/ui/formatters/cdp.d.ts +32 -9
- package/dist/ui/formatters/cdp.js +77 -6
- package/dist/ui/formatters/details.js +7 -15
- package/dist/ui/formatters/preview.d.ts +2 -0
- package/dist/ui/formatters/preview.js +7 -1
- package/dist/ui/formatters/status.js +6 -1
- package/dist/ui/formatting.d.ts +7 -0
- package/dist/ui/formatting.js +13 -0
- package/dist/ui/logging/logger.d.ts +1 -1
- package/dist/ui/messages/chrome.d.ts +13 -0
- package/dist/ui/messages/chrome.js +26 -0
- package/dist/ui/messages/commands.d.ts +71 -3
- package/dist/ui/messages/commands.js +98 -3
- package/dist/ui/messages/networkMessages.d.ts +24 -5
- package/dist/ui/messages/networkMessages.js +31 -8
- package/dist/utils/async.d.ts +3 -2
- package/dist/utils/async.js +16 -3
- package/dist/utils/http.d.ts +11 -4
- package/dist/utils/http.js +5 -3
- package/package.json +18 -4
- /package/dist/{commands/dom → runtime/page}/screenshotResize.d.ts +0 -0
- /package/dist/{commands/dom → runtime/page}/screenshotResize.js +0 -0
package/dist/ipc/client.d.ts
CHANGED
|
@@ -92,8 +92,11 @@ export declare function getHARData(): Promise<HARDataResponse>;
|
|
|
92
92
|
*
|
|
93
93
|
* @param url - Target URL to navigate to
|
|
94
94
|
* @param options - Session configuration options
|
|
95
|
+
* @param signal - Cancels the start: closes the connection, so the daemon
|
|
96
|
+
* abandons the session it is starting
|
|
95
97
|
* @returns Start session response with daemon and Chrome PIDs
|
|
96
98
|
* @throws Error if connection fails, session already running, or Chrome launch fails
|
|
99
|
+
* @throws IPCCancelledError if `signal` aborts first
|
|
97
100
|
*
|
|
98
101
|
* @example
|
|
99
102
|
* ```typescript
|
|
@@ -107,7 +110,7 @@ export declare function getHARData(): Promise<HARDataResponse>;
|
|
|
107
110
|
* }
|
|
108
111
|
* ```
|
|
109
112
|
*/
|
|
110
|
-
export declare function startSession(url: string, options?: SessionOptions): Promise<StartSessionResponse>;
|
|
113
|
+
export declare function startSession(url: string, options?: SessionOptions, signal?: AbortSignal): Promise<StartSessionResponse>;
|
|
111
114
|
/**
|
|
112
115
|
* Request session stop from the daemon.
|
|
113
116
|
* Stops telemetry collection and closes Chrome.
|
|
@@ -258,6 +261,16 @@ export declare function domAudit(params: NoType<(typeof COMMANDS)['dom_audit']['
|
|
|
258
261
|
export declare function cssSearch(params: NoType<(typeof COMMANDS)['css_search']['requestSchema']>): Promise<ClientResponse<'css_search'>>;
|
|
259
262
|
/** What one element looks like: styles, box, layout and child tree. */
|
|
260
263
|
export declare function domInspect(params: NoType<(typeof COMMANDS)['dom_inspect']['requestSchema']>): Promise<ClientResponse<'dom_inspect'>>;
|
|
264
|
+
/**
|
|
265
|
+
* Capture the page or one element; the daemon puts back the emulation the
|
|
266
|
+
* capture changed before it answers.
|
|
267
|
+
*
|
|
268
|
+
* @param params - What to capture and how
|
|
269
|
+
* @param signal - Cancels the capture (the daemon skips it and only restores)
|
|
270
|
+
* @returns The image and what was captured
|
|
271
|
+
* @throws IPCCancelledError if `signal` aborts first
|
|
272
|
+
*/
|
|
273
|
+
export declare function domScreenshot(params: NoType<(typeof COMMANDS)['dom_screenshot']['requestSchema']>, signal?: AbortSignal): Promise<ClientResponse<'dom_screenshot'>>;
|
|
261
274
|
/**
|
|
262
275
|
* Wait until elements appear, become visible, contain a text or are gone,
|
|
263
276
|
* and/or the page has loaded.
|
package/dist/ipc/client.js
CHANGED
|
@@ -117,8 +117,11 @@ export async function getHARData() {
|
|
|
117
117
|
*
|
|
118
118
|
* @param url - Target URL to navigate to
|
|
119
119
|
* @param options - Session configuration options
|
|
120
|
+
* @param signal - Cancels the start: closes the connection, so the daemon
|
|
121
|
+
* abandons the session it is starting
|
|
120
122
|
* @returns Start session response with daemon and Chrome PIDs
|
|
121
123
|
* @throws Error if connection fails, session already running, or Chrome launch fails
|
|
124
|
+
* @throws IPCCancelledError if `signal` aborts first
|
|
122
125
|
*
|
|
123
126
|
* @example
|
|
124
127
|
* ```typescript
|
|
@@ -132,7 +135,7 @@ export async function getHARData() {
|
|
|
132
135
|
* }
|
|
133
136
|
* ```
|
|
134
137
|
*/
|
|
135
|
-
export async function startSession(url, options) {
|
|
138
|
+
export async function startSession(url, options, signal) {
|
|
136
139
|
const request = withSession({
|
|
137
140
|
type: 'start_session_request',
|
|
138
141
|
url,
|
|
@@ -151,7 +154,7 @@ export async function startSession(url, options) {
|
|
|
151
154
|
}),
|
|
152
155
|
});
|
|
153
156
|
await assertResponsive();
|
|
154
|
-
return sendRequest(request, 'start session', 'start_session_response');
|
|
157
|
+
return sendRequest(request, 'start session', 'start_session_response', undefined, undefined, signal);
|
|
155
158
|
}
|
|
156
159
|
/**
|
|
157
160
|
* Request session stop from the daemon.
|
|
@@ -180,10 +183,12 @@ export async function stopSession() {
|
|
|
180
183
|
* @param commandName - Name of the command to send
|
|
181
184
|
* @param params - Command parameters (without type field)
|
|
182
185
|
* @param timeoutMs - How long to wait (default: IPC timeout; page work can take long)
|
|
186
|
+
* @param signal - Cancels the command: closes the connection, which the daemon notices
|
|
183
187
|
* @returns Command response from the session
|
|
184
188
|
* @throws Error if connection fails or command execution fails
|
|
189
|
+
* @throws IPCCancelledError if `signal` aborts first
|
|
185
190
|
*/
|
|
186
|
-
async function sendCommand(commandName, params, timeoutMs) {
|
|
191
|
+
async function sendCommand(commandName, params, timeoutMs, signal) {
|
|
187
192
|
const request = {
|
|
188
193
|
...params,
|
|
189
194
|
type: `${commandName}_request`,
|
|
@@ -191,7 +196,7 @@ async function sendCommand(commandName, params, timeoutMs) {
|
|
|
191
196
|
};
|
|
192
197
|
if (timeoutMs === undefined || timeoutMs > getQuickIPCRequestTimeout())
|
|
193
198
|
await assertResponsive();
|
|
194
|
-
return sendRequest(request, commandName, `${commandName}_response`, timeoutMs);
|
|
199
|
+
return sendRequest(request, commandName, `${commandName}_response`, timeoutMs, undefined, signal);
|
|
195
200
|
}
|
|
196
201
|
/**
|
|
197
202
|
* Get details for a specific network request or console message.
|
|
@@ -376,6 +381,18 @@ export async function cssSearch(params) {
|
|
|
376
381
|
export async function domInspect(params) {
|
|
377
382
|
return sendCommand('dom_inspect', params);
|
|
378
383
|
}
|
|
384
|
+
/**
|
|
385
|
+
* Capture the page or one element; the daemon puts back the emulation the
|
|
386
|
+
* capture changed before it answers.
|
|
387
|
+
*
|
|
388
|
+
* @param params - What to capture and how
|
|
389
|
+
* @param signal - Cancels the capture (the daemon skips it and only restores)
|
|
390
|
+
* @returns The image and what was captured
|
|
391
|
+
* @throws IPCCancelledError if `signal` aborts first
|
|
392
|
+
*/
|
|
393
|
+
export async function domScreenshot(params, signal) {
|
|
394
|
+
return sendCommand('dom_screenshot', params, undefined, signal);
|
|
395
|
+
}
|
|
379
396
|
/** Time the client gives `dom wait` beyond its --timeout (the daemon reports the timeout first) */
|
|
380
397
|
const WAIT_IPC_MARGIN_MS = 10_000;
|
|
381
398
|
/**
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { HintDetails } from '../../errors/notices.js';
|
|
8
8
|
import type { AuditCheck, AuditResult, CssSearchResult } from './auditTypes.js';
|
|
9
|
-
import type { ClickResult, FillResult, LayoutResult, ListenersResult, PressKeyResult, RawFormData, ScrollResult, SubmitResult } from './domTypes.js';
|
|
9
|
+
import type { ClickResult, DownloadInfo, FillResult, LayoutResult, ListenersResult, PressKeyResult, RawFormData, ScrollResult, SubmitResult } from './domTypes.js';
|
|
10
10
|
import type { InspectResult } from './inspectTypes.js';
|
|
11
11
|
import type { PageState, SessionActivity } from '../session/types.js';
|
|
12
|
-
import type { ColorScheme, NetworkRequest, ViewportSize } from '../../types.js';
|
|
12
|
+
import type { ColorScheme, NetworkRequest, ScreenshotResult, ViewportSize } from '../../types.js';
|
|
13
13
|
/**
|
|
14
14
|
* Session peek command request schema.
|
|
15
15
|
*/
|
|
@@ -78,6 +78,8 @@ export interface SessionPeekData {
|
|
|
78
78
|
droppedNetwork?: number;
|
|
79
79
|
/** Response bodies evicted at the total body budget (the oldest) */
|
|
80
80
|
evictedNetworkBodies?: number;
|
|
81
|
+
/** Downloads that began during the session, oldest first (left out when none) */
|
|
82
|
+
downloads?: DownloadInfo[];
|
|
81
83
|
/** Whether there are more network items available. */
|
|
82
84
|
hasMoreNetwork?: boolean;
|
|
83
85
|
/** Whether there are more console items available. */
|
|
@@ -378,6 +380,33 @@ export interface DomInspectCommand {
|
|
|
378
380
|
why?: string;
|
|
379
381
|
}
|
|
380
382
|
export type DomInspectData = InspectResult;
|
|
383
|
+
/**
|
|
384
|
+
* dom_screenshot: capture the page, or one element, as an image. The daemon
|
|
385
|
+
* changes the page's emulation for the capture and puts it back before it
|
|
386
|
+
* answers, so an interrupted CLI cannot leave it changed.
|
|
387
|
+
*/
|
|
388
|
+
export interface DomScreenshotCommand {
|
|
389
|
+
format: 'png' | 'jpeg';
|
|
390
|
+
/** JPEG quality (default 90) */
|
|
391
|
+
quality?: number;
|
|
392
|
+
/** Keep the full size instead of scaling down to the token budget */
|
|
393
|
+
noResize?: boolean;
|
|
394
|
+
/** Element to capture (the page when absent) */
|
|
395
|
+
backendNodeId?: number;
|
|
396
|
+
/** Element capture: CSS px of page added around the captured area */
|
|
397
|
+
padding?: number;
|
|
398
|
+
/** Page capture: the whole page (default true) */
|
|
399
|
+
fullPage?: boolean;
|
|
400
|
+
/** Page capture: selector scrolled into view first */
|
|
401
|
+
scroll?: string;
|
|
402
|
+
}
|
|
403
|
+
/** A captured image and what it shows */
|
|
404
|
+
export interface DomScreenshotData {
|
|
405
|
+
/** The image, base64-encoded */
|
|
406
|
+
image: string;
|
|
407
|
+
/** What was captured (all a screenshot reports but the file it is written to) */
|
|
408
|
+
screenshot: Omit<ScreenshotResult, 'path'>;
|
|
409
|
+
}
|
|
381
410
|
/**
|
|
382
411
|
* dom_form_discover: run the form discovery script and return raw form data.
|
|
383
412
|
*/
|
|
@@ -413,6 +442,7 @@ export type RegistryShape = {
|
|
|
413
442
|
dom_audit: CommandDef<DomAuditCommand, DomAuditData>;
|
|
414
443
|
css_search: CommandDef<CssSearchCommand, CssSearchData>;
|
|
415
444
|
dom_inspect: CommandDef<DomInspectCommand, DomInspectData>;
|
|
445
|
+
dom_screenshot: CommandDef<DomScreenshotCommand, DomScreenshotData>;
|
|
416
446
|
dom_wait: CommandDef<DomWaitCommand, DomWaitData>;
|
|
417
447
|
page_navigate: CommandDef<PageNavigateCommand, PageNavigationResult>;
|
|
418
448
|
page_emulate: CommandDef<PageEmulateCommand, PageEmulationResult>;
|
|
@@ -67,6 +67,27 @@ export interface ShownElement {
|
|
|
67
67
|
/** The element, e.g. `div.figcaption` */
|
|
68
68
|
element: string;
|
|
69
69
|
}
|
|
70
|
+
/** State of a download: still running, saved, or stopped before it finished */
|
|
71
|
+
export type DownloadState = 'inProgress' | 'completed' | 'canceled';
|
|
72
|
+
/** A file download a command started */
|
|
73
|
+
export interface DownloadInfo {
|
|
74
|
+
/** URL of the downloaded resource */
|
|
75
|
+
url: string;
|
|
76
|
+
/** File name the page or server suggested */
|
|
77
|
+
suggestedFilename: string;
|
|
78
|
+
/**
|
|
79
|
+
* Absolute path of the file once saved: for a Chrome bdg launched,
|
|
80
|
+
* `<session dir>/downloads/<name>` (`(1)`, `(2)`… added when the name is
|
|
81
|
+
* taken), set while it still runs; for an attached Chrome only when Chrome
|
|
82
|
+
* says where it saved it. Absent for a canceled download
|
|
83
|
+
*/
|
|
84
|
+
path?: string;
|
|
85
|
+
state: DownloadState;
|
|
86
|
+
/** Bytes received so far (all of them once completed) */
|
|
87
|
+
bytes?: number;
|
|
88
|
+
/** Why bdg canceled it, e.g. its downloads directory could not be created */
|
|
89
|
+
reason?: string;
|
|
90
|
+
}
|
|
70
91
|
/** What the page was still working on when an action returned */
|
|
71
92
|
export interface PendingChanges {
|
|
72
93
|
/** Content requests (documents, fetch/XHR, scripts) the action started that were still running */
|
|
@@ -75,7 +96,7 @@ export interface PendingChanges {
|
|
|
75
96
|
navigation?: true;
|
|
76
97
|
/** A loading indicator that appeared during the action and was still shown, e.g. `div#loading` */
|
|
77
98
|
loading?: string;
|
|
78
|
-
/** The DOM was still changing (several bursts of changes, the last one under 150 ms ago) */
|
|
99
|
+
/** The DOM was still changing (several bursts of changes, the last one under 150 ms of quiet time ago) */
|
|
79
100
|
domChanging?: true;
|
|
80
101
|
/** The page did not answer within 250 ms (a long-running script) */
|
|
81
102
|
busy?: true;
|
|
@@ -98,6 +119,8 @@ export interface ActionEffects {
|
|
|
98
119
|
pending?: PendingChanges;
|
|
99
120
|
/** All built-ins the page replaced that bdg's action scripts use (when the warning mentions them) */
|
|
100
121
|
replacedBuiltins?: string[];
|
|
122
|
+
/** Downloads that began during the action (absent when none did) */
|
|
123
|
+
downloads?: DownloadInfo[];
|
|
101
124
|
}
|
|
102
125
|
/** A filled field's value differing from the one given */
|
|
103
126
|
export interface FillValueMismatch {
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import type { IPCMessage } from './lifecycle.js';
|
|
7
7
|
import type { PageState, SessionActivity } from './types.js';
|
|
8
|
+
import type { DownloadInfo } from '../protocol/domTypes.js';
|
|
8
9
|
import type { ColorScheme, NetworkRequest, TelemetryType, ViewportSize } from '../../types.js';
|
|
9
10
|
/**
|
|
10
11
|
* Status request (client → daemon).
|
|
@@ -100,6 +101,8 @@ export interface PeekResponseData {
|
|
|
100
101
|
console: number;
|
|
101
102
|
};
|
|
102
103
|
currentNavigationId?: number;
|
|
104
|
+
/** Downloads that began during the session, oldest first */
|
|
105
|
+
downloads?: DownloadInfo[];
|
|
103
106
|
partial?: boolean;
|
|
104
107
|
};
|
|
105
108
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Common types used across session messages and session commands.
|
|
5
5
|
*/
|
|
6
|
+
import type { DownloadInfo } from '../protocol/domTypes.js';
|
|
6
7
|
import type { ColorScheme, ViewportSize } from '../../types.js';
|
|
7
8
|
/**
|
|
8
9
|
* Session activity metrics.
|
|
@@ -20,6 +21,10 @@ export interface SessionActivity {
|
|
|
20
21
|
lastNetworkRequestAt?: number;
|
|
21
22
|
/** Timestamp of last console message. */
|
|
22
23
|
lastConsoleMessageAt?: number;
|
|
24
|
+
/** Downloads that began during the session, oldest first (left out when none). */
|
|
25
|
+
downloads?: DownloadInfo[];
|
|
26
|
+
/** Why downloads do not go to the session directory (refused, or not redirected), while they do not */
|
|
27
|
+
downloadsWarning?: string;
|
|
23
28
|
}
|
|
24
29
|
/**
|
|
25
30
|
* Current page state.
|
|
@@ -79,4 +79,13 @@ export declare class IPCEarlyCloseError extends IPCError {
|
|
|
79
79
|
readonly requestName: string;
|
|
80
80
|
constructor(requestName: string);
|
|
81
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Error thrown when the client cancels a request (its abort signal fired),
|
|
84
|
+
* closing the connection before the response arrived.
|
|
85
|
+
*/
|
|
86
|
+
export declare class IPCCancelledError extends IPCError {
|
|
87
|
+
readonly name = "IPCCancelledError";
|
|
88
|
+
readonly requestName: string;
|
|
89
|
+
constructor(requestName: string);
|
|
90
|
+
}
|
|
82
91
|
//# sourceMappingURL=IPCError.d.ts.map
|
|
@@ -106,4 +106,16 @@ export class IPCEarlyCloseError extends IPCError {
|
|
|
106
106
|
this.requestName = requestName;
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Error thrown when the client cancels a request (its abort signal fired),
|
|
111
|
+
* closing the connection before the response arrived.
|
|
112
|
+
*/
|
|
113
|
+
export class IPCCancelledError extends IPCError {
|
|
114
|
+
name = 'IPCCancelledError';
|
|
115
|
+
requestName;
|
|
116
|
+
constructor(requestName) {
|
|
117
|
+
super(`${requestName} request cancelled`, EXIT_CODES.SOFTWARE_ERROR);
|
|
118
|
+
this.requestName = requestName;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
109
121
|
//# sourceMappingURL=IPCError.js.map
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Formats transport-layer errors with context using structured error classes.
|
|
5
5
|
*/
|
|
6
|
-
import { IPCConnectionError, IPCParseError, IPCTimeoutError, IPCEarlyCloseError } from './IPCError.js';
|
|
6
|
+
import { IPCCancelledError, IPCConnectionError, IPCParseError, IPCTimeoutError, IPCEarlyCloseError } from './IPCError.js';
|
|
7
7
|
export declare function formatConnectionError(requestName: string, socketPath: string, error: Error): IPCConnectionError;
|
|
8
8
|
export declare function formatParseError(requestName: string, error: unknown): IPCParseError;
|
|
9
9
|
export declare function formatTimeoutError(requestName: string, timeoutMs: number): IPCTimeoutError;
|
|
10
10
|
export declare function formatEarlyCloseError(requestName: string): IPCEarlyCloseError;
|
|
11
|
+
export declare function formatCancelledError(requestName: string): IPCCancelledError;
|
|
11
12
|
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Formats transport-layer errors with context using structured error classes.
|
|
5
5
|
*/
|
|
6
6
|
import { getErrorMessage } from '../../utils/errors.js';
|
|
7
|
-
import { IPCConnectionError, IPCParseError, IPCTimeoutError, IPCEarlyCloseError, } from './IPCError.js';
|
|
7
|
+
import { IPCCancelledError, IPCConnectionError, IPCParseError, IPCTimeoutError, IPCEarlyCloseError, } from './IPCError.js';
|
|
8
8
|
export function formatConnectionError(requestName, socketPath, error) {
|
|
9
9
|
const code = error.code;
|
|
10
10
|
const message = [
|
|
@@ -25,4 +25,7 @@ export function formatTimeoutError(requestName, timeoutMs) {
|
|
|
25
25
|
export function formatEarlyCloseError(requestName) {
|
|
26
26
|
return new IPCEarlyCloseError(requestName);
|
|
27
27
|
}
|
|
28
|
+
export function formatCancelledError(requestName) {
|
|
29
|
+
return new IPCCancelledError(requestName);
|
|
30
|
+
}
|
|
28
31
|
//# sourceMappingURL=errors.js.map
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Handles Unix domain socket communication with JSONL protocol.
|
|
5
5
|
*/
|
|
6
|
-
export { IPCError, IPCConnectionError, IPCTimeoutError, IPCParseError, IPCEarlyCloseError, } from './IPCError.js';
|
|
6
|
+
export { IPCError, IPCCancelledError, IPCConnectionError, IPCTimeoutError, IPCParseError, IPCEarlyCloseError, } from './IPCError.js';
|
|
7
7
|
type WithTypeAndSession = {
|
|
8
8
|
type: string;
|
|
9
9
|
sessionId: string;
|
|
@@ -17,6 +17,8 @@ type WithTypeAndSession = {
|
|
|
17
17
|
* @param expectedType - Response type to validate, if any
|
|
18
18
|
* @param timeoutMs - How long to wait for the response (default: IPC timeout)
|
|
19
19
|
* @param socketPath - Daemon socket (default: the selected session's)
|
|
20
|
+
* @param signal - Closes the connection and rejects when aborted (the daemon
|
|
21
|
+
* sees the client disconnect, e.g. an interrupted start is cancelled)
|
|
20
22
|
* @returns The daemon's response
|
|
21
23
|
* @throws CommandError (103) before connecting when the socket's session
|
|
22
24
|
* directory cannot be trusted (see {@link secureSessionDir}): a socket
|
|
@@ -25,5 +27,5 @@ type WithTypeAndSession = {
|
|
|
25
27
|
* needs write access to a directory of the chain, which the check has
|
|
26
28
|
* just found only the user has
|
|
27
29
|
*/
|
|
28
|
-
export declare function sendRequest<TRequest extends WithTypeAndSession, TResponse extends WithTypeAndSession>(request: TRequest, requestName: string, expectedType?: string, timeoutMs?: number, socketPath?: string): Promise<TResponse>;
|
|
30
|
+
export declare function sendRequest<TRequest extends WithTypeAndSession, TResponse extends WithTypeAndSession>(request: TRequest, requestName: string, expectedType?: string, timeoutMs?: number, socketPath?: string, signal?: AbortSignal): Promise<TResponse>;
|
|
29
31
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -10,11 +10,11 @@ import { untrustedSessionDirError } from '../../errors/messages.js';
|
|
|
10
10
|
import { getDaemonSocketPath, secureSessionDir } from '../../session/paths.js';
|
|
11
11
|
import { createLogger } from '../../ui/logging/index.js';
|
|
12
12
|
import { EXIT_CODES } from '../../utils/exitCodes.js';
|
|
13
|
-
import { formatConnectionError, formatEarlyCloseError, formatParseError, formatTimeoutError, } from './errors.js';
|
|
13
|
+
import { formatCancelledError, formatConnectionError, formatEarlyCloseError, formatParseError, formatTimeoutError, } from './errors.js';
|
|
14
14
|
import { JSONLBuffer, parseJSONLFrame, toJSONLFrame } from './jsonl.js';
|
|
15
15
|
import { createSocket } from './socket.js';
|
|
16
16
|
import { validateResponseType, validateSessionId } from './validation.js';
|
|
17
|
-
export { IPCError, IPCConnectionError, IPCTimeoutError, IPCParseError, IPCEarlyCloseError, } from './IPCError.js';
|
|
17
|
+
export { IPCError, IPCCancelledError, IPCConnectionError, IPCTimeoutError, IPCParseError, IPCEarlyCloseError, } from './IPCError.js';
|
|
18
18
|
const log = createLogger('client');
|
|
19
19
|
/**
|
|
20
20
|
* Send IPC request and wait for response.
|
|
@@ -25,6 +25,8 @@ const log = createLogger('client');
|
|
|
25
25
|
* @param expectedType - Response type to validate, if any
|
|
26
26
|
* @param timeoutMs - How long to wait for the response (default: IPC timeout)
|
|
27
27
|
* @param socketPath - Daemon socket (default: the selected session's)
|
|
28
|
+
* @param signal - Closes the connection and rejects when aborted (the daemon
|
|
29
|
+
* sees the client disconnect, e.g. an interrupted start is cancelled)
|
|
28
30
|
* @returns The daemon's response
|
|
29
31
|
* @throws CommandError (103) before connecting when the socket's session
|
|
30
32
|
* directory cannot be trusted (see {@link secureSessionDir}): a socket
|
|
@@ -33,19 +35,25 @@ const log = createLogger('client');
|
|
|
33
35
|
* needs write access to a directory of the chain, which the check has
|
|
34
36
|
* just found only the user has
|
|
35
37
|
*/
|
|
36
|
-
export async function sendRequest(request, requestName, expectedType, timeoutMs = getIPCRequestTimeout(), socketPath = getDaemonSocketPath()) {
|
|
38
|
+
export async function sendRequest(request, requestName, expectedType, timeoutMs = getIPCRequestTimeout(), socketPath = getDaemonSocketPath(), signal) {
|
|
37
39
|
const untrusted = secureSessionDir(path.dirname(socketPath));
|
|
38
40
|
if (untrusted) {
|
|
39
41
|
const err = untrustedSessionDirError(untrusted);
|
|
40
42
|
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.SESSION_FILE_ERROR);
|
|
41
43
|
}
|
|
42
44
|
return new Promise((resolve, reject) => {
|
|
45
|
+
if (signal?.aborted) {
|
|
46
|
+
reject(formatCancelledError(requestName));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
43
49
|
const buffer = new JSONLBuffer();
|
|
44
50
|
let resolved = false;
|
|
51
|
+
let onAbort = () => { };
|
|
45
52
|
const resolveOnce = (cleanup, error, response) => {
|
|
46
53
|
if (resolved)
|
|
47
54
|
return;
|
|
48
55
|
resolved = true;
|
|
56
|
+
signal?.removeEventListener('abort', onAbort);
|
|
49
57
|
cleanup();
|
|
50
58
|
if (error) {
|
|
51
59
|
reject(error);
|
|
@@ -91,6 +99,8 @@ export async function sendRequest(request, requestName, expectedType, timeoutMs
|
|
|
91
99
|
resolveOnce(cleanup, formatTimeoutError(requestName, timeoutMs));
|
|
92
100
|
},
|
|
93
101
|
});
|
|
102
|
+
onAbort = () => resolveOnce(cleanup, formatCancelledError(requestName));
|
|
103
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
94
104
|
});
|
|
95
105
|
}
|
|
96
106
|
//# sourceMappingURL=index.js.map
|
|
@@ -5,9 +5,13 @@
|
|
|
5
5
|
* working on the result. Costs one page script sent before the action (not
|
|
6
6
|
* waited for: CDP runs it before the action's own scripts) and one read
|
|
7
7
|
* after it, plus a second look 300 ms later when nothing seemed to happen.
|
|
8
|
+
* While watching, a timer in bdg's world notes the stalls during which the
|
|
9
|
+
* page's tasks could not run ({@link STALL_WATCH_START_SCRIPT}); they do not
|
|
10
|
+
* count as quiet time when deciding whether the DOM kept changing.
|
|
8
11
|
* Worst case, when the page does not answer (a navigation is pending, or a
|
|
9
12
|
* long script runs), the snapshot is given up after {@link START_TIMEOUT_MS}
|
|
10
|
-
* and each read after {@link READ_TIMEOUT_MS}
|
|
13
|
+
* and each read after {@link READ_TIMEOUT_MS}, plus as long again, once per
|
|
14
|
+
* action, to ask whether the page ran a long task ({@link pageAnswer}).
|
|
11
15
|
*/
|
|
12
16
|
import type { CDPConnection } from '../../connection/cdp.js';
|
|
13
17
|
import type { ActionEffects, NewMessage, PageNavigation, PendingChanges, ShownElement, TriggeredRequest } from '../../ipc/protocol/domTypes.js';
|
|
@@ -20,10 +24,20 @@ export interface SeenMessage {
|
|
|
20
24
|
element: string;
|
|
21
25
|
}
|
|
22
26
|
export type { NavigationEvents };
|
|
27
|
+
/**
|
|
28
|
+
* A stall: a stretch during which the page's tasks could not run (a long
|
|
29
|
+
* task, or a renderer running the page's tasks late), as how long ago it
|
|
30
|
+
* began and ended at a read (ms, `[began, ended]`)
|
|
31
|
+
*/
|
|
32
|
+
export type StallAges = [number, number];
|
|
23
33
|
/** Signs that the page was still working at a read (same document only) */
|
|
24
34
|
export interface SettleSignals {
|
|
35
|
+
/** Page time of the read (ms, `performance.now()`) */
|
|
36
|
+
at?: number;
|
|
25
37
|
/** How long ago each recent burst of structural DOM changes was (ms, newest last) */
|
|
26
38
|
burstAges: number[];
|
|
39
|
+
/** Stalls up to the read, oldest first (when its bursts made them matter) */
|
|
40
|
+
stalls?: StallAges[];
|
|
27
41
|
/** A loading indicator shown since the action began, described */
|
|
28
42
|
loading: string | null;
|
|
29
43
|
}
|
|
@@ -97,21 +111,43 @@ export declare function newMessages(before: SeenMessage[], after: SeenMessage[],
|
|
|
97
111
|
* @returns Elements to report
|
|
98
112
|
*/
|
|
99
113
|
export declare function shownElements(shown: ShownElement[], messages: NewMessage[]): ShownElement[];
|
|
114
|
+
/**
|
|
115
|
+
* How long the page was quiet between two moments of a read: the time
|
|
116
|
+
* between them less the stalls in it, during which the page's tasks could
|
|
117
|
+
* not run, so it could not change the DOM either.
|
|
118
|
+
*
|
|
119
|
+
* @param fromAge - Earlier moment, as an age at the read (ms)
|
|
120
|
+
* @param toAge - Later moment, as an age at the read (ms)
|
|
121
|
+
* @param stalls - Stalls of the read (they do not overlap)
|
|
122
|
+
* @returns Quiet time (ms)
|
|
123
|
+
*/
|
|
124
|
+
export declare function quietMs(fromAge: number, toAge: number, stalls?: StallAges[]): number;
|
|
100
125
|
/**
|
|
101
126
|
* Whether a read's DOM looks busy, worth a second look: at least
|
|
102
127
|
* {@link BUSY_BURSTS} bursts of structural changes within
|
|
103
|
-
* {@link BUSY_WINDOW_MS},
|
|
104
|
-
*
|
|
128
|
+
* {@link BUSY_WINDOW_MS}, and quiet for at most {@link BUSY_RECENT_MS} since
|
|
129
|
+
* the last ({@link quietMs}: stalls do not count). A single burst older than
|
|
130
|
+
* that counts when the page stalled for most of the time since, quiet for
|
|
131
|
+
* at most {@link LONE_BURST_QUIET_MS}: on a renderer running the page's
|
|
132
|
+
* timers late, a page's second step may not have come by the first read.
|
|
133
|
+
* Text-only changes (clocks) and style changes (animations) are not bursts.
|
|
105
134
|
*
|
|
106
135
|
* @param settle - Signals of the read
|
|
107
136
|
* @returns True when the DOM may still be changing
|
|
108
137
|
*/
|
|
109
138
|
export declare function domLooksBusy(settle: SettleSignals | undefined): boolean;
|
|
110
139
|
/**
|
|
111
|
-
* Whether the DOM kept changing during the second look: at least
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
140
|
+
* Whether the DOM kept changing during the second look: at least one new
|
|
141
|
+
* burst since the first read, and no quiet gap longer than
|
|
142
|
+
* {@link BUSY_RECENT_MS} from the last burst the first read saw, through the
|
|
143
|
+
* new ones, to the second read. Stalls in a gap, during which the page's
|
|
144
|
+
* tasks could not run, are not quiet ({@link quietMs}): a page whose steps
|
|
145
|
+
* come 250 ms apart around a 200 ms long task, or whose timers a starved
|
|
146
|
+
* renderer runs 150 ms late, keeps changing. A page changing every 140 ms
|
|
147
|
+
* keeps changing; changes more than 150 ms apart while the page could run,
|
|
148
|
+
* a render that ended over 150 ms before the second read and a poller
|
|
149
|
+
* updating every 300 ms do not. A short render whose last commit came within
|
|
150
|
+
* 150 ms of the second read counts as changing.
|
|
115
151
|
*
|
|
116
152
|
* @param settle - Signals of the second read
|
|
117
153
|
* @param sinceMs - Time since the first read
|
|
@@ -182,8 +218,11 @@ export interface ActionEffectsWatch {
|
|
|
182
218
|
}
|
|
183
219
|
/**
|
|
184
220
|
* Start watching an action's effects: listen for main-frame navigations,
|
|
185
|
-
* document statuses, requests and new windows,
|
|
186
|
-
* without waiting for it.
|
|
221
|
+
* document statuses, requests and new windows, send the page snapshot
|
|
222
|
+
* without waiting for it, and start the stall watch in bdg's world. That
|
|
223
|
+
* creates bdg's world for the reads now, while the page is idle (created at
|
|
224
|
+
* the first read, it would wait for a page busy after the action and could
|
|
225
|
+
* leave the read no time to answer).
|
|
187
226
|
*
|
|
188
227
|
* @param cdp - CDP connection
|
|
189
228
|
* @returns Watch to collect from after the action
|