chrome-devtools-mcp 0.8.0 → 0.9.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/README.md +79 -5
- package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Gzip.js +8 -6
- package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Settings.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Worker.js +10 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/core/host/UserMetrics.js +2 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/ArrayUtilities.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/ConnectionTransport.js +12 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/InspectorBackend.js +15 -27
- package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/protocol_client.js +2 -8
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSMatchedStyles.js +42 -7
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSRule.js +34 -6
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ChildTargetManager.js +3 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/Connections.js +2 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/DOMModel.js +3 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/DebuggerModel.js +2 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/NetworkManager.js +336 -40
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/PreloadingModel.js +56 -13
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/RehydratingConnection.js +32 -7
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ResourceTreeModel.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/{models/source_map_scopes → core/sdk}/ScopeTreeCache.js +9 -5
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMap.js +48 -11
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMapManager.js +8 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMapScopesInfo.js +131 -8
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/TargetManager.js +0 -21
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/TraceObject.js +9 -6
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/sdk.js +2 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/generated/ARIAProperties.js +1301 -174
- package/build/node_modules/chrome-devtools-frontend/front_end/generated/Deprecation.js +7 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/generated/InspectorBackendCommands.js +8 -6
- package/build/node_modules/chrome-devtools-frontend/front_end/generated/SupportedCSSProperties.js +16 -19
- package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.js +50 -34
- package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AICallTree.js +2 -3
- package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/CompilerScriptMapping.js +45 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/models/formatter/FormatterWorkerPool.js +14 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/source_map_scopes/NamesResolver.js +5 -11
- package/build/node_modules/chrome-devtools-frontend/front_end/models/source_map_scopes/source_map_scopes.js +1 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/Trie.js +8 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/ModelImpl.js +6 -3
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/extras/TraceTree.js +10 -3
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/MetaHandler.js +4 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/UserTimingsHandler.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/CLSCulprits.js +2 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/Cache.js +2 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/DOMSize.js +2 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/DocumentLatency.js +2 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/DuplicatedJavaScript.js +2 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/FontDisplay.js +2 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/ForcedReflow.js +3 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/INPBreakdown.js +2 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/ImageDelivery.js +2 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/LCPBreakdown.js +2 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/LCPDiscovery.js +2 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/LegacyJavaScript.js +2 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/ModernHTTP.js +2 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/NetworkDependencyTree.js +2 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/RenderBlocking.js +2 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/SlowCSSSelector.js +2 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/ThirdParties.js +2 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/Viewport.js +2 -1
- package/build/src/DevToolsConnectionAdapter.js +32 -0
- package/build/src/McpContext.js +81 -31
- package/build/src/McpResponse.js +149 -45
- package/build/src/PageCollector.js +110 -26
- package/build/src/WaitForHelper.js +5 -0
- package/build/src/browser.js +17 -7
- package/build/src/cli.js +82 -6
- package/build/src/formatters/consoleFormatter.js +29 -62
- package/build/src/formatters/networkFormatter.js +5 -6
- package/build/src/formatters/snapshotFormatter.js +23 -51
- package/build/src/logger.js +1 -1
- package/build/src/main.js +27 -26
- package/build/src/polyfill.js +2 -2
- package/build/src/third_party/THIRD_PARTY_NOTICES +1393 -0
- package/build/src/third_party/index.js +76159 -0
- package/build/src/tools/ToolDefinition.js +2 -2
- package/build/src/tools/categories.js +17 -9
- package/build/src/tools/console.js +71 -6
- package/build/src/tools/emulation.js +6 -7
- package/build/src/tools/input.js +78 -41
- package/build/src/tools/network.js +18 -10
- package/build/src/tools/pages.js +21 -21
- package/build/src/tools/performance.js +8 -8
- package/build/src/tools/screenshot.js +8 -8
- package/build/src/tools/script.js +29 -15
- package/build/src/tools/snapshot.js +15 -20
- package/build/src/utils/types.js +6 -0
- package/package.json +17 -14
package/build/src/McpResponse.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { formatConsoleEventShort, formatConsoleEventVerbose, } from './formatters/consoleFormatter.js';
|
|
2
2
|
import { getFormattedHeaderValue, getFormattedResponseBody, getFormattedRequestBody, getShortDescriptionForRequest, getStatusFromRequest, } from './formatters/networkFormatter.js';
|
|
3
3
|
import { formatA11ySnapshot } from './formatters/snapshotFormatter.js';
|
|
4
4
|
import { handleDialog } from './tools/pages.js';
|
|
@@ -6,17 +6,19 @@ import { paginate } from './utils/pagination.js';
|
|
|
6
6
|
export class McpResponse {
|
|
7
7
|
#includePages = false;
|
|
8
8
|
#includeSnapshot = false;
|
|
9
|
-
#
|
|
10
|
-
#
|
|
9
|
+
#includeVerboseSnapshot = false;
|
|
10
|
+
#attachedNetworkRequestId;
|
|
11
|
+
#attachedConsoleMessageId;
|
|
11
12
|
#textResponseLines = [];
|
|
12
|
-
#formattedConsoleData;
|
|
13
13
|
#images = [];
|
|
14
14
|
#networkRequestsOptions;
|
|
15
|
+
#consoleDataOptions;
|
|
15
16
|
setIncludePages(value) {
|
|
16
17
|
this.#includePages = value;
|
|
17
18
|
}
|
|
18
|
-
setIncludeSnapshot(value) {
|
|
19
|
+
setIncludeSnapshot(value, verbose = false) {
|
|
19
20
|
this.#includeSnapshot = value;
|
|
21
|
+
this.#includeVerboseSnapshot = verbose;
|
|
20
22
|
}
|
|
21
23
|
setIncludeNetworkRequests(value, options) {
|
|
22
24
|
if (!value) {
|
|
@@ -32,16 +34,32 @@ export class McpResponse {
|
|
|
32
34
|
}
|
|
33
35
|
: undefined,
|
|
34
36
|
resourceTypes: options?.resourceTypes,
|
|
37
|
+
includePreservedRequests: options?.includePreservedRequests,
|
|
35
38
|
};
|
|
36
39
|
}
|
|
37
|
-
setIncludeConsoleData(value) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
setIncludeConsoleData(value, options) {
|
|
41
|
+
if (!value) {
|
|
42
|
+
this.#consoleDataOptions = undefined;
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
this.#consoleDataOptions = {
|
|
46
|
+
include: value,
|
|
47
|
+
pagination: options?.pageSize || options?.pageIdx
|
|
48
|
+
? {
|
|
49
|
+
pageSize: options.pageSize,
|
|
50
|
+
pageIdx: options.pageIdx,
|
|
51
|
+
}
|
|
52
|
+
: undefined,
|
|
53
|
+
types: options?.types,
|
|
54
|
+
includePreservedMessages: options?.includePreservedMessages,
|
|
43
55
|
};
|
|
44
56
|
}
|
|
57
|
+
attachNetworkRequest(reqid) {
|
|
58
|
+
this.#attachedNetworkRequestId = reqid;
|
|
59
|
+
}
|
|
60
|
+
attachConsoleMessage(msgid) {
|
|
61
|
+
this.#attachedConsoleMessageId = msgid;
|
|
62
|
+
}
|
|
45
63
|
get includePages() {
|
|
46
64
|
return this.#includePages;
|
|
47
65
|
}
|
|
@@ -49,14 +67,20 @@ export class McpResponse {
|
|
|
49
67
|
return this.#networkRequestsOptions?.include ?? false;
|
|
50
68
|
}
|
|
51
69
|
get includeConsoleData() {
|
|
52
|
-
return this.#
|
|
70
|
+
return this.#consoleDataOptions?.include ?? false;
|
|
53
71
|
}
|
|
54
|
-
get
|
|
55
|
-
return this.#
|
|
72
|
+
get attachedNetworkRequestId() {
|
|
73
|
+
return this.#attachedNetworkRequestId;
|
|
56
74
|
}
|
|
57
75
|
get networkRequestsPageIdx() {
|
|
58
76
|
return this.#networkRequestsOptions?.pagination?.pageIdx;
|
|
59
77
|
}
|
|
78
|
+
get consoleMessagesPageIdx() {
|
|
79
|
+
return this.#consoleDataOptions?.pagination?.pageIdx;
|
|
80
|
+
}
|
|
81
|
+
get consoleMessagesTypes() {
|
|
82
|
+
return this.#consoleDataOptions?.types;
|
|
83
|
+
}
|
|
60
84
|
appendResponseLine(value) {
|
|
61
85
|
this.#textResponseLines.push(value);
|
|
62
86
|
}
|
|
@@ -72,34 +96,99 @@ export class McpResponse {
|
|
|
72
96
|
get includeSnapshot() {
|
|
73
97
|
return this.#includeSnapshot;
|
|
74
98
|
}
|
|
99
|
+
get includeVersboseSnapshot() {
|
|
100
|
+
return this.#includeVerboseSnapshot;
|
|
101
|
+
}
|
|
75
102
|
async handle(toolName, context) {
|
|
76
103
|
if (this.#includePages) {
|
|
77
104
|
await context.createPagesSnapshot();
|
|
78
105
|
}
|
|
79
106
|
if (this.#includeSnapshot) {
|
|
80
|
-
await context.createTextSnapshot();
|
|
107
|
+
await context.createTextSnapshot(this.#includeVerboseSnapshot);
|
|
81
108
|
}
|
|
82
|
-
|
|
83
|
-
if (this.#
|
|
84
|
-
const request = context.
|
|
85
|
-
|
|
86
|
-
await getFormattedRequestBody(request);
|
|
109
|
+
const bodies = {};
|
|
110
|
+
if (this.#attachedNetworkRequestId) {
|
|
111
|
+
const request = context.getNetworkRequestById(this.#attachedNetworkRequestId);
|
|
112
|
+
bodies.requestBody = await getFormattedRequestBody(request);
|
|
87
113
|
const response = request.response();
|
|
88
114
|
if (response) {
|
|
89
|
-
|
|
90
|
-
await getFormattedResponseBody(response);
|
|
115
|
+
bodies.responseBody = await getFormattedResponseBody(response);
|
|
91
116
|
}
|
|
92
117
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
118
|
+
let consoleData;
|
|
119
|
+
if (this.#attachedConsoleMessageId) {
|
|
120
|
+
const message = context.getConsoleMessageById(this.#attachedConsoleMessageId);
|
|
121
|
+
const consoleMessageStableId = this.#attachedConsoleMessageId;
|
|
122
|
+
if ('args' in message) {
|
|
123
|
+
const consoleMessage = message;
|
|
124
|
+
consoleData = {
|
|
125
|
+
consoleMessageStableId,
|
|
126
|
+
type: consoleMessage.type(),
|
|
127
|
+
message: consoleMessage.text(),
|
|
128
|
+
args: await Promise.all(consoleMessage.args().map(async (arg) => {
|
|
129
|
+
const stringArg = await arg.jsonValue().catch(() => {
|
|
130
|
+
// Ignore errors.
|
|
131
|
+
});
|
|
132
|
+
return typeof stringArg === 'object'
|
|
133
|
+
? JSON.stringify(stringArg)
|
|
134
|
+
: String(stringArg);
|
|
135
|
+
})),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
consoleData = {
|
|
140
|
+
consoleMessageStableId,
|
|
141
|
+
type: 'error',
|
|
142
|
+
message: message.message,
|
|
143
|
+
args: [],
|
|
144
|
+
};
|
|
98
145
|
}
|
|
99
146
|
}
|
|
100
|
-
|
|
147
|
+
let consoleListData;
|
|
148
|
+
if (this.#consoleDataOptions?.include) {
|
|
149
|
+
let messages = context.getConsoleData(this.#consoleDataOptions.includePreservedMessages);
|
|
150
|
+
if (this.#consoleDataOptions.types?.length) {
|
|
151
|
+
const normalizedTypes = new Set(this.#consoleDataOptions.types);
|
|
152
|
+
messages = messages.filter(message => {
|
|
153
|
+
if ('type' in message) {
|
|
154
|
+
return normalizedTypes.has(message.type());
|
|
155
|
+
}
|
|
156
|
+
return normalizedTypes.has('error');
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
consoleListData = await Promise.all(messages.map(async (item) => {
|
|
160
|
+
const consoleMessageStableId = context.getConsoleMessageStableId(item);
|
|
161
|
+
if ('args' in item) {
|
|
162
|
+
const consoleMessage = item;
|
|
163
|
+
return {
|
|
164
|
+
consoleMessageStableId,
|
|
165
|
+
type: consoleMessage.type(),
|
|
166
|
+
message: consoleMessage.text(),
|
|
167
|
+
args: await Promise.all(consoleMessage.args().map(async (arg) => {
|
|
168
|
+
const stringArg = await arg.jsonValue().catch(() => {
|
|
169
|
+
// Ignore errors.
|
|
170
|
+
});
|
|
171
|
+
return typeof stringArg === 'object'
|
|
172
|
+
? JSON.stringify(stringArg)
|
|
173
|
+
: String(stringArg);
|
|
174
|
+
})),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
consoleMessageStableId,
|
|
179
|
+
type: 'error',
|
|
180
|
+
message: item.message,
|
|
181
|
+
args: [],
|
|
182
|
+
};
|
|
183
|
+
}));
|
|
184
|
+
}
|
|
185
|
+
return this.format(toolName, context, {
|
|
186
|
+
bodies,
|
|
187
|
+
consoleData,
|
|
188
|
+
consoleListData,
|
|
189
|
+
});
|
|
101
190
|
}
|
|
102
|
-
format(toolName, context) {
|
|
191
|
+
format(toolName, context, data) {
|
|
103
192
|
const response = [`# ${toolName} response`];
|
|
104
193
|
for (const line of this.#textResponseLines) {
|
|
105
194
|
response.push(line);
|
|
@@ -117,8 +206,11 @@ export class McpResponse {
|
|
|
117
206
|
}
|
|
118
207
|
const dialog = context.getDialog();
|
|
119
208
|
if (dialog) {
|
|
209
|
+
const defaultValueIfNeeded = dialog.type() === 'prompt'
|
|
210
|
+
? ` (default value: "${dialog.defaultValue()}")`
|
|
211
|
+
: '';
|
|
120
212
|
response.push(`# Open dialog
|
|
121
|
-
${dialog.type()}: ${dialog.message()}
|
|
213
|
+
${dialog.type()}: ${dialog.message()}${defaultValueIfNeeded}.
|
|
122
214
|
Call ${handleDialog.name} to handle it before continuing.`);
|
|
123
215
|
}
|
|
124
216
|
if (this.#includePages) {
|
|
@@ -138,9 +230,10 @@ Call ${handleDialog.name} to handle it before continuing.`);
|
|
|
138
230
|
response.push(formattedSnapshot);
|
|
139
231
|
}
|
|
140
232
|
}
|
|
141
|
-
response.push(...this.#
|
|
233
|
+
response.push(...this.#formatNetworkRequestData(context, data.bodies));
|
|
234
|
+
response.push(...this.#formatConsoleData(data.consoleData));
|
|
142
235
|
if (this.#networkRequestsOptions?.include) {
|
|
143
|
-
let requests = context.getNetworkRequests();
|
|
236
|
+
let requests = context.getNetworkRequests(this.#networkRequestsOptions?.includePreservedRequests);
|
|
144
237
|
// Apply resource type filtering if specified
|
|
145
238
|
if (this.#networkRequestsOptions.resourceTypes?.length) {
|
|
146
239
|
const normalizedTypes = new Set(this.#networkRequestsOptions.resourceTypes);
|
|
@@ -154,17 +247,20 @@ Call ${handleDialog.name} to handle it before continuing.`);
|
|
|
154
247
|
const data = this.#dataWithPagination(requests, this.#networkRequestsOptions.pagination);
|
|
155
248
|
response.push(...data.info);
|
|
156
249
|
for (const request of data.items) {
|
|
157
|
-
response.push(getShortDescriptionForRequest(request));
|
|
250
|
+
response.push(getShortDescriptionForRequest(request, context.getNetworkRequestStableId(request)));
|
|
158
251
|
}
|
|
159
252
|
}
|
|
160
253
|
else {
|
|
161
254
|
response.push('No requests found.');
|
|
162
255
|
}
|
|
163
256
|
}
|
|
164
|
-
if (this.#
|
|
257
|
+
if (this.#consoleDataOptions?.include) {
|
|
258
|
+
const messages = data.consoleListData ?? [];
|
|
165
259
|
response.push('## Console messages');
|
|
166
|
-
if (
|
|
167
|
-
|
|
260
|
+
if (messages.length) {
|
|
261
|
+
const data = this.#dataWithPagination(messages, this.#consoleDataOptions.pagination);
|
|
262
|
+
response.push(...data.info);
|
|
263
|
+
response.push(...data.items.map(message => formatConsoleEventShort(message)));
|
|
168
264
|
}
|
|
169
265
|
else {
|
|
170
266
|
response.push('<no console messages found>');
|
|
@@ -203,22 +299,30 @@ Call ${handleDialog.name} to handle it before continuing.`);
|
|
|
203
299
|
items: paginationResult.items,
|
|
204
300
|
};
|
|
205
301
|
}
|
|
206
|
-
#
|
|
302
|
+
#formatConsoleData(data) {
|
|
303
|
+
const response = [];
|
|
304
|
+
if (!data) {
|
|
305
|
+
return response;
|
|
306
|
+
}
|
|
307
|
+
response.push(formatConsoleEventVerbose(data));
|
|
308
|
+
return response;
|
|
309
|
+
}
|
|
310
|
+
#formatNetworkRequestData(context, data) {
|
|
207
311
|
const response = [];
|
|
208
|
-
const
|
|
209
|
-
if (!
|
|
312
|
+
const id = this.#attachedNetworkRequestId;
|
|
313
|
+
if (!id) {
|
|
210
314
|
return response;
|
|
211
315
|
}
|
|
212
|
-
const httpRequest = context.
|
|
316
|
+
const httpRequest = context.getNetworkRequestById(id);
|
|
213
317
|
response.push(`## Request ${httpRequest.url()}`);
|
|
214
318
|
response.push(`Status: ${getStatusFromRequest(httpRequest)}`);
|
|
215
319
|
response.push(`### Request Headers`);
|
|
216
320
|
for (const line of getFormattedHeaderValue(httpRequest.headers())) {
|
|
217
321
|
response.push(line);
|
|
218
322
|
}
|
|
219
|
-
if (
|
|
323
|
+
if (data.requestBody) {
|
|
220
324
|
response.push(`### Request Body`);
|
|
221
|
-
response.push(
|
|
325
|
+
response.push(data.requestBody);
|
|
222
326
|
}
|
|
223
327
|
const httpResponse = httpRequest.response();
|
|
224
328
|
if (httpResponse) {
|
|
@@ -227,9 +331,9 @@ Call ${handleDialog.name} to handle it before continuing.`);
|
|
|
227
331
|
response.push(line);
|
|
228
332
|
}
|
|
229
333
|
}
|
|
230
|
-
if (
|
|
334
|
+
if (data.responseBody) {
|
|
231
335
|
response.push(`### Response Body`);
|
|
232
|
-
response.push(
|
|
336
|
+
response.push(data.responseBody);
|
|
233
337
|
}
|
|
234
338
|
const httpFailure = httpRequest.failure();
|
|
235
339
|
if (httpFailure) {
|
|
@@ -241,7 +345,7 @@ Call ${handleDialog.name} to handle it before continuing.`);
|
|
|
241
345
|
response.push(`### Redirect chain`);
|
|
242
346
|
let indent = 0;
|
|
243
347
|
for (const request of redirectChain.reverse()) {
|
|
244
|
-
response.push(`${' '.repeat(indent)}${getShortDescriptionForRequest(request)}`);
|
|
348
|
+
response.push(`${' '.repeat(indent)}${getShortDescriptionForRequest(request, context.getNetworkRequestStableId(request))}`);
|
|
245
349
|
indent++;
|
|
246
350
|
}
|
|
247
351
|
}
|
|
@@ -3,18 +3,30 @@
|
|
|
3
3
|
* Copyright 2025 Google LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
+
function createIdGenerator() {
|
|
7
|
+
let i = 1;
|
|
8
|
+
return () => {
|
|
9
|
+
if (i === Number.MAX_SAFE_INTEGER) {
|
|
10
|
+
i = 0;
|
|
11
|
+
}
|
|
12
|
+
return i++;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export const stableIdSymbol = Symbol('stableIdSymbol');
|
|
6
16
|
export class PageCollector {
|
|
7
17
|
#browser;
|
|
8
|
-
#
|
|
18
|
+
#listenersInitializer;
|
|
19
|
+
#listeners = new WeakMap();
|
|
20
|
+
#maxNavigationSaved = 3;
|
|
9
21
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
22
|
+
* This maps a Page to a list of navigations with a sub-list
|
|
23
|
+
* of all collected resources.
|
|
24
|
+
* The newer navigations come first.
|
|
13
25
|
*/
|
|
14
26
|
storage = new WeakMap();
|
|
15
|
-
constructor(browser,
|
|
27
|
+
constructor(browser, listeners) {
|
|
16
28
|
this.#browser = browser;
|
|
17
|
-
this.#
|
|
29
|
+
this.#listenersInitializer = listeners;
|
|
18
30
|
}
|
|
19
31
|
async init() {
|
|
20
32
|
const pages = await this.#browser.pages();
|
|
@@ -28,6 +40,13 @@ export class PageCollector {
|
|
|
28
40
|
}
|
|
29
41
|
this.#initializePage(page);
|
|
30
42
|
});
|
|
43
|
+
this.#browser.on('targetdestroyed', async (target) => {
|
|
44
|
+
const page = await target.page();
|
|
45
|
+
if (!page) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
this.#cleanupPageDestroyed(page);
|
|
49
|
+
});
|
|
31
50
|
}
|
|
32
51
|
addPage(page) {
|
|
33
52
|
this.#initializePage(page);
|
|
@@ -36,36 +55,95 @@ export class PageCollector {
|
|
|
36
55
|
if (this.storage.has(page)) {
|
|
37
56
|
return;
|
|
38
57
|
}
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
58
|
+
const idGenerator = createIdGenerator();
|
|
59
|
+
const storedLists = [[]];
|
|
60
|
+
this.storage.set(page, storedLists);
|
|
61
|
+
const listeners = this.#listenersInitializer(value => {
|
|
62
|
+
const withId = value;
|
|
63
|
+
withId[stableIdSymbol] = idGenerator();
|
|
64
|
+
const navigations = this.storage.get(page) ?? [[]];
|
|
65
|
+
navigations[0].push(withId);
|
|
66
|
+
});
|
|
67
|
+
listeners['framenavigated'] = (frame) => {
|
|
68
|
+
// Only split the storage on main frame navigation
|
|
43
69
|
if (frame !== page.mainFrame()) {
|
|
44
70
|
return;
|
|
45
71
|
}
|
|
46
|
-
this.
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
72
|
+
this.splitAfterNavigation(page);
|
|
73
|
+
};
|
|
74
|
+
for (const [name, listener] of Object.entries(listeners)) {
|
|
75
|
+
page.on(name, listener);
|
|
76
|
+
}
|
|
77
|
+
this.#listeners.set(page, listeners);
|
|
51
78
|
}
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
if (
|
|
55
|
-
|
|
56
|
-
collection.length = 0;
|
|
79
|
+
splitAfterNavigation(page) {
|
|
80
|
+
const navigations = this.storage.get(page);
|
|
81
|
+
if (!navigations) {
|
|
82
|
+
return;
|
|
57
83
|
}
|
|
84
|
+
// Add the latest navigation first
|
|
85
|
+
navigations.unshift([]);
|
|
86
|
+
navigations.splice(this.#maxNavigationSaved);
|
|
58
87
|
}
|
|
59
|
-
|
|
60
|
-
|
|
88
|
+
#cleanupPageDestroyed(page) {
|
|
89
|
+
const listeners = this.#listeners.get(page);
|
|
90
|
+
if (listeners) {
|
|
91
|
+
for (const [name, listener] of Object.entries(listeners)) {
|
|
92
|
+
page.off(name, listener);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
this.storage.delete(page);
|
|
96
|
+
}
|
|
97
|
+
getData(page, includePreservedData) {
|
|
98
|
+
const navigations = this.storage.get(page);
|
|
99
|
+
if (!navigations) {
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
if (!includePreservedData) {
|
|
103
|
+
return navigations[0];
|
|
104
|
+
}
|
|
105
|
+
const data = [];
|
|
106
|
+
for (let index = this.#maxNavigationSaved; index >= 0; index--) {
|
|
107
|
+
if (navigations[index]) {
|
|
108
|
+
data.push(...navigations[index]);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return data;
|
|
112
|
+
}
|
|
113
|
+
getIdForResource(resource) {
|
|
114
|
+
return resource[stableIdSymbol] ?? -1;
|
|
115
|
+
}
|
|
116
|
+
getById(page, stableId) {
|
|
117
|
+
const navigations = this.storage.get(page);
|
|
118
|
+
if (!navigations) {
|
|
119
|
+
throw new Error('No requests found for selected page');
|
|
120
|
+
}
|
|
121
|
+
for (const navigation of navigations) {
|
|
122
|
+
for (const collected of navigation) {
|
|
123
|
+
if (collected[stableIdSymbol] === stableId) {
|
|
124
|
+
return collected;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
throw new Error('Request not found for selected page');
|
|
61
129
|
}
|
|
62
130
|
}
|
|
63
131
|
export class NetworkCollector extends PageCollector {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
132
|
+
constructor(browser, listeners = collect => {
|
|
133
|
+
return {
|
|
134
|
+
request: req => {
|
|
135
|
+
collect(req);
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
}) {
|
|
139
|
+
super(browser, listeners);
|
|
140
|
+
}
|
|
141
|
+
splitAfterNavigation(page) {
|
|
142
|
+
const navigations = this.storage.get(page) ?? [];
|
|
143
|
+
if (!navigations) {
|
|
67
144
|
return;
|
|
68
145
|
}
|
|
146
|
+
const requests = navigations[0];
|
|
69
147
|
const lastRequestIdx = requests.findLastIndex(request => {
|
|
70
148
|
return request.frame() === page.mainFrame()
|
|
71
149
|
? request.isNavigationRequest()
|
|
@@ -74,6 +152,12 @@ export class NetworkCollector extends PageCollector {
|
|
|
74
152
|
// Keep all requests since the last navigation request including that
|
|
75
153
|
// navigation request itself.
|
|
76
154
|
// Keep the reference
|
|
77
|
-
|
|
155
|
+
if (lastRequestIdx !== -1) {
|
|
156
|
+
const fromCurrentNavigation = requests.splice(lastRequestIdx);
|
|
157
|
+
navigations.unshift(fromCurrentNavigation);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
navigations.unshift([]);
|
|
161
|
+
}
|
|
78
162
|
}
|
|
79
163
|
}
|
package/build/src/browser.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import fs from 'node:fs';
|
|
7
7
|
import os from 'node:os';
|
|
8
8
|
import path from 'node:path';
|
|
9
|
-
import puppeteer from '
|
|
9
|
+
import { puppeteer } from './third_party/index.js';
|
|
10
10
|
let browser;
|
|
11
11
|
function makeTargetFilter(devtools) {
|
|
12
12
|
const ignoredPrefixes = new Set([
|
|
@@ -33,12 +33,24 @@ export async function ensureBrowserConnected(options) {
|
|
|
33
33
|
if (browser?.connected) {
|
|
34
34
|
return browser;
|
|
35
35
|
}
|
|
36
|
-
|
|
36
|
+
const connectOptions = {
|
|
37
37
|
targetFilter: makeTargetFilter(options.devtools),
|
|
38
|
-
browserURL: options.browserURL,
|
|
39
38
|
defaultViewport: null,
|
|
40
39
|
handleDevToolsAsPage: options.devtools,
|
|
41
|
-
}
|
|
40
|
+
};
|
|
41
|
+
if (options.wsEndpoint) {
|
|
42
|
+
connectOptions.browserWSEndpoint = options.wsEndpoint;
|
|
43
|
+
if (options.wsHeaders) {
|
|
44
|
+
connectOptions.headers = options.wsHeaders;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
else if (options.browserURL) {
|
|
48
|
+
connectOptions.browserURL = options.browserURL;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
throw new Error('Either browserURL or wsEndpoint must be provided');
|
|
52
|
+
}
|
|
53
|
+
browser = await puppeteer.connect(connectOptions);
|
|
42
54
|
return browser;
|
|
43
55
|
}
|
|
44
56
|
export async function launch(options) {
|
|
@@ -101,9 +113,7 @@ export async function launch(options) {
|
|
|
101
113
|
}
|
|
102
114
|
catch (error) {
|
|
103
115
|
if (userDataDir &&
|
|
104
|
-
|
|
105
|
-
error.message.includes('Target closed') ||
|
|
106
|
-
error.message.includes('Connection closed'))) {
|
|
116
|
+
error.message.includes('The browser is already running')) {
|
|
107
117
|
throw new Error(`The browser is already running for ${userDataDir}. Use --isolated to run multiple browser instances.`, {
|
|
108
118
|
cause: error,
|
|
109
119
|
});
|