browser-debugger-cli 0.12.0 → 0.14.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 +100 -186
- package/README.md +5 -4
- package/dist/commands/cdp.d.ts +22 -1
- package/dist/commands/cdp.js +100 -43
- package/dist/commands/console.d.ts +12 -0
- package/dist/commands/console.js +67 -13
- package/dist/commands/dom/DomElementResolver.d.ts +3 -1
- package/dist/commands/dom/DomElementResolver.js +10 -3
- package/dist/commands/dom/a11y.d.ts +1 -1
- package/dist/commands/dom/a11y.js +23 -22
- package/dist/commands/dom/eval.d.ts +4 -2
- package/dist/commands/dom/eval.js +31 -7
- package/dist/commands/dom/form.js +10 -9
- package/dist/commands/dom/formInteraction.js +9 -8
- package/dist/commands/dom/get.js +32 -14
- package/dist/commands/dom/helpers/index.d.ts +1 -1
- package/dist/commands/dom/helpers/index.js +1 -1
- package/dist/commands/dom/helpers/query.d.ts +27 -3
- package/dist/commands/dom/helpers/query.js +152 -64
- package/dist/commands/dom/helpers/screenshot.js +13 -13
- package/dist/commands/dom/index.js +10 -3
- package/dist/commands/dom/query.d.ts +20 -2
- package/dist/commands/dom/query.js +39 -6
- package/dist/commands/dom/screenshot.js +3 -1
- package/dist/commands/dom/semanticUtils.d.ts +3 -2
- package/dist/commands/dom/semanticUtils.js +40 -9
- package/dist/commands/helpJson.d.ts +82 -19
- package/dist/commands/helpJson.js +112 -41
- package/dist/commands/helpTopic.d.ts +16 -1
- package/dist/commands/helpTopic.js +59 -1
- package/dist/commands/installSkill.d.ts +15 -5
- package/dist/commands/installSkill.js +86 -16
- package/dist/commands/network/list.js +65 -12
- package/dist/commands/optionBehaviors.d.ts +25 -2
- package/dist/commands/optionBehaviors.js +81 -46
- package/dist/commands/peek.js +3 -0
- package/dist/commands/shared/CommandRunner.js +13 -13
- package/dist/commands/shared/daemonErrorHandler.d.ts +5 -2
- package/dist/commands/shared/daemonErrorHandler.js +21 -10
- package/dist/commands/shared/dataFetcher.d.ts +14 -4
- package/dist/commands/shared/dataFetcher.js +20 -4
- package/dist/commands/shared/followMode.d.ts +9 -1
- package/dist/commands/shared/followMode.js +22 -4
- package/dist/commands/shared/handleValidationError.js +3 -3
- package/dist/commands/shared/optionTypes.d.ts +17 -3
- package/dist/commands/shared/outputFile.js +6 -1
- package/dist/commands/shared/startHelpers.js +3 -3
- package/dist/commands/start.d.ts +7 -5
- package/dist/commands/start.js +65 -21
- package/dist/commands/stop.d.ts +11 -0
- package/dist/commands/stop.js +24 -1
- package/dist/commands.js +1 -1
- package/dist/connection/cdp.d.ts +7 -0
- package/dist/connection/cdp.js +9 -0
- package/dist/connection/chromeIdentity.d.ts +8 -2
- package/dist/connection/chromeIdentity.js +85 -13
- package/dist/connection/launcher.js +3 -2
- package/dist/constants.d.ts +29 -1
- package/dist/constants.js +35 -1
- package/dist/daemon/SessionController.js +8 -1
- package/dist/daemon/launcher.d.ts +3 -2
- package/dist/daemon/launcher.js +47 -3
- package/dist/daemon/session/Session.d.ts +5 -1
- package/dist/daemon/session/Session.js +42 -3
- package/dist/daemon/session/TelemetryStore.d.ts +15 -1
- package/dist/daemon/session/TelemetryStore.js +19 -1
- package/dist/daemon/session/commandRegistry.js +52 -18
- package/dist/daemon/session/interactions.d.ts +2 -1
- package/dist/daemon/session/interactions.js +13 -1
- package/dist/daemon/session/matchedStylesReset.d.ts +26 -0
- package/dist/daemon/session/matchedStylesReset.js +46 -0
- package/dist/daemon/session/plugins.js +17 -2
- package/dist/daemon/session/teardown.js +1 -1
- package/dist/daemon/session/triggeredRequests.d.ts +0 -5
- package/dist/daemon/session/triggeredRequests.js +13 -7
- package/dist/daemon.js +2385 -1229
- package/dist/errors/messages.d.ts +62 -11
- package/dist/errors/messages.js +119 -22
- package/dist/index.js +14995 -9866
- package/dist/ipc/client.d.ts +18 -2
- package/dist/ipc/client.js +26 -5
- package/dist/ipc/protocol/auditTypes.d.ts +8 -2
- package/dist/ipc/protocol/commands.d.ts +16 -0
- package/dist/ipc/protocol/domTypes.d.ts +12 -0
- package/dist/ipc/protocol/inspectTypes.d.ts +7 -2
- package/dist/ipc/session/types.d.ts +7 -1
- package/dist/program.d.ts +14 -0
- package/dist/program.js +53 -0
- package/dist/runtime/dom/actionEffects.d.ts +5 -1
- package/dist/runtime/dom/actionEffects.js +26 -14
- package/dist/runtime/dom/audit.js +3 -2
- package/dist/runtime/dom/auditModel.js +6 -1
- package/dist/runtime/dom/auditScripts.d.ts +9 -3
- package/dist/runtime/dom/auditScripts.js +41 -5
- package/dist/runtime/dom/elementGeometry.d.ts +33 -3
- package/dist/runtime/dom/elementGeometry.js +44 -19
- package/dist/runtime/dom/elementInfo.d.ts +76 -18
- package/dist/runtime/dom/elementInfo.js +190 -40
- package/dist/runtime/dom/evalHelpers.d.ts +12 -2
- package/dist/runtime/dom/evalHelpers.js +67 -7
- package/dist/runtime/dom/formDiscovery.d.ts +6 -2
- package/dist/runtime/dom/formDiscovery.js +20 -3
- package/dist/runtime/dom/formFillHelpers/fill.js +7 -11
- package/dist/runtime/dom/formFillHelpers/pressKey.js +2 -2
- package/dist/runtime/dom/formFillHelpers/shared.d.ts +16 -10
- package/dist/runtime/dom/formFillHelpers/shared.js +19 -52
- package/dist/runtime/dom/formSubmitHelpers.js +4 -3
- package/dist/runtime/dom/frameLayout.js +1 -0
- package/dist/runtime/dom/frameScopedConnection.d.ts +7 -0
- package/dist/runtime/dom/frameScopedConnection.js +2 -2
- package/dist/runtime/dom/inspect.d.ts +17 -3
- package/dist/runtime/dom/inspect.js +45 -32
- package/dist/runtime/dom/inspectAllStyles.js +1 -0
- package/dist/runtime/dom/inspectHints.d.ts +1 -1
- package/dist/runtime/dom/inspectModel.d.ts +5 -4
- package/dist/runtime/dom/inspectModel.js +7 -3
- package/dist/runtime/dom/inspectPaintModel.d.ts +2 -0
- package/dist/runtime/dom/inspectPaintModel.js +3 -1
- package/dist/runtime/dom/inspectRules.d.ts +29 -3
- package/dist/runtime/dom/inspectRules.js +205 -11
- package/dist/runtime/dom/inspectScripts.d.ts +29 -2
- package/dist/runtime/dom/inspectScripts.js +49 -10
- package/dist/runtime/dom/layout.d.ts +0 -2
- package/dist/runtime/dom/layout.js +10 -9
- package/dist/runtime/dom/reactEventHelpers.d.ts +17 -4
- package/dist/runtime/dom/reactEventHelpers.js +71 -28
- package/dist/runtime/dom/targetNode.d.ts +27 -10
- package/dist/runtime/dom/targetNode.js +283 -16
- package/dist/runtime/dom/wait.js +2 -1
- package/dist/runtime/page/bdgWorld.d.ts +57 -0
- package/dist/runtime/page/bdgWorld.js +180 -0
- package/dist/runtime/page/replacedBuiltins.d.ts +28 -0
- package/dist/runtime/page/replacedBuiltins.js +136 -0
- package/dist/session/QueryCacheManager.d.ts +4 -1
- package/dist/session/QueryCacheManager.js +5 -2
- package/dist/session/chrome.d.ts +4 -1
- package/dist/session/chrome.js +7 -1
- package/dist/session/cleanup/staleSession.d.ts +21 -4
- package/dist/session/cleanup/staleSession.js +79 -9
- package/dist/session/cleanup/userCommands.d.ts +4 -1
- package/dist/session/cleanup/userCommands.js +10 -5
- package/dist/session/daemonSocket.d.ts +10 -0
- package/dist/session/daemonSocket.js +22 -0
- package/dist/session/lastSession.d.ts +6 -3
- package/dist/session/lastSession.js +11 -5
- package/dist/session/paths.d.ts +3 -1
- package/dist/session/paths.js +5 -5
- package/dist/session/portClaims.js +4 -3
- package/dist/session/sessionList.d.ts +13 -5
- package/dist/session/sessionList.js +31 -7
- package/dist/telemetry/a11y.d.ts +15 -1
- package/dist/telemetry/a11y.js +85 -2
- package/dist/telemetry/console.d.ts +2 -1
- package/dist/telemetry/console.js +30 -21
- package/dist/telemetry/har/builder.js +1 -1
- package/dist/telemetry/network.d.ts +13 -16
- package/dist/telemetry/network.js +30 -52
- package/dist/telemetry/networkRetention.d.ts +83 -0
- package/dist/telemetry/networkRetention.js +117 -0
- package/dist/telemetry/pageCrash.d.ts +26 -0
- package/dist/telemetry/pageCrash.js +53 -0
- package/dist/types.d.ts +42 -0
- package/dist/ui/OutputBuilder.d.ts +10 -0
- package/dist/ui/OutputBuilder.js +12 -0
- package/dist/ui/formatters/a11y.d.ts +5 -7
- package/dist/ui/formatters/a11y.js +7 -61
- package/dist/ui/formatters/audit.js +14 -5
- package/dist/ui/formatters/cdp.d.ts +138 -0
- package/dist/ui/formatters/cdp.js +131 -0
- package/dist/ui/formatters/console/chronological.js +7 -5
- package/dist/ui/formatters/console/follow.d.ts +5 -2
- package/dist/ui/formatters/console/follow.js +7 -4
- package/dist/ui/formatters/console/json.d.ts +4 -7
- package/dist/ui/formatters/console/json.js +16 -14
- package/dist/ui/formatters/console/shared.d.ts +47 -2
- package/dist/ui/formatters/console/shared.js +33 -0
- package/dist/ui/formatters/console/summarize.d.ts +9 -2
- package/dist/ui/formatters/console/summarize.js +57 -11
- package/dist/ui/formatters/console.d.ts +3 -2
- package/dist/ui/formatters/console.js +8 -10
- package/dist/ui/formatters/details.js +4 -2
- package/dist/ui/formatters/dom.d.ts +14 -5
- package/dist/ui/formatters/dom.js +30 -13
- package/dist/ui/formatters/helpFormatters.js +1 -1
- package/dist/ui/formatters/inspect.js +9 -3
- package/dist/ui/formatters/installSkill.d.ts +9 -1
- package/dist/ui/formatters/installSkill.js +32 -6
- package/dist/ui/formatters/layout.js +4 -2
- package/dist/ui/formatters/longValues.d.ts +14 -0
- package/dist/ui/formatters/longValues.js +23 -0
- package/dist/ui/formatters/networkList.d.ts +8 -2
- package/dist/ui/formatters/networkList.js +11 -3
- package/dist/ui/formatters/preview.d.ts +6 -1
- package/dist/ui/formatters/preview.js +67 -15
- package/dist/ui/formatters/sessions.d.ts +2 -2
- package/dist/ui/formatters/sessions.js +9 -2
- package/dist/ui/formatters/status.js +7 -0
- package/dist/ui/formatters/triggeredRequests.js +2 -1
- package/dist/ui/logging/logger.d.ts +1 -1
- package/dist/ui/messages/chrome.d.ts +20 -1
- package/dist/ui/messages/chrome.js +29 -3
- package/dist/ui/messages/commands.d.ts +153 -12
- package/dist/ui/messages/commands.js +198 -15
- package/dist/ui/messages/consoleMessages.d.ts +24 -0
- package/dist/ui/messages/consoleMessages.js +32 -0
- package/dist/ui/messages/networkMessages.d.ts +24 -0
- package/dist/ui/messages/networkMessages.js +45 -0
- package/dist/ui/messages/preview.d.ts +6 -0
- package/dist/ui/messages/preview.js +9 -1
- package/dist/ui/messages/session.d.ts +13 -2
- package/dist/ui/messages/session.js +22 -3
- package/dist/utils/directories.d.ts +34 -0
- package/dist/utils/directories.js +88 -0
- package/dist/utils/display.d.ts +16 -0
- package/dist/utils/display.js +42 -0
- package/dist/utils/exitCodes.d.ts +1 -0
- package/dist/utils/exitCodes.js +6 -0
- package/dist/utils/http.d.ts +9 -2
- package/dist/utils/http.js +4 -3
- package/dist/utils/process.d.ts +12 -0
- package/dist/utils/process.js +25 -0
- package/dist/utils/strings.d.ts +19 -0
- package/dist/utils/strings.js +16 -0
- package/package.json +2 -2
|
@@ -49,12 +49,18 @@ export declare function waitForDevToolsEndpoint(logs: StartupLogs, isRunning: ()
|
|
|
49
49
|
* Check that the Chrome answering on 127.0.0.1:<port> is the one just
|
|
50
50
|
* launched, so bdg never drives another session's browser.
|
|
51
51
|
*
|
|
52
|
+
* A Chrome that announced 127.0.0.1:<port> but does not answer yet (a slow
|
|
53
|
+
* start, its `/json/version` request timing out) is asked again until the
|
|
54
|
+
* deadline; only an answer from something else is a port conflict. A Chrome
|
|
55
|
+
* that fell back to [::1] is asked once: 127.0.0.1 is held by something else.
|
|
56
|
+
*
|
|
52
57
|
* @param options - Chrome's log positions from before the launch, requested
|
|
53
58
|
* port (free on 127.0.0.1 and ::1 right before the launch), Chrome's PID and
|
|
54
|
-
* longest wait for its announcement
|
|
59
|
+
* longest wait for its announcement and its answer
|
|
55
60
|
* @throws ChromeLaunchError: CHROME_DIED_AFTER_LAUNCH if Chrome exits first;
|
|
56
61
|
* PORT_IN_USE if another browser or process answers on the port;
|
|
57
|
-
* CHROME_LAUNCH_FAILED if Chrome announced nothing and nothing answers
|
|
62
|
+
* CHROME_LAUNCH_FAILED if Chrome announced nothing and nothing answers, or
|
|
63
|
+
* announced the port but did not answer on it in time
|
|
58
64
|
*/
|
|
59
65
|
export declare function verifyLaunchedChrome(options: {
|
|
60
66
|
logs: StartupLogs;
|
|
@@ -18,8 +18,9 @@
|
|
|
18
18
|
* before the launch, and a second listener is how a conflict shows.
|
|
19
19
|
*/
|
|
20
20
|
import { createLogger } from '../ui/logging/index.js';
|
|
21
|
+
import { chromeNotAnsweringReason, portTakenByReason } from '../ui/messages/chrome.js';
|
|
21
22
|
import { delay } from '../utils/async.js';
|
|
22
|
-
import { fetchBrowserWsUrl } from '../utils/http.js';
|
|
23
|
+
import { CDP_HTTP_TIMEOUT_MS, fetchBrowserWsUrl, probeDevToolsEndpoint, } from '../utils/http.js';
|
|
23
24
|
import { isProcessAlive } from '../utils/process.js';
|
|
24
25
|
import { ChromeLaunchError } from './errors.js';
|
|
25
26
|
import { acceptsConnections } from './portReservation.js';
|
|
@@ -29,6 +30,8 @@ const LISTENING_PATTERN = /^DevTools listening on (ws:\/\/\S+)/;
|
|
|
29
30
|
/** How long to wait for Chrome to announce its endpoint after the port answered */
|
|
30
31
|
const ENDPOINT_WAIT_MS = 10000;
|
|
31
32
|
const ENDPOINT_POLL_MS = 50;
|
|
33
|
+
/** Shortest wait for one answer, also when the deadline has passed */
|
|
34
|
+
const MIN_ANSWER_WAIT_MS = 1000;
|
|
32
35
|
const log = createLogger('chrome');
|
|
33
36
|
/**
|
|
34
37
|
* Find the endpoint Chrome announced in its output (the last announcement).
|
|
@@ -75,32 +78,75 @@ export async function waitForDevToolsEndpoint(logs, isRunning, timeoutMs = ENDPO
|
|
|
75
78
|
* Check that the Chrome answering on 127.0.0.1:<port> is the one just
|
|
76
79
|
* launched, so bdg never drives another session's browser.
|
|
77
80
|
*
|
|
81
|
+
* A Chrome that announced 127.0.0.1:<port> but does not answer yet (a slow
|
|
82
|
+
* start, its `/json/version` request timing out) is asked again until the
|
|
83
|
+
* deadline; only an answer from something else is a port conflict. A Chrome
|
|
84
|
+
* that fell back to [::1] is asked once: 127.0.0.1 is held by something else.
|
|
85
|
+
*
|
|
78
86
|
* @param options - Chrome's log positions from before the launch, requested
|
|
79
87
|
* port (free on 127.0.0.1 and ::1 right before the launch), Chrome's PID and
|
|
80
|
-
* longest wait for its announcement
|
|
88
|
+
* longest wait for its announcement and its answer
|
|
81
89
|
* @throws ChromeLaunchError: CHROME_DIED_AFTER_LAUNCH if Chrome exits first;
|
|
82
90
|
* PORT_IN_USE if another browser or process answers on the port;
|
|
83
|
-
* CHROME_LAUNCH_FAILED if Chrome announced nothing and nothing answers
|
|
91
|
+
* CHROME_LAUNCH_FAILED if Chrome announced nothing and nothing answers, or
|
|
92
|
+
* announced the port but did not answer on it in time
|
|
84
93
|
*/
|
|
85
94
|
export async function verifyLaunchedChrome(options) {
|
|
86
|
-
const { logs, port, pid, timeoutMs } = options;
|
|
95
|
+
const { logs, port, pid, timeoutMs = ENDPOINT_WAIT_MS } = options;
|
|
96
|
+
const started = Date.now();
|
|
87
97
|
const isRunning = () => isProcessAlive(pid);
|
|
88
98
|
const endpoint = await waitForDevToolsEndpoint(logs, isRunning, timeoutMs);
|
|
89
|
-
if (!endpoint && !isRunning())
|
|
90
|
-
throw
|
|
91
|
-
issue: { code: 'CHROME_DIED_AFTER_LAUNCH', context: { port, pid } },
|
|
92
|
-
});
|
|
93
|
-
}
|
|
99
|
+
if (!endpoint && !isRunning())
|
|
100
|
+
throw diedError(port, pid);
|
|
94
101
|
if (!endpoint)
|
|
95
102
|
return acceptUnannouncedChrome(logs, port);
|
|
96
103
|
if (endpoint.port !== port) {
|
|
97
104
|
throw portTakenError(port, `Chrome listens on port ${endpoint.port} instead`);
|
|
98
105
|
}
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
106
|
+
const onLoopback = endpoint.host === '127.0.0.1';
|
|
107
|
+
const deadline = onLoopback ? started + timeoutMs : Date.now();
|
|
108
|
+
const answer = await waitForAnswer(port, deadline, isRunning);
|
|
109
|
+
if (answer.kind === 'devtools' && new URL(answer.wsUrl).pathname === endpoint.browserPath) {
|
|
110
|
+
log.debug(`Chrome on port ${port} is the launched one (${endpoint.browserPath})`);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (answer.kind === 'unreachable' && !isRunning())
|
|
114
|
+
throw diedError(port, pid);
|
|
115
|
+
if (answer.kind === 'unreachable' && onLoopback) {
|
|
116
|
+
throw slowStartError(port, Date.now() - started);
|
|
117
|
+
}
|
|
118
|
+
throw portTakenError(port, portTakenByReason(answerSource(answer), endpoint.host));
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Ask 127.0.0.1:<port> for its DevTools version until something answers,
|
|
122
|
+
* Chrome exits or the deadline passes (at least once). Each request waits at
|
|
123
|
+
* most until the deadline (but at least MIN_ANSWER_WAIT_MS).
|
|
124
|
+
*
|
|
125
|
+
* @param port - Requested port
|
|
126
|
+
* @param deadline - Time (ms since epoch) to stop asking
|
|
127
|
+
* @param isRunning - Whether Chrome is still running
|
|
128
|
+
* @returns The first answer, or the last `unreachable` result
|
|
129
|
+
*/
|
|
130
|
+
async function waitForAnswer(port, deadline, isRunning) {
|
|
131
|
+
for (;;) {
|
|
132
|
+
const remaining = Math.max(deadline - Date.now(), MIN_ANSWER_WAIT_MS);
|
|
133
|
+
const timeoutMs = Math.min(CDP_HTTP_TIMEOUT_MS, remaining);
|
|
134
|
+
const answer = await probeDevToolsEndpoint(port, undefined, { timeoutMs });
|
|
135
|
+
if (answer.kind !== 'unreachable' || !isRunning() || Date.now() >= deadline)
|
|
136
|
+
return answer;
|
|
137
|
+
await delay(ENDPOINT_POLL_MS);
|
|
102
138
|
}
|
|
103
|
-
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* What answered on 127.0.0.1:<port> instead of the launched Chrome.
|
|
142
|
+
*
|
|
143
|
+
* @param answer - The answer
|
|
144
|
+
* @returns A browser, another process, or nothing
|
|
145
|
+
*/
|
|
146
|
+
function answerSource(answer) {
|
|
147
|
+
if (answer.kind === 'devtools')
|
|
148
|
+
return 'browser';
|
|
149
|
+
return answer.kind === 'not-devtools' ? 'process' : 'nothing';
|
|
104
150
|
}
|
|
105
151
|
/**
|
|
106
152
|
* Accept a Chrome that did not announce its endpoint, if a browser answers on
|
|
@@ -128,6 +174,32 @@ async function acceptUnannouncedChrome(logs, port) {
|
|
|
128
174
|
}
|
|
129
175
|
log.info(`Warning: ${missing}; using the browser on 127.0.0.1:${port}, which was free before the launch`);
|
|
130
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* The error for a Chrome that exited during the launch checks.
|
|
179
|
+
*
|
|
180
|
+
* @param port - Requested port
|
|
181
|
+
* @param pid - Chrome's PID
|
|
182
|
+
* @returns Launch error with the CHROME_DIED_AFTER_LAUNCH issue
|
|
183
|
+
*/
|
|
184
|
+
function diedError(port, pid) {
|
|
185
|
+
return new ChromeLaunchError(`Chrome died immediately after launch (PID: ${pid})`, {
|
|
186
|
+
issue: { code: 'CHROME_DIED_AFTER_LAUNCH', context: { port, pid } },
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* The error for a Chrome that announced the port but did not answer on it in
|
|
191
|
+
* time.
|
|
192
|
+
*
|
|
193
|
+
* @param port - Requested port
|
|
194
|
+
* @param waitedMs - How long bdg waited
|
|
195
|
+
* @returns Launch error with the CHROME_LAUNCH_FAILED issue
|
|
196
|
+
*/
|
|
197
|
+
function slowStartError(port, waitedMs) {
|
|
198
|
+
const reason = chromeNotAnsweringReason(port, waitedMs);
|
|
199
|
+
return new ChromeLaunchError(reason, {
|
|
200
|
+
issue: { code: 'CHROME_LAUNCH_FAILED', context: { port, reason } },
|
|
201
|
+
});
|
|
202
|
+
}
|
|
131
203
|
/**
|
|
132
204
|
* The error for a port another process answers on.
|
|
133
205
|
*
|
|
@@ -3,6 +3,7 @@ import * as os from 'os';
|
|
|
3
3
|
import * as path from 'path';
|
|
4
4
|
import * as chromeLauncher from 'chrome-launcher';
|
|
5
5
|
import { BDG_CHROME_PREFS, DEFAULT_CDP_PORT, CHROME_PROFILE_DIR, DEFAULT_CHROME_LOG_LEVEL, DEFAULT_CHROME_HANDLE_SIGINT, } from '../constants.js';
|
|
6
|
+
import { makeDirectory } from '../utils/directories.js';
|
|
6
7
|
import { getErrorMessage } from '../utils/errors.js';
|
|
7
8
|
import { filterDefined } from '../utils/objects.js';
|
|
8
9
|
import { isProcessAlive } from '../utils/process.js';
|
|
@@ -62,7 +63,7 @@ export async function launchChrome(options = {}) {
|
|
|
62
63
|
const userDataDir = options.userDataDir ?? getPersistentUserDataDir(options.baseDir ?? options.sessionDir);
|
|
63
64
|
if (!fs.existsSync(userDataDir)) {
|
|
64
65
|
try {
|
|
65
|
-
|
|
66
|
+
makeDirectory(userDataDir);
|
|
66
67
|
}
|
|
67
68
|
catch (error) {
|
|
68
69
|
throw new ChromeLaunchError(`Failed to create user data directory`, {
|
|
@@ -160,7 +161,7 @@ function getPersistentUserDataDir(baseDir) {
|
|
|
160
161
|
const userDataDir = path.join(dir, CHROME_PROFILE_DIR);
|
|
161
162
|
if (!fs.existsSync(userDataDir)) {
|
|
162
163
|
try {
|
|
163
|
-
|
|
164
|
+
makeDirectory(userDataDir);
|
|
164
165
|
}
|
|
165
166
|
catch (error) {
|
|
166
167
|
throw new ChromeLaunchError(`Failed to create user data directory`, {
|
package/dist/constants.d.ts
CHANGED
|
@@ -58,7 +58,8 @@ export declare const DOCKER_CHROME_FLAGS: string[];
|
|
|
58
58
|
*/
|
|
59
59
|
export declare const BDG_CHROME_PREFS: Record<string, unknown>;
|
|
60
60
|
/**
|
|
61
|
-
*
|
|
61
|
+
* Finished network requests kept: past this the oldest are dropped, so the
|
|
62
|
+
* newest are kept (requests in flight are never dropped)
|
|
62
63
|
* Prevents memory issues in long-running sessions with high network activity
|
|
63
64
|
*/
|
|
64
65
|
export declare const MAX_NETWORK_REQUESTS = 10000;
|
|
@@ -88,6 +89,24 @@ export declare const OBJECT_EXPANSION_FAILURE_THRESHOLD = 5;
|
|
|
88
89
|
* Can be overridden with --max-body-size flag
|
|
89
90
|
*/
|
|
90
91
|
export declare const MAX_RESPONSE_SIZE: number;
|
|
92
|
+
/**
|
|
93
|
+
* Total size of the response bodies a session keeps (100MB)
|
|
94
|
+
* Past this the oldest bodies are replaced by a placeholder; their requests stay
|
|
95
|
+
*/
|
|
96
|
+
export declare const MAX_TOTAL_BODY_BYTES: number;
|
|
97
|
+
/**
|
|
98
|
+
* Characters of one value `dom get --raw` and `dom eval` print (human output,
|
|
99
|
+
* and eval string results and outer HTML in JSON)
|
|
100
|
+
*/
|
|
101
|
+
export declare const MAX_VALUE_LENGTH = 20000;
|
|
102
|
+
/**
|
|
103
|
+
* Characters of a console message text in human output (`console`, `peek`)
|
|
104
|
+
*/
|
|
105
|
+
export declare const MAX_CONSOLE_TEXT_LENGTH = 200;
|
|
106
|
+
/**
|
|
107
|
+
* Characters of a console message text in JSON output (`console`, `peek`)
|
|
108
|
+
*/
|
|
109
|
+
export declare const MAX_CONSOLE_JSON_TEXT_LENGTH = 10000;
|
|
91
110
|
/**
|
|
92
111
|
* Total Chrome network buffer size (50MB)
|
|
93
112
|
* Limits total memory used by Chrome for preserving network payloads
|
|
@@ -103,6 +122,15 @@ export declare const CHROME_NETWORK_BUFFER_PER_RESOURCE: number;
|
|
|
103
122
|
* Limits size of POST body data included in requestWillBeSent notification
|
|
104
123
|
*/
|
|
105
124
|
export declare const CHROME_POST_DATA_LIMIT: number;
|
|
125
|
+
/** Matches `dom query` and `dom a11y query` list with `--json` and no `--limit` */
|
|
126
|
+
export declare const QUERY_JSON_LIST_LIMIT = 100;
|
|
127
|
+
/** Matches `dom layout` measures per command (the rest are counted as omitted) */
|
|
128
|
+
export declare const LAYOUT_ELEMENT_LIMIT = 100;
|
|
129
|
+
/**
|
|
130
|
+
* Requests listed in an action's result (a click that loads a page triggers
|
|
131
|
+
* its whole load); notable ones are kept before assets
|
|
132
|
+
*/
|
|
133
|
+
export declare const MAX_TRIGGERED_REQUESTS = 50;
|
|
106
134
|
/**
|
|
107
135
|
* Default page readiness timeout (2 seconds)
|
|
108
136
|
* Maximum time to wait for page to be ready before proceeding
|
package/dist/constants.js
CHANGED
|
@@ -87,7 +87,8 @@ export const BDG_CHROME_PREFS = {
|
|
|
87
87
|
// DATA COLLECTION LIMITS
|
|
88
88
|
// ============================================================================
|
|
89
89
|
/**
|
|
90
|
-
*
|
|
90
|
+
* Finished network requests kept: past this the oldest are dropped, so the
|
|
91
|
+
* newest are kept (requests in flight are never dropped)
|
|
91
92
|
* Prevents memory issues in long-running sessions with high network activity
|
|
92
93
|
*/
|
|
93
94
|
export const MAX_NETWORK_REQUESTS = 10000;
|
|
@@ -120,6 +121,27 @@ export const OBJECT_EXPANSION_FAILURE_THRESHOLD = 5;
|
|
|
120
121
|
* Can be overridden with --max-body-size flag
|
|
121
122
|
*/
|
|
122
123
|
export const MAX_RESPONSE_SIZE = 5 * 1024 * 1024; // 5MB
|
|
124
|
+
/**
|
|
125
|
+
* Total size of the response bodies a session keeps (100MB)
|
|
126
|
+
* Past this the oldest bodies are replaced by a placeholder; their requests stay
|
|
127
|
+
*/
|
|
128
|
+
export const MAX_TOTAL_BODY_BYTES = 100 * 1024 * 1024;
|
|
129
|
+
// ============================================================================
|
|
130
|
+
// OUTPUT VALUE LIMITS (lifted by --full)
|
|
131
|
+
// ============================================================================
|
|
132
|
+
/**
|
|
133
|
+
* Characters of one value `dom get --raw` and `dom eval` print (human output,
|
|
134
|
+
* and eval string results and outer HTML in JSON)
|
|
135
|
+
*/
|
|
136
|
+
export const MAX_VALUE_LENGTH = 20_000;
|
|
137
|
+
/**
|
|
138
|
+
* Characters of a console message text in human output (`console`, `peek`)
|
|
139
|
+
*/
|
|
140
|
+
export const MAX_CONSOLE_TEXT_LENGTH = 200;
|
|
141
|
+
/**
|
|
142
|
+
* Characters of a console message text in JSON output (`console`, `peek`)
|
|
143
|
+
*/
|
|
144
|
+
export const MAX_CONSOLE_JSON_TEXT_LENGTH = 10_000;
|
|
123
145
|
// ============================================================================
|
|
124
146
|
// CHROME CDP BUFFER LIMITS
|
|
125
147
|
// ============================================================================
|
|
@@ -139,6 +161,18 @@ export const CHROME_NETWORK_BUFFER_PER_RESOURCE = 10 * 1024 * 1024; // 10MB
|
|
|
139
161
|
*/
|
|
140
162
|
export const CHROME_POST_DATA_LIMIT = 1 * 1024 * 1024; // 1MB
|
|
141
163
|
// ============================================================================
|
|
164
|
+
// JSON LIST LIMITS
|
|
165
|
+
// ============================================================================
|
|
166
|
+
/** Matches `dom query` and `dom a11y query` list with `--json` and no `--limit` */
|
|
167
|
+
export const QUERY_JSON_LIST_LIMIT = 100;
|
|
168
|
+
/** Matches `dom layout` measures per command (the rest are counted as omitted) */
|
|
169
|
+
export const LAYOUT_ELEMENT_LIMIT = 100;
|
|
170
|
+
/**
|
|
171
|
+
* Requests listed in an action's result (a click that loads a page triggers
|
|
172
|
+
* its whole load); notable ones are kept before assets
|
|
173
|
+
*/
|
|
174
|
+
export const MAX_TRIGGERED_REQUESTS = 50;
|
|
175
|
+
// ============================================================================
|
|
142
176
|
// TIMEOUTS & INTERVALS
|
|
143
177
|
// ============================================================================
|
|
144
178
|
/**
|
|
@@ -241,8 +241,15 @@ export class SessionController {
|
|
|
241
241
|
duration: data.duration,
|
|
242
242
|
target: data.target,
|
|
243
243
|
data: { network: data.network, console: data.console },
|
|
244
|
-
totals: {
|
|
244
|
+
totals: {
|
|
245
|
+
network: data.totalNetwork,
|
|
246
|
+
console: data.totalConsole,
|
|
247
|
+
...(data.droppedConsole && { consoleDropped: data.droppedConsole }),
|
|
248
|
+
...(data.droppedNetwork && { networkDropped: data.droppedNetwork }),
|
|
249
|
+
...(data.evictedNetworkBodies && { networkBodiesEvicted: data.evictedNetworkBodies }),
|
|
250
|
+
},
|
|
245
251
|
currentNavigationId: data.currentNavigationId,
|
|
252
|
+
...(data.pageCrashedAt !== undefined && { pageCrashedAt: data.pageCrashedAt }),
|
|
246
253
|
partial: true,
|
|
247
254
|
},
|
|
248
255
|
},
|
|
@@ -24,8 +24,9 @@ export declare function launchDaemon(): Promise<SpawnedDaemon | undefined>;
|
|
|
24
24
|
* Check that the session directory can hold the daemon's files before
|
|
25
25
|
* spawning it (otherwise the daemon dies and only its log says why).
|
|
26
26
|
*
|
|
27
|
-
* @throws SessionDirError (103) for a file
|
|
28
|
-
*
|
|
27
|
+
* @throws SessionDirError (103) for a file or a path that cannot hold a
|
|
28
|
+
* directory (a pseudo-filesystem like `/proc`), (81) for a too-long path
|
|
29
|
+
* like a named session's, (82) when not writable
|
|
29
30
|
*/
|
|
30
31
|
export declare function assertUsableSessionDir(): void;
|
|
31
32
|
//# sourceMappingURL=launcher.d.ts.map
|
package/dist/daemon/launcher.js
CHANGED
|
@@ -11,13 +11,16 @@ import fs from 'fs';
|
|
|
11
11
|
import { join } from 'path';
|
|
12
12
|
import { DaemonStartupError, SessionDirError } from './errors.js';
|
|
13
13
|
import { sessionDirIsFileError, sessionDirNotWritableError, socketPathTooLongError, } from '../errors/messages.js';
|
|
14
|
-
import {
|
|
14
|
+
import { killSessionChromes, readLiveDaemonPid } from '../session/cleanup/staleSession.js';
|
|
15
|
+
import { isDaemonAlive, isDaemonSocketGone } from '../session/daemonSocket.js';
|
|
15
16
|
import { MAX_DAEMON_SOCKET_PATH_BYTES, ensureSessionDir, getSessionDir, getSessionFilePath, } from '../session/paths.js';
|
|
16
17
|
import { createLogger } from '../ui/logging/index.js';
|
|
17
18
|
import { delay } from '../utils/async.js';
|
|
19
|
+
import { directoryProblem } from '../utils/directories.js';
|
|
18
20
|
import { getErrorMessage } from '../utils/errors.js';
|
|
19
21
|
import { EXIT_CODES } from '../utils/exitCodes.js';
|
|
20
22
|
import { DAEMON_SCRIPT_PATH } from '../utils/packageRoot.js';
|
|
23
|
+
import { isProcessAlive } from '../utils/process.js';
|
|
21
24
|
const log = createLogger('launcher');
|
|
22
25
|
const DAEMON_READY_TIMEOUT_MS = 5000;
|
|
23
26
|
const DAEMON_READY_POLL_MS = 20;
|
|
@@ -37,6 +40,7 @@ export async function launchDaemon() {
|
|
|
37
40
|
throw new DaemonStartupError(`Daemon script not found at ${DAEMON_SCRIPT_PATH}. Did you run 'npm run build'?`, 'DAEMON_SCRIPT_NOT_FOUND');
|
|
38
41
|
}
|
|
39
42
|
assertUsableSessionDir();
|
|
43
|
+
await stopUnreachableDaemon();
|
|
40
44
|
const logPath = join(getSessionDir(), 'daemon.log');
|
|
41
45
|
rotateLog(logPath);
|
|
42
46
|
const logFd = fs.openSync(logPath, 'a');
|
|
@@ -54,12 +58,48 @@ export async function launchDaemon() {
|
|
|
54
58
|
await waitForDaemonReady(() => exited);
|
|
55
59
|
return { pid: daemon.pid, hasExited: () => exited };
|
|
56
60
|
}
|
|
61
|
+
/** How long a daemon that lost its socket gets to end its session before it is killed */
|
|
62
|
+
const UNREACHABLE_DAEMON_EXIT_MS = 8000;
|
|
63
|
+
/**
|
|
64
|
+
* Stop a daemon of this session directory that still runs but can no longer
|
|
65
|
+
* be reached (its socket was removed, or refuses connections; one that is
|
|
66
|
+
* only slow to answer is left alone), before a new one starts: two daemons
|
|
67
|
+
* would launch two Chromes and remove each other's files. It is asked to end
|
|
68
|
+
* its session (closing its Chrome), and killed with what it launched if it
|
|
69
|
+
* does not exit in time.
|
|
70
|
+
*/
|
|
71
|
+
async function stopUnreachableDaemon() {
|
|
72
|
+
const pid = readLiveDaemonPid();
|
|
73
|
+
if (pid === null || !(await isDaemonSocketGone()))
|
|
74
|
+
return;
|
|
75
|
+
log.info(`Stopping daemon ${pid}, which can no longer be reached (its socket is gone)`);
|
|
76
|
+
try {
|
|
77
|
+
process.kill(pid, 'SIGTERM');
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
log.debug(`Daemon ${pid} not signalled: ${getErrorMessage(error)}`);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const deadline = Date.now() + UNREACHABLE_DAEMON_EXIT_MS;
|
|
84
|
+
while (isProcessAlive(pid) && Date.now() < deadline)
|
|
85
|
+
await delay(100);
|
|
86
|
+
if (!isProcessAlive(pid))
|
|
87
|
+
return;
|
|
88
|
+
try {
|
|
89
|
+
process.kill(pid, 'SIGKILL');
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
log.debug(`Daemon ${pid} not killed: ${getErrorMessage(error)}`);
|
|
93
|
+
}
|
|
94
|
+
killSessionChromes();
|
|
95
|
+
}
|
|
57
96
|
/**
|
|
58
97
|
* Check that the session directory can hold the daemon's files before
|
|
59
98
|
* spawning it (otherwise the daemon dies and only its log says why).
|
|
60
99
|
*
|
|
61
|
-
* @throws SessionDirError (103) for a file
|
|
62
|
-
*
|
|
100
|
+
* @throws SessionDirError (103) for a file or a path that cannot hold a
|
|
101
|
+
* directory (a pseudo-filesystem like `/proc`), (81) for a too-long path
|
|
102
|
+
* like a named session's, (82) when not writable
|
|
63
103
|
*/
|
|
64
104
|
export function assertUsableSessionDir() {
|
|
65
105
|
const dir = getSessionDir();
|
|
@@ -72,6 +112,10 @@ export function assertUsableSessionDir() {
|
|
|
72
112
|
if (Buffer.byteLength(socketPath) > MAX_DAEMON_SOCKET_PATH_BYTES) {
|
|
73
113
|
fail(socketPathTooLongError(socketPath, MAX_DAEMON_SOCKET_PATH_BYTES), EXIT_CODES.INVALID_ARGUMENTS);
|
|
74
114
|
}
|
|
115
|
+
const problem = directoryProblem(dir);
|
|
116
|
+
if (problem) {
|
|
117
|
+
fail(sessionDirNotWritableError(dir, problem.reason), problem.denied ? EXIT_CODES.PERMISSION_DENIED : EXIT_CODES.SESSION_FILE_ERROR);
|
|
118
|
+
}
|
|
75
119
|
try {
|
|
76
120
|
ensureSessionDir();
|
|
77
121
|
fs.accessSync(dir, fs.constants.W_OK);
|
|
@@ -80,7 +80,11 @@ export declare class Session {
|
|
|
80
80
|
*/
|
|
81
81
|
launch(): Promise<void>;
|
|
82
82
|
/**
|
|
83
|
-
* Execute a registered command against this session.
|
|
83
|
+
* Execute a registered command against this session. After a renderer
|
|
84
|
+
* crash only {@link RUN_ON_CRASHED_PAGE} commands run (`bdg cdp` only for
|
|
85
|
+
* {@link CRASH_SAFE_CDP} methods); others fail with exit 107 instead of
|
|
86
|
+
* waiting for a page that cannot answer. Commands that may change the page
|
|
87
|
+
* drop `dom inspect`'s kept matched rules ({@link withMatchedStylesReset}).
|
|
84
88
|
*
|
|
85
89
|
* @param name - Command name
|
|
86
90
|
* @param params - Command parameters
|
|
@@ -11,13 +11,16 @@ import { connectCDP, navigateToTarget } from './cdpSetup.js';
|
|
|
11
11
|
import { externalChromePort, findPageTarget, setupChromeConnection, } from './chromeConnection.js';
|
|
12
12
|
import { startTelemetryCollectors } from './collectors.js';
|
|
13
13
|
import { createCommandRegistry } from './commandRegistry.js';
|
|
14
|
+
import { withMatchedStylesReset } from './matchedStylesReset.js';
|
|
14
15
|
import { teardownSession } from './teardown.js';
|
|
15
16
|
import { CommandError } from '../../errors/index.js';
|
|
17
|
+
import { unknownSessionCommandMessage } from '../../errors/messages.js';
|
|
16
18
|
import { applySessionEmulation } from '../../runtime/page/emulation.js';
|
|
17
19
|
import { readPageLoadingState } from '../../runtime/page/loadingState.js';
|
|
18
20
|
import { reapOrphanedChrome, removeSessionFiles } from '../../session/cleanup/staleSession.js';
|
|
19
21
|
import { writeSessionMetadata } from '../../session/metadata.js';
|
|
20
22
|
import { getSessionPort } from '../../session/port.js';
|
|
23
|
+
import { pageCrashedCommandError } from '../../telemetry/pageCrash.js';
|
|
21
24
|
import { createLogger } from '../../ui/logging/index.js';
|
|
22
25
|
import { formatChromeNotice } from '../../ui/messages/chrome.js';
|
|
23
26
|
import { delay } from '../../utils/async.js';
|
|
@@ -31,6 +34,26 @@ const log = createLogger('session');
|
|
|
31
34
|
const CRASH_SETTLE_MS = 500;
|
|
32
35
|
/** Ports tried when automatically chosen ports turn out to be taken */
|
|
33
36
|
const PORT_ATTEMPTS = 3;
|
|
37
|
+
/**
|
|
38
|
+
* Commands that still work after the page's renderer crashed: they read what
|
|
39
|
+
* the session collected, load the page again (`page reload`/`navigate`) or
|
|
40
|
+
* send raw CDP. Every other command needs the page and fails at once.
|
|
41
|
+
*/
|
|
42
|
+
const RUN_ON_CRASHED_PAGE = new Set([
|
|
43
|
+
'session_peek',
|
|
44
|
+
'session_details',
|
|
45
|
+
'session_status',
|
|
46
|
+
'session_har_data',
|
|
47
|
+
'session_network_headers',
|
|
48
|
+
'page_navigate',
|
|
49
|
+
'cdp_call',
|
|
50
|
+
]);
|
|
51
|
+
/**
|
|
52
|
+
* CDP methods `bdg cdp` may still send after a renderer crash: loading the
|
|
53
|
+
* page again, and domains the browser process answers. Others wait for the
|
|
54
|
+
* renderer, which never answers.
|
|
55
|
+
*/
|
|
56
|
+
const CRASH_SAFE_CDP = /^(Page\.(navigate|reload|getNavigationHistory|navigateToHistoryEntry)|Target\.|Browser\.|Inspector\.|Network\.(?!getResponseBody|getRequestPostData)|Storage\.|SystemInfo\.)/;
|
|
34
57
|
/**
|
|
35
58
|
* Whether a launch failed because another process holds the port.
|
|
36
59
|
*
|
|
@@ -127,7 +150,11 @@ export class Session {
|
|
|
127
150
|
}
|
|
128
151
|
}
|
|
129
152
|
/**
|
|
130
|
-
* Execute a registered command against this session.
|
|
153
|
+
* Execute a registered command against this session. After a renderer
|
|
154
|
+
* crash only {@link RUN_ON_CRASHED_PAGE} commands run (`bdg cdp` only for
|
|
155
|
+
* {@link CRASH_SAFE_CDP} methods); others fail with exit 107 instead of
|
|
156
|
+
* waiting for a page that cannot answer. Commands that may change the page
|
|
157
|
+
* drop `dom inspect`'s kept matched rules ({@link withMatchedStylesReset}).
|
|
131
158
|
*
|
|
132
159
|
* @param name - Command name
|
|
133
160
|
* @param params - Command parameters
|
|
@@ -137,7 +164,19 @@ export class Session {
|
|
|
137
164
|
if (!this.cdp || !this.started || this.stopping) {
|
|
138
165
|
return Promise.reject(new Error('No active session'));
|
|
139
166
|
}
|
|
140
|
-
|
|
167
|
+
const crashedAt = this.store.pageCrashedAt;
|
|
168
|
+
const cdpMethod = name === 'cdp_call' ? params.method : undefined;
|
|
169
|
+
const needsPage = !RUN_ON_CRASHED_PAGE.has(name) ||
|
|
170
|
+
(cdpMethod !== undefined && !CRASH_SAFE_CDP.test(cdpMethod));
|
|
171
|
+
if (crashedAt !== undefined && needsPage) {
|
|
172
|
+
return Promise.reject(pageCrashedCommandError(crashedAt));
|
|
173
|
+
}
|
|
174
|
+
if (!Object.hasOwn(this.registry, name)) {
|
|
175
|
+
return Promise.reject(new CommandError(unknownSessionCommandMessage(name), {}, EXIT_CODES.INVALID_ARGUMENTS));
|
|
176
|
+
}
|
|
177
|
+
const handler = this.registry[name];
|
|
178
|
+
const cdp = this.cdp;
|
|
179
|
+
return withMatchedStylesReset(cdp, name, () => handler(cdp, params));
|
|
141
180
|
}
|
|
142
181
|
/**
|
|
143
182
|
* Summary of this session for start responses.
|
|
@@ -223,7 +262,7 @@ export class Session {
|
|
|
223
262
|
await this.releaseResources();
|
|
224
263
|
if (!this.started)
|
|
225
264
|
return;
|
|
226
|
-
removeSessionFiles();
|
|
265
|
+
removeSessionFiles(process.pid);
|
|
227
266
|
this.onEnded(reason);
|
|
228
267
|
})();
|
|
229
268
|
return this.stopping;
|
|
@@ -1,17 +1,31 @@
|
|
|
1
1
|
import type { DialogInfo } from '../../ipc/protocol/domTypes.js';
|
|
2
2
|
import type { NavigationEvent } from '../../telemetry/navigation.js';
|
|
3
3
|
import type { PendingRequest } from '../../telemetry/network.js';
|
|
4
|
+
import type { NetworkEvictions } from '../../telemetry/networkRetention.js';
|
|
4
5
|
import type { CDPTarget, ConsoleMessage, NetworkRequest, TelemetryType, WebSocketConnection } from '../../types.js';
|
|
5
6
|
export declare class TelemetryStore {
|
|
7
|
+
/**
|
|
8
|
+
* Finished requests, oldest first: the newest are kept, so the session's
|
|
9
|
+
* `networkEvictions.requestsDropped` oldest ones are gone
|
|
10
|
+
*/
|
|
6
11
|
readonly networkRequests: NetworkRequest[];
|
|
12
|
+
/** Requests dropped and response bodies evicted at the capture limits */
|
|
13
|
+
readonly networkEvictions: NetworkEvictions;
|
|
7
14
|
/** Requests still in flight, keyed by CDP requestId */
|
|
8
15
|
readonly pendingNetworkRequests: Map<string, PendingRequest>;
|
|
9
16
|
readonly consoleMessages: ConsoleMessage[];
|
|
17
|
+
/**
|
|
18
|
+
* Console messages dropped at the limit, oldest first: the index of
|
|
19
|
+
* `consoleMessages[i]` in the session is `consoleDropped + i`
|
|
20
|
+
*/
|
|
21
|
+
consoleDropped: number;
|
|
10
22
|
readonly navigationEvents: NavigationEvent[];
|
|
11
23
|
readonly websocketConnections: WebSocketConnection[];
|
|
12
24
|
/** JavaScript dialogs accepted during the session */
|
|
13
25
|
readonly dialogs: DialogInfo[];
|
|
14
26
|
activeTelemetry: TelemetryType[];
|
|
27
|
+
/** When the page's renderer crashed (epoch ms); undefined while the page is alive */
|
|
28
|
+
pageCrashedAt: number | undefined;
|
|
15
29
|
getCurrentNavigationId: (() => number) | null;
|
|
16
30
|
sessionStartTime: number;
|
|
17
31
|
targetInfo: CDPTarget | null;
|
|
@@ -21,7 +35,7 @@ export declare class TelemetryStore {
|
|
|
21
35
|
/**
|
|
22
36
|
* Record an accepted dialog, and show it among the console messages when
|
|
23
37
|
* console telemetry is collected (it is otherwise invisible: bdg accepts it
|
|
24
|
-
* before anyone could see it).
|
|
38
|
+
* before anyone could see it), dropping the oldest message at the limit.
|
|
25
39
|
*
|
|
26
40
|
* @param dialog - Dialog type and text
|
|
27
41
|
*/
|
|
@@ -1,14 +1,28 @@
|
|
|
1
|
+
import { MAX_CONSOLE_MESSAGES } from '../../constants.js';
|
|
1
2
|
import { dialogConsoleText } from '../../ui/messages/commands.js';
|
|
2
3
|
export class TelemetryStore {
|
|
4
|
+
/**
|
|
5
|
+
* Finished requests, oldest first: the newest are kept, so the session's
|
|
6
|
+
* `networkEvictions.requestsDropped` oldest ones are gone
|
|
7
|
+
*/
|
|
3
8
|
networkRequests = [];
|
|
9
|
+
/** Requests dropped and response bodies evicted at the capture limits */
|
|
10
|
+
networkEvictions = { requestsDropped: 0, bodiesEvicted: 0 };
|
|
4
11
|
/** Requests still in flight, keyed by CDP requestId */
|
|
5
12
|
pendingNetworkRequests = new Map();
|
|
6
13
|
consoleMessages = [];
|
|
14
|
+
/**
|
|
15
|
+
* Console messages dropped at the limit, oldest first: the index of
|
|
16
|
+
* `consoleMessages[i]` in the session is `consoleDropped + i`
|
|
17
|
+
*/
|
|
18
|
+
consoleDropped = 0;
|
|
7
19
|
navigationEvents = [];
|
|
8
20
|
websocketConnections = [];
|
|
9
21
|
/** JavaScript dialogs accepted during the session */
|
|
10
22
|
dialogs = [];
|
|
11
23
|
activeTelemetry = [];
|
|
24
|
+
/** When the page's renderer crashed (epoch ms); undefined while the page is alive */
|
|
25
|
+
pageCrashedAt;
|
|
12
26
|
getCurrentNavigationId = null;
|
|
13
27
|
sessionStartTime = Date.now();
|
|
14
28
|
targetInfo = null;
|
|
@@ -24,7 +38,7 @@ export class TelemetryStore {
|
|
|
24
38
|
/**
|
|
25
39
|
* Record an accepted dialog, and show it among the console messages when
|
|
26
40
|
* console telemetry is collected (it is otherwise invisible: bdg accepts it
|
|
27
|
-
* before anyone could see it).
|
|
41
|
+
* before anyone could see it), dropping the oldest message at the limit.
|
|
28
42
|
*
|
|
29
43
|
* @param dialog - Dialog type and text
|
|
30
44
|
*/
|
|
@@ -32,6 +46,10 @@ export class TelemetryStore {
|
|
|
32
46
|
this.dialogs.push(dialog);
|
|
33
47
|
if (!this.activeTelemetry.includes('console'))
|
|
34
48
|
return;
|
|
49
|
+
if (this.consoleMessages.length >= MAX_CONSOLE_MESSAGES) {
|
|
50
|
+
this.consoleMessages.shift();
|
|
51
|
+
this.consoleDropped++;
|
|
52
|
+
}
|
|
35
53
|
this.consoleMessages.push({
|
|
36
54
|
type: 'info',
|
|
37
55
|
text: dialogConsoleText(dialog),
|