browser-debugger-cli 0.14.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 +3 -2
- 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 +201 -133
- package/dist/commands/cleanup.js +21 -4
- package/dist/commands/dom/eval.d.ts +2 -1
- package/dist/commands/dom/eval.js +6 -21
- package/dist/commands/dom/formInteraction.js +8 -4
- package/dist/commands/dom/helpers/evalResult.d.ts +36 -0
- package/dist/commands/dom/helpers/evalResult.js +59 -0
- 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/helpJson.d.ts +1 -1
- package/dist/commands/helpJson.js +3 -3
- package/dist/commands/helpTopic.js +10 -4
- package/dist/commands/network/har.js +18 -14
- package/dist/commands/optionBehaviors.js +24 -9
- 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 +3 -0
- package/dist/commands/shared/outputFile.d.ts +2 -1
- package/dist/commands/shared/outputFile.js +7 -4
- package/dist/commands/shared/startHelpers.d.ts +26 -3
- package/dist/commands/shared/startHelpers.js +145 -23
- package/dist/commands/status.js +3 -1
- package/dist/commands/stop.js +2 -1
- 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/flagsBuilder.d.ts +46 -0
- package/dist/connection/launcher/flagsBuilder.js +107 -23
- package/dist/connection/launcher.d.ts +35 -2
- package/dist/connection/launcher.js +99 -12
- package/dist/connection/typed-cdp.d.ts +3 -2
- package/dist/constants.d.ts +3 -5
- package/dist/constants.js +3 -5
- 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 +22 -3
- package/dist/daemon/launcher.js +45 -8
- 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 +48 -13
- 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 +18520 -17014
- package/dist/errors/CommandError.d.ts +2 -0
- package/dist/errors/issues.d.ts +1 -1
- package/dist/errors/messages.d.ts +81 -0
- package/dist/errors/messages.js +198 -6
- package/dist/index.js +1446 -1078
- package/dist/ipc/client.d.ts +20 -2
- package/dist/ipc/client.js +32 -6
- package/dist/ipc/protocol/commands.d.ts +36 -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 +10 -2
- package/dist/ipc/transport/index.js +29 -4
- 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 +13 -4
- package/dist/runtime/dom/elementInfo.js +12 -3
- package/dist/runtime/dom/evalHelpers.d.ts +24 -4
- package/dist/runtime/dom/evalHelpers.js +40 -12
- package/dist/runtime/dom/formDiscovery.d.ts +1 -1
- package/dist/runtime/dom/frames.d.ts +2 -1
- package/dist/runtime/dom/frames.js +3 -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/emulation.js +6 -5
- package/dist/runtime/page/screenshot.d.ts +41 -0
- package/dist/runtime/page/screenshot.js +394 -0
- package/dist/runtime/page/userAgent.d.ts +86 -2
- package/dist/runtime/page/userAgent.js +154 -33
- package/dist/session/paths.d.ts +52 -3
- package/dist/session/paths.js +179 -7
- package/dist/session/portClaims.d.ts +0 -8
- package/dist/session/portClaims.js +1 -22
- package/dist/session/sessionList.d.ts +5 -1
- package/dist/session/sessionList.js +5 -1
- package/dist/telemetry/downloads.d.ts +127 -0
- package/dist/telemetry/downloads.js +265 -0
- package/dist/telemetry/har/builder.d.ts +12 -1
- package/dist/telemetry/har/builder.js +32 -9
- package/dist/telemetry/har/sanitize.d.ts +28 -0
- package/dist/telemetry/har/sanitize.js +184 -0
- package/dist/telemetry/har/sanitizeBody.d.ts +78 -0
- package/dist/telemetry/har/sanitizeBody.js +541 -0
- 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/sessions.d.ts +3 -2
- package/dist/ui/formatters/sessions.js +10 -3
- 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 +27 -6
- package/dist/ui/messages/chrome.js +78 -12
- package/dist/ui/messages/commands.d.ts +71 -3
- package/dist/ui/messages/commands.js +98 -3
- package/dist/ui/messages/networkMessages.d.ts +50 -5
- package/dist/ui/messages/networkMessages.js +50 -6
- package/dist/ui/messages/session.d.ts +8 -0
- package/dist/ui/messages/session.js +10 -0
- package/dist/utils/async.d.ts +3 -2
- package/dist/utils/async.js +16 -3
- package/dist/utils/atomicFile.d.ts +2 -1
- package/dist/utils/atomicFile.js +5 -2
- package/dist/utils/directories.d.ts +41 -0
- package/dist/utils/directories.js +48 -0
- 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
|
@@ -11,11 +11,18 @@ export interface SpawnedDaemon {
|
|
|
11
11
|
pid: number | undefined;
|
|
12
12
|
/** Whether it has exited (from the child process's exit event) */
|
|
13
13
|
hasExited: () => boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Tell it to shut down (SIGTERM, unless it has exited): it ends what it
|
|
16
|
+
* hosts and exits. For a start interrupted before its request was sent.
|
|
17
|
+
*/
|
|
18
|
+
stop: () => void;
|
|
14
19
|
}
|
|
15
20
|
/**
|
|
16
21
|
* Ensure a daemon is running, spawning one if needed.
|
|
17
22
|
*
|
|
18
23
|
* @returns The daemon it spawned, or undefined when one was already running
|
|
24
|
+
* @throws SessionDirError if the session directory is unusable or untrusted
|
|
25
|
+
* (checked first, so a socket planted there is never taken for a daemon)
|
|
19
26
|
* @throws DaemonStartupError if the daemon script is missing or the daemon
|
|
20
27
|
* does not accept connections in time
|
|
21
28
|
*/
|
|
@@ -24,9 +31,21 @@ export declare function launchDaemon(): Promise<SpawnedDaemon | undefined>;
|
|
|
24
31
|
* Check that the session directory can hold the daemon's files before
|
|
25
32
|
* spawning it (otherwise the daemon dies and only its log says why).
|
|
26
33
|
*
|
|
27
|
-
* @throws SessionDirError (103) for a file
|
|
28
|
-
* directory (a pseudo-filesystem like `/proc`)
|
|
29
|
-
*
|
|
34
|
+
* @throws SessionDirError (103) for a file, a path that cannot hold a
|
|
35
|
+
* directory (a pseudo-filesystem like `/proc`) or an untrusted directory
|
|
36
|
+
* (see {@link secureSessionDir}, which also tightens the user's own 0755
|
|
37
|
+
* directories to 0700), (81) for a too-long path like a named session's,
|
|
38
|
+
* (82) when not writable
|
|
30
39
|
*/
|
|
31
40
|
export declare function assertUsableSessionDir(): void;
|
|
41
|
+
/**
|
|
42
|
+
* Open the daemon log for the daemon's output, creating it 0600. A symlink in
|
|
43
|
+
* its place is refused, not followed: it would append the log to the file it
|
|
44
|
+
* points to.
|
|
45
|
+
*
|
|
46
|
+
* @param logPath - Daemon log path
|
|
47
|
+
* @returns File descriptor
|
|
48
|
+
* @throws SessionDirError (103) when it cannot be opened (`ELOOP` for a symlink)
|
|
49
|
+
*/
|
|
50
|
+
export declare function openDaemonLog(logPath: string): number;
|
|
32
51
|
//# sourceMappingURL=launcher.d.ts.map
|
package/dist/daemon/launcher.js
CHANGED
|
@@ -10,10 +10,10 @@ import { spawn } from 'child_process';
|
|
|
10
10
|
import fs from 'fs';
|
|
11
11
|
import { join } from 'path';
|
|
12
12
|
import { DaemonStartupError, SessionDirError } from './errors.js';
|
|
13
|
-
import { sessionDirIsFileError, sessionDirNotWritableError, socketPathTooLongError, } from '../errors/messages.js';
|
|
13
|
+
import { daemonLogNotOpenedError, sessionDirIsFileError, sessionDirNotWritableError, socketPathTooLongError, untrustedSessionDirError, } from '../errors/messages.js';
|
|
14
14
|
import { killSessionChromes, readLiveDaemonPid } from '../session/cleanup/staleSession.js';
|
|
15
15
|
import { isDaemonAlive, isDaemonSocketGone } from '../session/daemonSocket.js';
|
|
16
|
-
import { MAX_DAEMON_SOCKET_PATH_BYTES, ensureSessionDir, getSessionDir, getSessionFilePath, } from '../session/paths.js';
|
|
16
|
+
import { MAX_DAEMON_SOCKET_PATH_BYTES, ensureSessionDir, getSessionDir, getSessionFilePath, secureSessionDir, } from '../session/paths.js';
|
|
17
17
|
import { createLogger } from '../ui/logging/index.js';
|
|
18
18
|
import { delay } from '../utils/async.js';
|
|
19
19
|
import { directoryProblem } from '../utils/directories.js';
|
|
@@ -28,10 +28,13 @@ const DAEMON_READY_POLL_MS = 20;
|
|
|
28
28
|
* Ensure a daemon is running, spawning one if needed.
|
|
29
29
|
*
|
|
30
30
|
* @returns The daemon it spawned, or undefined when one was already running
|
|
31
|
+
* @throws SessionDirError if the session directory is unusable or untrusted
|
|
32
|
+
* (checked first, so a socket planted there is never taken for a daemon)
|
|
31
33
|
* @throws DaemonStartupError if the daemon script is missing or the daemon
|
|
32
34
|
* does not accept connections in time
|
|
33
35
|
*/
|
|
34
36
|
export async function launchDaemon() {
|
|
37
|
+
assertUsableSessionDir();
|
|
35
38
|
if (await isDaemonAlive()) {
|
|
36
39
|
log.debug('Daemon already running');
|
|
37
40
|
return undefined;
|
|
@@ -39,11 +42,10 @@ export async function launchDaemon() {
|
|
|
39
42
|
if (!fs.existsSync(DAEMON_SCRIPT_PATH)) {
|
|
40
43
|
throw new DaemonStartupError(`Daemon script not found at ${DAEMON_SCRIPT_PATH}. Did you run 'npm run build'?`, 'DAEMON_SCRIPT_NOT_FOUND');
|
|
41
44
|
}
|
|
42
|
-
assertUsableSessionDir();
|
|
43
45
|
await stopUnreachableDaemon();
|
|
44
46
|
const logPath = join(getSessionDir(), 'daemon.log');
|
|
45
47
|
rotateLog(logPath);
|
|
46
|
-
const logFd =
|
|
48
|
+
const logFd = openDaemonLog(logPath);
|
|
47
49
|
log.debug(`Starting daemon: ${DAEMON_SCRIPT_PATH}`);
|
|
48
50
|
const daemon = spawn(process.execPath, [DAEMON_SCRIPT_PATH], {
|
|
49
51
|
detached: true,
|
|
@@ -56,7 +58,14 @@ export async function launchDaemon() {
|
|
|
56
58
|
});
|
|
57
59
|
daemon.unref();
|
|
58
60
|
await waitForDaemonReady(() => exited);
|
|
59
|
-
return {
|
|
61
|
+
return {
|
|
62
|
+
pid: daemon.pid,
|
|
63
|
+
hasExited: () => exited,
|
|
64
|
+
stop: () => {
|
|
65
|
+
if (!exited)
|
|
66
|
+
daemon.kill('SIGTERM');
|
|
67
|
+
},
|
|
68
|
+
};
|
|
60
69
|
}
|
|
61
70
|
/** How long a daemon that lost its socket gets to end its session before it is killed */
|
|
62
71
|
const UNREACHABLE_DAEMON_EXIT_MS = 8000;
|
|
@@ -97,9 +106,11 @@ async function stopUnreachableDaemon() {
|
|
|
97
106
|
* Check that the session directory can hold the daemon's files before
|
|
98
107
|
* spawning it (otherwise the daemon dies and only its log says why).
|
|
99
108
|
*
|
|
100
|
-
* @throws SessionDirError (103) for a file
|
|
101
|
-
* directory (a pseudo-filesystem like `/proc`)
|
|
102
|
-
*
|
|
109
|
+
* @throws SessionDirError (103) for a file, a path that cannot hold a
|
|
110
|
+
* directory (a pseudo-filesystem like `/proc`) or an untrusted directory
|
|
111
|
+
* (see {@link secureSessionDir}, which also tightens the user's own 0755
|
|
112
|
+
* directories to 0700), (81) for a too-long path like a named session's,
|
|
113
|
+
* (82) when not writable
|
|
103
114
|
*/
|
|
104
115
|
export function assertUsableSessionDir() {
|
|
105
116
|
const dir = getSessionDir();
|
|
@@ -116,6 +127,9 @@ export function assertUsableSessionDir() {
|
|
|
116
127
|
if (problem) {
|
|
117
128
|
fail(sessionDirNotWritableError(dir, problem.reason), problem.denied ? EXIT_CODES.PERMISSION_DENIED : EXIT_CODES.SESSION_FILE_ERROR);
|
|
118
129
|
}
|
|
130
|
+
const untrusted = secureSessionDir(dir);
|
|
131
|
+
if (untrusted)
|
|
132
|
+
fail(untrustedSessionDirError(untrusted));
|
|
119
133
|
try {
|
|
120
134
|
ensureSessionDir();
|
|
121
135
|
fs.accessSync(dir, fs.constants.W_OK);
|
|
@@ -126,6 +140,29 @@ export function assertUsableSessionDir() {
|
|
|
126
140
|
fail(sessionDirNotWritableError(dir, code ?? getErrorMessage(error)), denied ? EXIT_CODES.PERMISSION_DENIED : EXIT_CODES.SESSION_FILE_ERROR);
|
|
127
141
|
}
|
|
128
142
|
}
|
|
143
|
+
/** Flags opening the daemon log for appending, refusing (not following) a symlink */
|
|
144
|
+
const DAEMON_LOG_FLAGS = fs.constants.O_WRONLY |
|
|
145
|
+
fs.constants.O_APPEND |
|
|
146
|
+
fs.constants.O_CREAT |
|
|
147
|
+
(fs.constants.O_NOFOLLOW ?? 0);
|
|
148
|
+
/**
|
|
149
|
+
* Open the daemon log for the daemon's output, creating it 0600. A symlink in
|
|
150
|
+
* its place is refused, not followed: it would append the log to the file it
|
|
151
|
+
* points to.
|
|
152
|
+
*
|
|
153
|
+
* @param logPath - Daemon log path
|
|
154
|
+
* @returns File descriptor
|
|
155
|
+
* @throws SessionDirError (103) when it cannot be opened (`ELOOP` for a symlink)
|
|
156
|
+
*/
|
|
157
|
+
export function openDaemonLog(logPath) {
|
|
158
|
+
try {
|
|
159
|
+
return fs.openSync(logPath, DAEMON_LOG_FLAGS, 0o600);
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
const err = daemonLogNotOpenedError(logPath, error.code ?? getErrorMessage(error));
|
|
163
|
+
throw new SessionDirError(err.message, err.suggestion);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
129
166
|
/** Size above which the daemon log is rotated when a daemon starts */
|
|
130
167
|
const MAX_LOG_BYTES = 5 * 1024 * 1024;
|
|
131
168
|
/**
|
|
@@ -39,6 +39,7 @@ export declare class Session {
|
|
|
39
39
|
private readonly onEnded;
|
|
40
40
|
private readonly store;
|
|
41
41
|
private readonly registry;
|
|
42
|
+
private readonly captures;
|
|
42
43
|
private readonly notify;
|
|
43
44
|
private chrome;
|
|
44
45
|
private cdp;
|
|
@@ -85,12 +86,15 @@ export declare class Session {
|
|
|
85
86
|
* {@link CRASH_SAFE_CDP} methods); others fail with exit 107 instead of
|
|
86
87
|
* waiting for a page that cannot answer. Commands that may change the page
|
|
87
88
|
* drop `dom inspect`'s kept matched rules ({@link withMatchedStylesReset}).
|
|
89
|
+
* Page commands run once a screenshot running before them has put the
|
|
90
|
+
* page's emulation back ({@link CaptureGate}).
|
|
88
91
|
*
|
|
89
92
|
* @param name - Command name
|
|
90
93
|
* @param params - Command parameters
|
|
94
|
+
* @param abandoned - Aborted when the requesting client disconnects
|
|
91
95
|
* @returns Command result
|
|
92
96
|
*/
|
|
93
|
-
execute<K extends CommandName>(name: K, params: CommandSchemas[K]['requestSchema']): Promise<CommandSchemas[K]['responseSchema']>;
|
|
97
|
+
execute<K extends CommandName>(name: K, params: CommandSchemas[K]['requestSchema'], abandoned?: AbortSignal): Promise<CommandSchemas[K]['responseSchema']>;
|
|
94
98
|
/**
|
|
95
99
|
* Summary of this session for start responses.
|
|
96
100
|
*
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { ChromeLaunchError } from '../../connection/errors.js';
|
|
9
9
|
import { TelemetryStore } from './TelemetryStore.js';
|
|
10
|
+
import { CaptureGate, TELEMETRY_READS } from './captureGate.js';
|
|
10
11
|
import { connectCDP, navigateToTarget } from './cdpSetup.js';
|
|
11
12
|
import { externalChromePort, findPageTarget, setupChromeConnection, } from './chromeConnection.js';
|
|
12
13
|
import { startTelemetryCollectors } from './collectors.js';
|
|
@@ -40,11 +41,7 @@ const PORT_ATTEMPTS = 3;
|
|
|
40
41
|
* send raw CDP. Every other command needs the page and fails at once.
|
|
41
42
|
*/
|
|
42
43
|
const RUN_ON_CRASHED_PAGE = new Set([
|
|
43
|
-
|
|
44
|
-
'session_details',
|
|
45
|
-
'session_status',
|
|
46
|
-
'session_har_data',
|
|
47
|
-
'session_network_headers',
|
|
44
|
+
...TELEMETRY_READS,
|
|
48
45
|
'page_navigate',
|
|
49
46
|
'cdp_call',
|
|
50
47
|
]);
|
|
@@ -83,6 +80,7 @@ export class Session {
|
|
|
83
80
|
get: () => filterDefined({ viewport: this.config.viewport, colorScheme: this.config.colorScheme }),
|
|
84
81
|
set: (emulation) => this.setEmulation(emulation),
|
|
85
82
|
});
|
|
83
|
+
captures = new CaptureGate();
|
|
86
84
|
notify = (notice) => log.info(formatChromeNotice(notice));
|
|
87
85
|
chrome = null;
|
|
88
86
|
cdp = null;
|
|
@@ -155,12 +153,15 @@ export class Session {
|
|
|
155
153
|
* {@link CRASH_SAFE_CDP} methods); others fail with exit 107 instead of
|
|
156
154
|
* waiting for a page that cannot answer. Commands that may change the page
|
|
157
155
|
* drop `dom inspect`'s kept matched rules ({@link withMatchedStylesReset}).
|
|
156
|
+
* Page commands run once a screenshot running before them has put the
|
|
157
|
+
* page's emulation back ({@link CaptureGate}).
|
|
158
158
|
*
|
|
159
159
|
* @param name - Command name
|
|
160
160
|
* @param params - Command parameters
|
|
161
|
+
* @param abandoned - Aborted when the requesting client disconnects
|
|
161
162
|
* @returns Command result
|
|
162
163
|
*/
|
|
163
|
-
execute(name, params) {
|
|
164
|
+
execute(name, params, abandoned) {
|
|
164
165
|
if (!this.cdp || !this.started || this.stopping) {
|
|
165
166
|
return Promise.reject(new Error('No active session'));
|
|
166
167
|
}
|
|
@@ -176,7 +177,7 @@ export class Session {
|
|
|
176
177
|
}
|
|
177
178
|
const handler = this.registry[name];
|
|
178
179
|
const cdp = this.cdp;
|
|
179
|
-
return withMatchedStylesReset(cdp, name, () => handler(cdp, params));
|
|
180
|
+
return this.captures.run(name, () => withMatchedStylesReset(cdp, name, () => handler(cdp, params, abandoned)));
|
|
180
181
|
}
|
|
181
182
|
/**
|
|
182
183
|
* Summary of this session for start responses.
|
|
@@ -315,7 +316,7 @@ export class Session {
|
|
|
315
316
|
this.config = { ...this.config, port: await getSessionPort(explicitPort) };
|
|
316
317
|
this.throwIfStopping();
|
|
317
318
|
try {
|
|
318
|
-
this.chrome = await setupChromeConnection(this.config, this.store, log, this.notify);
|
|
319
|
+
this.chrome = await setupChromeConnection(this.config, this.store, log, this.notify, this.launchAbort.signal);
|
|
319
320
|
return;
|
|
320
321
|
}
|
|
321
322
|
catch (error) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { DialogInfo } from '../../ipc/protocol/domTypes.js';
|
|
2
|
+
import type { TrackedDownload } from '../../telemetry/downloads.js';
|
|
2
3
|
import type { NavigationEvent } from '../../telemetry/navigation.js';
|
|
3
4
|
import type { PendingRequest } from '../../telemetry/network.js';
|
|
4
5
|
import type { NetworkEvictions } from '../../telemetry/networkRetention.js';
|
|
@@ -23,6 +24,10 @@ export declare class TelemetryStore {
|
|
|
23
24
|
readonly websocketConnections: WebSocketConnection[];
|
|
24
25
|
/** JavaScript dialogs accepted during the session */
|
|
25
26
|
readonly dialogs: DialogInfo[];
|
|
27
|
+
/** Downloads that began during the session, oldest first, updated as they progress */
|
|
28
|
+
readonly downloads: TrackedDownload[];
|
|
29
|
+
/** Set while downloads do not go where bdg meant them to (refused, or not redirected) */
|
|
30
|
+
downloadsWarning: string | undefined;
|
|
26
31
|
activeTelemetry: TelemetryType[];
|
|
27
32
|
/** When the page's renderer crashed (epoch ms); undefined while the page is alive */
|
|
28
33
|
pageCrashedAt: number | undefined;
|
|
@@ -20,6 +20,10 @@ export class TelemetryStore {
|
|
|
20
20
|
websocketConnections = [];
|
|
21
21
|
/** JavaScript dialogs accepted during the session */
|
|
22
22
|
dialogs = [];
|
|
23
|
+
/** Downloads that began during the session, oldest first, updated as they progress */
|
|
24
|
+
downloads = [];
|
|
25
|
+
/** Set while downloads do not go where bdg meant them to (refused, or not redirected) */
|
|
26
|
+
downloadsWarning = undefined;
|
|
23
27
|
activeTelemetry = [];
|
|
24
28
|
/** When the page's renderer crashed (epoch ms); undefined while the page is alive */
|
|
25
29
|
pageCrashedAt;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A screenshot changes the page's emulation for its capture (pixel ratio,
|
|
3
|
+
* touch, viewport, scrollbars) and puts it back once the capture ended. Its
|
|
4
|
+
* client may be gone long before that: interrupted (Ctrl-C), killed, or timed
|
|
5
|
+
* out by the daemon's command timeout. Page commands therefore run only once
|
|
6
|
+
* the captures before them are done, restore included, so no command, from
|
|
7
|
+
* any client, sees the capture's emulation. Commands that read only the
|
|
8
|
+
* telemetry run at once. The wait counts toward the command's own timeout
|
|
9
|
+
* (30 s), like any time the command takes.
|
|
10
|
+
*/
|
|
11
|
+
import type { CommandName } from '../../ipc/index.js';
|
|
12
|
+
/** Commands that read only what the session collected, never the page */
|
|
13
|
+
export declare const TELEMETRY_READS: readonly CommandName[];
|
|
14
|
+
/**
|
|
15
|
+
* How long a page command waits for a capture before it runs anyway. A
|
|
16
|
+
* capture of a very tall page takes a few seconds; one that never ends (CDP
|
|
17
|
+
* calls have no timeout) must not stall the session. A capture waits for the
|
|
18
|
+
* one before it without this limit (bounded by its command timeout): run
|
|
19
|
+
* during it, it would record that capture's emulation as the page's own and
|
|
20
|
+
* put that back.
|
|
21
|
+
*/
|
|
22
|
+
export declare const CAPTURE_WAIT_MS = 15000;
|
|
23
|
+
/**
|
|
24
|
+
* Orders page commands after the captures before them.
|
|
25
|
+
*/
|
|
26
|
+
export declare class CaptureGate {
|
|
27
|
+
private readonly waitMs;
|
|
28
|
+
private capture;
|
|
29
|
+
/**
|
|
30
|
+
* @param waitMs - How long a command waits for a capture at most
|
|
31
|
+
*/
|
|
32
|
+
constructor(waitMs?: number);
|
|
33
|
+
/**
|
|
34
|
+
* Run a command: at once when it reads only the telemetry, else once the
|
|
35
|
+
* capture running before it has ended (or, unless it is a capture itself,
|
|
36
|
+
* {@link waitMs} passed). A
|
|
37
|
+
* capture is waited for by the commands after it until it ends, however it
|
|
38
|
+
* ends.
|
|
39
|
+
*
|
|
40
|
+
* @param name - Command name
|
|
41
|
+
* @param command - The command
|
|
42
|
+
* @returns Its result
|
|
43
|
+
*/
|
|
44
|
+
run<T>(name: CommandName, command: () => Promise<T>): Promise<T>;
|
|
45
|
+
/**
|
|
46
|
+
* Wait for the running capture, if any: until it ends for a capture, else
|
|
47
|
+
* at most {@link waitMs}.
|
|
48
|
+
*
|
|
49
|
+
* @param isCapture - Whether the waiting command is a capture
|
|
50
|
+
*/
|
|
51
|
+
private captureEnded;
|
|
52
|
+
/**
|
|
53
|
+
* Make a capture the one later commands wait for.
|
|
54
|
+
*
|
|
55
|
+
* @param result - The capture's result
|
|
56
|
+
*/
|
|
57
|
+
private track;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=captureGate.d.ts.map
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A screenshot changes the page's emulation for its capture (pixel ratio,
|
|
3
|
+
* touch, viewport, scrollbars) and puts it back once the capture ended. Its
|
|
4
|
+
* client may be gone long before that: interrupted (Ctrl-C), killed, or timed
|
|
5
|
+
* out by the daemon's command timeout. Page commands therefore run only once
|
|
6
|
+
* the captures before them are done, restore included, so no command, from
|
|
7
|
+
* any client, sees the capture's emulation. Commands that read only the
|
|
8
|
+
* telemetry run at once. The wait counts toward the command's own timeout
|
|
9
|
+
* (30 s), like any time the command takes.
|
|
10
|
+
*/
|
|
11
|
+
import { settledWithin } from '../../runtime/dom/evalHelpers.js';
|
|
12
|
+
import { createLogger } from '../../ui/logging/index.js';
|
|
13
|
+
const log = createLogger('session');
|
|
14
|
+
/** Commands that read only what the session collected, never the page */
|
|
15
|
+
export const TELEMETRY_READS = [
|
|
16
|
+
'session_peek',
|
|
17
|
+
'session_details',
|
|
18
|
+
'session_status',
|
|
19
|
+
'session_har_data',
|
|
20
|
+
'session_network_headers',
|
|
21
|
+
];
|
|
22
|
+
const IGNORES_CAPTURE = new Set(TELEMETRY_READS);
|
|
23
|
+
/**
|
|
24
|
+
* How long a page command waits for a capture before it runs anyway. A
|
|
25
|
+
* capture of a very tall page takes a few seconds; one that never ends (CDP
|
|
26
|
+
* calls have no timeout) must not stall the session. A capture waits for the
|
|
27
|
+
* one before it without this limit (bounded by its command timeout): run
|
|
28
|
+
* during it, it would record that capture's emulation as the page's own and
|
|
29
|
+
* put that back.
|
|
30
|
+
*/
|
|
31
|
+
export const CAPTURE_WAIT_MS = 15_000;
|
|
32
|
+
/** Command that changes the page's emulation until it ends */
|
|
33
|
+
const CAPTURE_COMMAND = 'dom_screenshot';
|
|
34
|
+
/**
|
|
35
|
+
* Orders page commands after the captures before them.
|
|
36
|
+
*/
|
|
37
|
+
export class CaptureGate {
|
|
38
|
+
waitMs;
|
|
39
|
+
capture;
|
|
40
|
+
/**
|
|
41
|
+
* @param waitMs - How long a command waits for a capture at most
|
|
42
|
+
*/
|
|
43
|
+
constructor(waitMs = CAPTURE_WAIT_MS) {
|
|
44
|
+
this.waitMs = waitMs;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Run a command: at once when it reads only the telemetry, else once the
|
|
48
|
+
* capture running before it has ended (or, unless it is a capture itself,
|
|
49
|
+
* {@link waitMs} passed). A
|
|
50
|
+
* capture is waited for by the commands after it until it ends, however it
|
|
51
|
+
* ends.
|
|
52
|
+
*
|
|
53
|
+
* @param name - Command name
|
|
54
|
+
* @param command - The command
|
|
55
|
+
* @returns Its result
|
|
56
|
+
*/
|
|
57
|
+
run(name, command) {
|
|
58
|
+
if (IGNORES_CAPTURE.has(name))
|
|
59
|
+
return command();
|
|
60
|
+
const isCapture = name === CAPTURE_COMMAND;
|
|
61
|
+
const result = this.captureEnded(isCapture).then(command);
|
|
62
|
+
if (isCapture)
|
|
63
|
+
this.track(result);
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Wait for the running capture, if any: until it ends for a capture, else
|
|
68
|
+
* at most {@link waitMs}.
|
|
69
|
+
*
|
|
70
|
+
* @param isCapture - Whether the waiting command is a capture
|
|
71
|
+
*/
|
|
72
|
+
async captureEnded(isCapture) {
|
|
73
|
+
const capture = this.capture;
|
|
74
|
+
if (!capture)
|
|
75
|
+
return;
|
|
76
|
+
if (isCapture)
|
|
77
|
+
return capture;
|
|
78
|
+
const ended = await settledWithin(capture, this.waitMs);
|
|
79
|
+
if (!ended.settled)
|
|
80
|
+
log.info(`Capture still running after ${this.waitMs} ms; not waiting`);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Make a capture the one later commands wait for.
|
|
84
|
+
*
|
|
85
|
+
* @param result - The capture's result
|
|
86
|
+
*/
|
|
87
|
+
track(result) {
|
|
88
|
+
const ended = result.then(() => undefined, () => undefined);
|
|
89
|
+
this.capture = ended;
|
|
90
|
+
void ended.then(() => {
|
|
91
|
+
if (this.capture === ended)
|
|
92
|
+
this.capture = undefined;
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=captureGate.js.map
|
|
@@ -12,9 +12,24 @@ import type { Logger } from '../../ui/logging/index.js';
|
|
|
12
12
|
/**
|
|
13
13
|
* Setup Chrome connection - either launch new instance or connect to existing.
|
|
14
14
|
*
|
|
15
|
+
* @param config - Session configuration
|
|
16
|
+
* @param telemetryStore - Store receiving the target info
|
|
17
|
+
* @param log - Logger
|
|
18
|
+
* @param notify - Receives notices about an external Chrome
|
|
19
|
+
* @param signal - Ends a launch at once when aborted (the session was stopped)
|
|
15
20
|
* @returns Launched Chrome instance (null if connecting to external Chrome)
|
|
16
21
|
*/
|
|
17
|
-
export declare function setupChromeConnection(config: SessionConfig, telemetryStore: TelemetryStore, log: Logger, notify: NoticeSink<ChromeNoticeCode
|
|
22
|
+
export declare function setupChromeConnection(config: SessionConfig, telemetryStore: TelemetryStore, log: Logger, notify: NoticeSink<ChromeNoticeCode>, signal?: AbortSignal): Promise<LaunchedChrome | null>;
|
|
23
|
+
/**
|
|
24
|
+
* Browser-level DevTools WebSocket URL of the session's Chrome: a launched
|
|
25
|
+
* Chrome's, or for `--chrome-ws-url` the URL itself when it is browser-level,
|
|
26
|
+
* else the one Chrome reports, reached the way the user's URL is.
|
|
27
|
+
*
|
|
28
|
+
* @param config - Session configuration (port resolved)
|
|
29
|
+
* @param log - Logger
|
|
30
|
+
* @returns The URL, or null when Chrome does not answer `/json/version`
|
|
31
|
+
*/
|
|
32
|
+
export declare function browserWebSocketUrl(config: SessionConfig, log: Logger): Promise<string | null>;
|
|
18
33
|
/**
|
|
19
34
|
* Debugging port of an external Chrome, taken from its WebSocket URL.
|
|
20
35
|
*
|
|
@@ -14,23 +14,47 @@ import { writeChromePid } from '../../session/chrome.js';
|
|
|
14
14
|
import { findConflictingOwner } from '../../session/chromeOwners.js';
|
|
15
15
|
import { getSessionDir } from '../../session/paths.js';
|
|
16
16
|
import { EXIT_CODES } from '../../utils/exitCodes.js';
|
|
17
|
-
import { createPageTarget, fetchCDPTargets, probeDevToolsEndpoint } from '../../utils/http.js';
|
|
17
|
+
import { createPageTarget, fetchBrowserWsUrl, fetchCDPTargets, probeDevToolsEndpoint, } from '../../utils/http.js';
|
|
18
18
|
import { filterDefined } from '../../utils/objects.js';
|
|
19
19
|
/**
|
|
20
20
|
* Setup Chrome connection - either launch new instance or connect to existing.
|
|
21
21
|
*
|
|
22
|
+
* @param config - Session configuration
|
|
23
|
+
* @param telemetryStore - Store receiving the target info
|
|
24
|
+
* @param log - Logger
|
|
25
|
+
* @param notify - Receives notices about an external Chrome
|
|
26
|
+
* @param signal - Ends a launch at once when aborted (the session was stopped)
|
|
22
27
|
* @returns Launched Chrome instance (null if connecting to external Chrome)
|
|
23
28
|
*/
|
|
24
|
-
export async function setupChromeConnection(config, telemetryStore, log, notify) {
|
|
29
|
+
export async function setupChromeConnection(config, telemetryStore, log, notify, signal) {
|
|
25
30
|
if (config.chromeWsUrl) {
|
|
26
31
|
return setupExternalChrome(config, telemetryStore, log, notify);
|
|
27
32
|
}
|
|
28
33
|
else {
|
|
29
|
-
return setupLaunchedChrome(config, log);
|
|
34
|
+
return setupLaunchedChrome(config, log, signal);
|
|
30
35
|
}
|
|
31
36
|
}
|
|
32
37
|
/** Path of browser-level DevTools WebSocket URLs (`/devtools/browser/<uuid>`) */
|
|
33
38
|
const BROWSER_WS_PATH = '/devtools/browser/';
|
|
39
|
+
/**
|
|
40
|
+
* Browser-level DevTools WebSocket URL of the session's Chrome: a launched
|
|
41
|
+
* Chrome's, or for `--chrome-ws-url` the URL itself when it is browser-level,
|
|
42
|
+
* else the one Chrome reports, reached the way the user's URL is.
|
|
43
|
+
*
|
|
44
|
+
* @param config - Session configuration (port resolved)
|
|
45
|
+
* @param log - Logger
|
|
46
|
+
* @returns The URL, or null when Chrome does not answer `/json/version`
|
|
47
|
+
*/
|
|
48
|
+
export async function browserWebSocketUrl(config, log) {
|
|
49
|
+
if (!config.chromeWsUrl)
|
|
50
|
+
return fetchBrowserWsUrl(config.port, log);
|
|
51
|
+
const { hostname, pathname, protocol, port } = new URL(config.chromeWsUrl);
|
|
52
|
+
if (pathname.startsWith(BROWSER_WS_PATH))
|
|
53
|
+
return config.chromeWsUrl;
|
|
54
|
+
const http = { host: hostname, secure: protocol === 'wss:' };
|
|
55
|
+
const reported = await fetchBrowserWsUrl(config.port, log, http);
|
|
56
|
+
return reported && withEndpoint(reported, protocol, hostname, port);
|
|
57
|
+
}
|
|
34
58
|
/**
|
|
35
59
|
* Debugging port of an external Chrome, taken from its WebSocket URL.
|
|
36
60
|
*
|
|
@@ -162,12 +186,18 @@ export function windowSizeFlags(config) {
|
|
|
162
186
|
}
|
|
163
187
|
/**
|
|
164
188
|
* Launch a new Chrome instance and record its PID for crash cleanup.
|
|
189
|
+
*
|
|
190
|
+
* @param config - Session configuration
|
|
191
|
+
* @param log - Logger
|
|
192
|
+
* @param signal - Ends the launch at once when aborted
|
|
193
|
+
* @returns Launched Chrome
|
|
165
194
|
*/
|
|
166
|
-
async function setupLaunchedChrome(config, log) {
|
|
195
|
+
async function setupLaunchedChrome(config, log, signal) {
|
|
167
196
|
const chrome = await launchChrome({
|
|
168
197
|
port: config.port,
|
|
169
198
|
logger: log,
|
|
170
199
|
sessionDir: getSessionDir(),
|
|
200
|
+
signal,
|
|
171
201
|
...filterDefined({
|
|
172
202
|
userDataDir: config.userDataDir,
|
|
173
203
|
headless: config.headless,
|
|
@@ -4,5 +4,20 @@ import type { SessionConfig } from './types.js';
|
|
|
4
4
|
import type { CDPConnection } from '../../connection/cdp.js';
|
|
5
5
|
import type { CleanupFunction } from '../../types.js';
|
|
6
6
|
import type { Logger } from '../../ui/logging/index.js';
|
|
7
|
+
/**
|
|
8
|
+
* Start the session's collectors (telemetry plugins) in order.
|
|
9
|
+
*
|
|
10
|
+
* When one fails to start, the ones already started are cleaned up (their
|
|
11
|
+
* failures logged) before the error is rethrown, so nothing they opened (a
|
|
12
|
+
* browser-level connection, event handlers) outlives the failed start.
|
|
13
|
+
*
|
|
14
|
+
* @param cdp - Page connection
|
|
15
|
+
* @param config - Session configuration
|
|
16
|
+
* @param store - Telemetry store
|
|
17
|
+
* @param logger - Logger
|
|
18
|
+
* @param plugins - Plugins to start (default: the registered ones)
|
|
19
|
+
* @returns Cleanup functions of the started collectors
|
|
20
|
+
* @throws The error of the collector that failed to start
|
|
21
|
+
*/
|
|
7
22
|
export declare function startTelemetryCollectors(cdp: CDPConnection, config: SessionConfig, store: TelemetryStore, logger: Logger, plugins?: TelemetryPlugin[]): Promise<CleanupFunction[]>;
|
|
8
23
|
//# sourceMappingURL=collectors.d.ts.map
|
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
import { sessionActivatingCollector, sessionCollectorsActivated } from '../messages.js';
|
|
2
|
+
import { getErrorMessage } from '../../utils/errors.js';
|
|
2
3
|
import { getRegisteredTelemetryPlugins, shouldActivatePlugin } from './plugins.js';
|
|
3
4
|
const DEFAULT_TELEMETRY = ['network', 'console', 'dom'];
|
|
5
|
+
/**
|
|
6
|
+
* Start the session's collectors (telemetry plugins) in order.
|
|
7
|
+
*
|
|
8
|
+
* When one fails to start, the ones already started are cleaned up (their
|
|
9
|
+
* failures logged) before the error is rethrown, so nothing they opened (a
|
|
10
|
+
* browser-level connection, event handlers) outlives the failed start.
|
|
11
|
+
*
|
|
12
|
+
* @param cdp - Page connection
|
|
13
|
+
* @param config - Session configuration
|
|
14
|
+
* @param store - Telemetry store
|
|
15
|
+
* @param logger - Logger
|
|
16
|
+
* @param plugins - Plugins to start (default: the registered ones)
|
|
17
|
+
* @returns Cleanup functions of the started collectors
|
|
18
|
+
* @throws The error of the collector that failed to start
|
|
19
|
+
*/
|
|
4
20
|
export async function startTelemetryCollectors(cdp, config, store, logger, plugins) {
|
|
5
21
|
const cleanupFunctions = [];
|
|
6
22
|
store.activeTelemetry = config.telemetry ?? DEFAULT_TELEMETRY;
|
|
@@ -10,10 +26,31 @@ export async function startTelemetryCollectors(cdp, config, store, logger, plugi
|
|
|
10
26
|
continue;
|
|
11
27
|
}
|
|
12
28
|
logger.debug(sessionActivatingCollector(plugin.name));
|
|
13
|
-
|
|
14
|
-
|
|
29
|
+
try {
|
|
30
|
+
cleanupFunctions.push(await plugin.start({ cdp, config, store, logger }));
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
await runCleanups(cleanupFunctions, logger);
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
15
36
|
}
|
|
16
37
|
logger.debug(sessionCollectorsActivated(store.activeTelemetry));
|
|
17
38
|
return cleanupFunctions;
|
|
18
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Run cleanup functions, logging (not throwing) their failures.
|
|
42
|
+
*
|
|
43
|
+
* @param cleanups - Cleanup functions
|
|
44
|
+
* @param logger - Logger
|
|
45
|
+
*/
|
|
46
|
+
async function runCleanups(cleanups, logger) {
|
|
47
|
+
for (const cleanup of cleanups) {
|
|
48
|
+
try {
|
|
49
|
+
await cleanup();
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
logger.debug(`Collector cleanup error: ${getErrorMessage(error)}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
19
56
|
//# sourceMappingURL=collectors.js.map
|
|
@@ -1,11 +1,24 @@
|
|
|
1
1
|
import type { TelemetryStore } from './TelemetryStore.js';
|
|
2
2
|
import type { CDPConnection } from '../../connection/cdp.js';
|
|
3
|
+
import { CommandError } from '../../errors/index.js';
|
|
3
4
|
import type { CommandName, CommandSchemas } from '../../ipc/index.js';
|
|
4
5
|
import { type SessionEmulation } from '../../runtime/page/emulation.js';
|
|
5
|
-
|
|
6
|
+
/** A command's handler; `abandoned` aborts when its client disconnects */
|
|
7
|
+
type Handler<K extends CommandName> = (cdp: CDPConnection, params: CommandSchemas[K]['requestSchema'], abandoned?: AbortSignal) => Promise<CommandSchemas[K]['responseSchema']>;
|
|
6
8
|
export type CommandRegistry = {
|
|
7
9
|
[K in CommandName]: Handler<K>;
|
|
8
10
|
};
|
|
11
|
+
/**
|
|
12
|
+
* A `bdg cdp` failure that is the caller's: a method this Chrome doesn't
|
|
13
|
+
* implement (83), wrong parameters (81), or an id of a node, target or frame
|
|
14
|
+
* that does not exist (83). Other failures (internal errors, a detached
|
|
15
|
+
* page) stay software errors.
|
|
16
|
+
*
|
|
17
|
+
* @param method - CDP method
|
|
18
|
+
* @param error - What the call threw
|
|
19
|
+
* @returns The error to report, or undefined to keep the original
|
|
20
|
+
*/
|
|
21
|
+
export declare function callerError(method: string, error: unknown): CommandError | undefined;
|
|
9
22
|
/** The session's page emulation, which `page emulate` reads and changes */
|
|
10
23
|
export interface EmulationState {
|
|
11
24
|
get: () => SessionEmulation;
|