@flrande/browserctl 0.5.0-dev.22.1 → 0.6.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/dist/client.d.ts +34 -0
- package/dist/client.js +138 -0
- package/dist/commandRegistry.d.ts +16 -0
- package/dist/commandRegistry.js +21 -0
- package/dist/help.d.ts +4 -0
- package/dist/help.js +24 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +23 -0
- package/dist/runCli.d.ts +5 -0
- package/dist/runCli.js +170 -0
- package/package.json +32 -59
- package/INSTALL-CN.md +0 -92
- package/INSTALL.md +0 -92
- package/LICENSE +0 -21
- package/README-CN.md +0 -69
- package/README.md +0 -69
- package/apps/browserctl/src/commands/a11y-snapshot.ts +0 -20
- package/apps/browserctl/src/commands/act.test.ts +0 -71
- package/apps/browserctl/src/commands/act.ts +0 -64
- package/apps/browserctl/src/commands/command-wrappers.test.ts +0 -688
- package/apps/browserctl/src/commands/common.test.ts +0 -87
- package/apps/browserctl/src/commands/common.ts +0 -191
- package/apps/browserctl/src/commands/console-list.test.ts +0 -102
- package/apps/browserctl/src/commands/console-list.ts +0 -108
- package/apps/browserctl/src/commands/cookie-clear.ts +0 -18
- package/apps/browserctl/src/commands/cookie-get.ts +0 -18
- package/apps/browserctl/src/commands/cookie-set.ts +0 -22
- package/apps/browserctl/src/commands/dialog-arm.ts +0 -20
- package/apps/browserctl/src/commands/dom-query-all.ts +0 -18
- package/apps/browserctl/src/commands/dom-query.ts +0 -18
- package/apps/browserctl/src/commands/download-trigger.ts +0 -22
- package/apps/browserctl/src/commands/download-wait.test.ts +0 -67
- package/apps/browserctl/src/commands/download-wait.ts +0 -27
- package/apps/browserctl/src/commands/element-screenshot.ts +0 -20
- package/apps/browserctl/src/commands/frame-list.ts +0 -16
- package/apps/browserctl/src/commands/frame-snapshot.ts +0 -18
- package/apps/browserctl/src/commands/har-export.test.ts +0 -112
- package/apps/browserctl/src/commands/har-export.ts +0 -120
- package/apps/browserctl/src/commands/memory-delete.ts +0 -20
- package/apps/browserctl/src/commands/memory-inspect.ts +0 -20
- package/apps/browserctl/src/commands/memory-list.ts +0 -90
- package/apps/browserctl/src/commands/memory-mode-set.ts +0 -29
- package/apps/browserctl/src/commands/memory-purge.ts +0 -16
- package/apps/browserctl/src/commands/memory-resolve.ts +0 -56
- package/apps/browserctl/src/commands/memory-status.ts +0 -16
- package/apps/browserctl/src/commands/memory-ttl-set.ts +0 -28
- package/apps/browserctl/src/commands/memory-upsert.ts +0 -142
- package/apps/browserctl/src/commands/network-list.test.ts +0 -110
- package/apps/browserctl/src/commands/network-list.ts +0 -112
- package/apps/browserctl/src/commands/network-wait-for.test.ts +0 -90
- package/apps/browserctl/src/commands/network-wait-for.ts +0 -100
- package/apps/browserctl/src/commands/profile-list.ts +0 -16
- package/apps/browserctl/src/commands/profile-use.ts +0 -18
- package/apps/browserctl/src/commands/response-body.ts +0 -24
- package/apps/browserctl/src/commands/screenshot.ts +0 -16
- package/apps/browserctl/src/commands/session-drop.test.ts +0 -36
- package/apps/browserctl/src/commands/session-drop.ts +0 -16
- package/apps/browserctl/src/commands/session-list.test.ts +0 -81
- package/apps/browserctl/src/commands/session-list.ts +0 -70
- package/apps/browserctl/src/commands/snapshot.ts +0 -16
- package/apps/browserctl/src/commands/status.ts +0 -10
- package/apps/browserctl/src/commands/storage-get.ts +0 -20
- package/apps/browserctl/src/commands/storage-set.ts +0 -22
- package/apps/browserctl/src/commands/tab-close.ts +0 -20
- package/apps/browserctl/src/commands/tab-focus.ts +0 -20
- package/apps/browserctl/src/commands/tab-open.ts +0 -19
- package/apps/browserctl/src/commands/tabs.ts +0 -13
- package/apps/browserctl/src/commands/trace-get.test.ts +0 -61
- package/apps/browserctl/src/commands/trace-get.ts +0 -62
- package/apps/browserctl/src/commands/upload-arm.ts +0 -26
- package/apps/browserctl/src/commands/wait-element.test.ts +0 -80
- package/apps/browserctl/src/commands/wait-element.ts +0 -76
- package/apps/browserctl/src/commands/wait-text.test.ts +0 -110
- package/apps/browserctl/src/commands/wait-text.ts +0 -93
- package/apps/browserctl/src/commands/wait-url.test.ts +0 -80
- package/apps/browserctl/src/commands/wait-url.ts +0 -76
- package/apps/browserctl/src/daemon-client.test.ts +0 -512
- package/apps/browserctl/src/daemon-client.ts +0 -632
- package/apps/browserctl/src/e2e.test.ts +0 -103
- package/apps/browserctl/src/main.dispatch.test.ts +0 -461
- package/apps/browserctl/src/main.test.ts +0 -334
- package/apps/browserctl/src/main.ts +0 -957
- package/apps/browserctl/src/smoke.e2e.test.ts +0 -97
- package/apps/browserctl/src/test-port.ts +0 -26
- package/apps/browserd/src/bootstrap.ts +0 -432
- package/apps/browserd/src/chrome-relay-extension-bridge.test.ts +0 -250
- package/apps/browserd/src/chrome-relay-extension-bridge.ts +0 -506
- package/apps/browserd/src/container.ts +0 -3088
- package/apps/browserd/src/main.test.ts +0 -1522
- package/apps/browserd/src/main.ts +0 -7
- package/apps/browserd/src/test-port.ts +0 -26
- package/apps/browserd/src/tool-matrix.test.ts +0 -887
- package/bin/browserctl.cjs +0 -21
- package/bin/browserd.cjs +0 -21
- package/extensions/chrome-relay/README-CN.md +0 -39
- package/extensions/chrome-relay/README.md +0 -39
- package/extensions/chrome-relay/background.js +0 -1687
- package/extensions/chrome-relay/manifest.json +0 -15
- package/extensions/chrome-relay/popup.html +0 -369
- package/extensions/chrome-relay/popup.js +0 -972
- package/packages/core/src/bootstrap.test.ts +0 -10
- package/packages/core/src/driver-registry.test.ts +0 -45
- package/packages/core/src/driver-registry.ts +0 -22
- package/packages/core/src/driver.ts +0 -47
- package/packages/core/src/index.ts +0 -6
- package/packages/core/src/navigation-memory.test.ts +0 -259
- package/packages/core/src/navigation-memory.ts +0 -360
- package/packages/core/src/ref-cache.test.ts +0 -61
- package/packages/core/src/ref-cache.ts +0 -28
- package/packages/core/src/session-store.test.ts +0 -82
- package/packages/core/src/session-store.ts +0 -138
- package/packages/core/src/types.ts +0 -9
- package/packages/driver-chrome-relay/src/chrome-relay-driver.test.ts +0 -744
- package/packages/driver-chrome-relay/src/chrome-relay-driver.ts +0 -2429
- package/packages/driver-chrome-relay/src/chrome-relay-extension-runtime.test.ts +0 -264
- package/packages/driver-chrome-relay/src/chrome-relay-extension-runtime.ts +0 -521
- package/packages/driver-chrome-relay/src/index.ts +0 -26
- package/packages/driver-managed/src/index.ts +0 -22
- package/packages/driver-managed/src/managed-driver.test.ts +0 -183
- package/packages/driver-managed/src/managed-driver.ts +0 -341
- package/packages/driver-managed/src/managed-local-driver.test.ts +0 -608
- package/packages/driver-managed/src/managed-local-driver.ts +0 -2243
- package/packages/driver-remote-cdp/src/index.ts +0 -19
- package/packages/driver-remote-cdp/src/remote-cdp-driver.test.ts +0 -727
- package/packages/driver-remote-cdp/src/remote-cdp-driver.ts +0 -2264
- package/packages/protocol/src/envelope.test.ts +0 -25
- package/packages/protocol/src/envelope.ts +0 -31
- package/packages/protocol/src/errors.test.ts +0 -17
- package/packages/protocol/src/errors.ts +0 -11
- package/packages/protocol/src/index.ts +0 -3
- package/packages/protocol/src/tools.ts +0 -3
- package/packages/transport-mcp-stdio/src/index.ts +0 -3
- package/packages/transport-mcp-stdio/src/sdk-server.ts +0 -139
- package/packages/transport-mcp-stdio/src/server.test.ts +0 -281
- package/packages/transport-mcp-stdio/src/server.ts +0 -183
- package/packages/transport-mcp-stdio/src/tool-map.ts +0 -84
|
@@ -1,2243 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
BrowserDriver,
|
|
3
|
-
BrowserDriverScreenshot,
|
|
4
|
-
DriverObject
|
|
5
|
-
} from "../../core/src/driver";
|
|
6
|
-
import type { ProfileId, TargetId } from "../../core/src/types";
|
|
7
|
-
|
|
8
|
-
export type ManagedLocalBrowserName = "chromium" | "firefox" | "webkit";
|
|
9
|
-
|
|
10
|
-
export type ManagedLocalBrowserLaunchConfig = {
|
|
11
|
-
browserName: ManagedLocalBrowserName;
|
|
12
|
-
headless: boolean;
|
|
13
|
-
channel?: string;
|
|
14
|
-
executablePath?: string;
|
|
15
|
-
launchTimeoutMs?: number;
|
|
16
|
-
args: string[];
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export type ManagedLocalDriverConfig = {
|
|
20
|
-
runtime?: ManagedLocalDriverRuntime;
|
|
21
|
-
browserName?: ManagedLocalBrowserName;
|
|
22
|
-
headless?: boolean;
|
|
23
|
-
channel?: string;
|
|
24
|
-
executablePath?: string;
|
|
25
|
-
launchTimeoutMs?: number;
|
|
26
|
-
args?: string[];
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
export type ManagedLocalDriverStatus = {
|
|
30
|
-
kind: "managed-local";
|
|
31
|
-
connected: boolean;
|
|
32
|
-
launched: boolean;
|
|
33
|
-
browserName: ManagedLocalBrowserName;
|
|
34
|
-
headless: boolean;
|
|
35
|
-
channel?: string;
|
|
36
|
-
executablePath?: string;
|
|
37
|
-
launchTimeoutMs?: number;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
export type ManagedLocalConsoleEntryLocation = {
|
|
41
|
-
url?: string;
|
|
42
|
-
lineNumber?: number;
|
|
43
|
-
columnNumber?: number;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
export type ManagedLocalConsoleEntry = {
|
|
47
|
-
type: string;
|
|
48
|
-
text: string;
|
|
49
|
-
timestamp: string;
|
|
50
|
-
location?: ManagedLocalConsoleEntryLocation;
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
export type ManagedLocalNetworkRequestSummary = {
|
|
54
|
-
requestId: string;
|
|
55
|
-
url: string;
|
|
56
|
-
method?: string;
|
|
57
|
-
resourceType?: string;
|
|
58
|
-
status?: number;
|
|
59
|
-
timestamp: string;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
export type ManagedLocalNetworkResponseBody = {
|
|
63
|
-
body: string;
|
|
64
|
-
encoding: "utf8" | "base64";
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
export type ManagedLocalSnapshot = {
|
|
68
|
-
kind: "managed-local";
|
|
69
|
-
profile: ProfileId;
|
|
70
|
-
targetId: TargetId;
|
|
71
|
-
hasTarget: boolean;
|
|
72
|
-
url?: string;
|
|
73
|
-
title?: string;
|
|
74
|
-
html?: string;
|
|
75
|
-
requestSummaries?: ManagedLocalNetworkRequestSummary[];
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
export type ManagedLocalScreenshot = {
|
|
79
|
-
kind: "managed-local";
|
|
80
|
-
profile: ProfileId;
|
|
81
|
-
targetId: TargetId;
|
|
82
|
-
hasTarget: boolean;
|
|
83
|
-
mimeType?: string;
|
|
84
|
-
encoding?: "base64";
|
|
85
|
-
imageBase64?: string;
|
|
86
|
-
width?: number;
|
|
87
|
-
height?: number;
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
export type ManagedLocalTelemetryDriverExtensions = {
|
|
91
|
-
getConsoleEntries?(targetId: TargetId, profile?: ProfileId): ManagedLocalConsoleEntry[];
|
|
92
|
-
getNetworkResponseBody?(
|
|
93
|
-
requestId: string,
|
|
94
|
-
targetId: TargetId,
|
|
95
|
-
profile?: ProfileId
|
|
96
|
-
): ManagedLocalNetworkResponseBody | undefined;
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
export type ManagedLocalLocator = {
|
|
100
|
-
click(): Promise<void>;
|
|
101
|
-
fill(value: string): Promise<void>;
|
|
102
|
-
type(value: string): Promise<void>;
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
export type ManagedLocalKeyboard = {
|
|
106
|
-
press(key: string): Promise<void>;
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
export type ManagedLocalPage = {
|
|
110
|
-
goto(url: string): Promise<void>;
|
|
111
|
-
bringToFront(): Promise<void>;
|
|
112
|
-
close(): Promise<void>;
|
|
113
|
-
url(): string;
|
|
114
|
-
title(): Promise<string>;
|
|
115
|
-
content(): Promise<string>;
|
|
116
|
-
screenshot?(options?: Record<string, unknown>): Promise<unknown>;
|
|
117
|
-
locator(selector: string): ManagedLocalLocator;
|
|
118
|
-
route?(url: string, handler: ManagedLocalNetworkMockHandler): Promise<void>;
|
|
119
|
-
unroute?(url: string, handler?: ManagedLocalNetworkMockHandler): Promise<void>;
|
|
120
|
-
keyboard?: ManagedLocalKeyboard;
|
|
121
|
-
on?(eventName: string, listener: (payload: unknown) => unknown): void;
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
export type ManagedLocalBrowserContext = {
|
|
125
|
-
newPage(): Promise<ManagedLocalPage>;
|
|
126
|
-
close(): Promise<void>;
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
export type ManagedLocalBrowser = {
|
|
130
|
-
newContext(): Promise<ManagedLocalBrowserContext>;
|
|
131
|
-
close(): Promise<void>;
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
export type ManagedLocalDriverRuntime = {
|
|
135
|
-
launch(config: ManagedLocalBrowserLaunchConfig): Promise<ManagedLocalBrowser>;
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
const DEFAULT_PROFILE_ID: ProfileId = "profile:managed-local:default";
|
|
139
|
-
const DEFAULT_BROWSER_NAME: ManagedLocalBrowserName = "chromium";
|
|
140
|
-
const DEFAULT_HEADLESS = true;
|
|
141
|
-
|
|
142
|
-
type ManagedLocalTab = {
|
|
143
|
-
url: string;
|
|
144
|
-
page: ManagedLocalPage;
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
type ManagedLocalProfileState = {
|
|
148
|
-
nextTargetNumber: number;
|
|
149
|
-
tabs: Map<TargetId, ManagedLocalTab>;
|
|
150
|
-
tabOrder: TargetId[];
|
|
151
|
-
focusedTargetId?: TargetId;
|
|
152
|
-
context?: ManagedLocalBrowserContext;
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
type ManagedLocalTargetOperationState = {
|
|
156
|
-
uploadFiles: string[];
|
|
157
|
-
dialogArmedCount: number;
|
|
158
|
-
triggerCount: number;
|
|
159
|
-
nextNetworkMockNumber: number;
|
|
160
|
-
networkMocks: Map<string, ManagedLocalNetworkMockBinding>;
|
|
161
|
-
requestedDownloadPath?: string;
|
|
162
|
-
downloadInFlight?: Promise<ManagedLocalDownloadArtifact>;
|
|
163
|
-
latestDownload?: ManagedLocalDownloadArtifact;
|
|
164
|
-
latestRawDownload?: unknown;
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
type ManagedLocalDownloadArtifact = {
|
|
168
|
-
path: string;
|
|
169
|
-
suggestedFilename?: string;
|
|
170
|
-
url?: string;
|
|
171
|
-
mimeType?: string;
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
type ManagedLocalNetworkMockHandler = (...args: unknown[]) => Promise<void>;
|
|
175
|
-
type ManagedLocalNetworkMockBinding = {
|
|
176
|
-
urlPattern: string;
|
|
177
|
-
handler: ManagedLocalNetworkMockHandler;
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
type ManagedLocalTargetTelemetryState = {
|
|
181
|
-
consoleEntries: ManagedLocalConsoleEntry[];
|
|
182
|
-
requestSummaries: ManagedLocalNetworkRequestSummary[];
|
|
183
|
-
networkResponseBodies: Map<string, ManagedLocalNetworkResponseBody>;
|
|
184
|
-
nextRequestNumber: number;
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
function resolveProfileId(profile?: ProfileId): ProfileId {
|
|
188
|
-
return profile ?? DEFAULT_PROFILE_ID;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
function createTargetId(profileId: ProfileId, targetNumber: number): TargetId {
|
|
192
|
-
return `target:managed-local:${profileId}:${targetNumber}`;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
function createProfileState(): ManagedLocalProfileState {
|
|
196
|
-
return {
|
|
197
|
-
nextTargetNumber: 1,
|
|
198
|
-
tabs: new Map<TargetId, ManagedLocalTab>(),
|
|
199
|
-
tabOrder: []
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
function createTargetOperationState(): ManagedLocalTargetOperationState {
|
|
204
|
-
return {
|
|
205
|
-
uploadFiles: [],
|
|
206
|
-
dialogArmedCount: 0,
|
|
207
|
-
triggerCount: 0,
|
|
208
|
-
nextNetworkMockNumber: 1,
|
|
209
|
-
networkMocks: new Map<string, ManagedLocalNetworkMockBinding>()
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function createTargetTelemetryState(): ManagedLocalTargetTelemetryState {
|
|
214
|
-
return {
|
|
215
|
-
consoleEntries: [],
|
|
216
|
-
requestSummaries: [],
|
|
217
|
-
networkResponseBodies: new Map<string, ManagedLocalNetworkResponseBody>(),
|
|
218
|
-
nextRequestNumber: 1
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
function createDownloadPath(profileId: ProfileId, targetId: TargetId, triggerCount: number): string {
|
|
223
|
-
return `managed-local-${encodeURIComponent(profileId)}-${encodeURIComponent(targetId)}-${triggerCount}.bin`;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
function createRequestId(profileId: ProfileId, targetId: TargetId, requestNumber: number): string {
|
|
227
|
-
return `request:managed-local:${encodeURIComponent(profileId)}:${encodeURIComponent(targetId)}:${requestNumber}`;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
function toErrorMessage(error: unknown): string {
|
|
231
|
-
return error instanceof Error ? error.message : "Unexpected managed-local driver failure.";
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
function readActionPayloadString(payload: DriverObject | undefined, key: string): string | undefined {
|
|
235
|
-
const value = payload?.[key];
|
|
236
|
-
return typeof value === "string" && value.trim().length > 0 ? value : undefined;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
function readActionPayloadNumber(payload: DriverObject | undefined, key: string): number | undefined {
|
|
240
|
-
const value = payload?.[key];
|
|
241
|
-
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
function isObjectRecord(value: unknown): value is Record<string, unknown> {
|
|
245
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
function readObjectMethod<TValue>(value: unknown, methodName: string): TValue | undefined {
|
|
249
|
-
if (typeof value !== "object" || value === null) {
|
|
250
|
-
return undefined;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
const method = (value as Record<string, unknown>)[methodName];
|
|
254
|
-
if (typeof method !== "function") {
|
|
255
|
-
return undefined;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
try {
|
|
259
|
-
return (method as () => TValue).call(value);
|
|
260
|
-
} catch {
|
|
261
|
-
return undefined;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
function getObjectMethod(
|
|
266
|
-
value: unknown,
|
|
267
|
-
methodName: string
|
|
268
|
-
): ((...args: unknown[]) => unknown) | undefined {
|
|
269
|
-
if (typeof value !== "object" || value === null) {
|
|
270
|
-
return undefined;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
const method = (value as Record<string, unknown>)[methodName];
|
|
274
|
-
return typeof method === "function" ? (method as (...args: unknown[]) => unknown) : undefined;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
function readObjectStringProperty(value: unknown, key: string): string | undefined {
|
|
278
|
-
if (typeof value !== "object" || value === null) {
|
|
279
|
-
return undefined;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
const property = (value as Record<string, unknown>)[key];
|
|
283
|
-
return typeof property === "string" ? property : undefined;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
function readObjectNumberProperty(value: unknown, key: string): number | undefined {
|
|
287
|
-
if (typeof value !== "object" || value === null) {
|
|
288
|
-
return undefined;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
const property = (value as Record<string, unknown>)[key];
|
|
292
|
-
return typeof property === "number" ? property : undefined;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
function readConsoleEntry(value: unknown): ManagedLocalConsoleEntry {
|
|
296
|
-
const locationValue = readObjectMethod<unknown>(value, "location");
|
|
297
|
-
const locationUrl = readObjectStringProperty(locationValue, "url");
|
|
298
|
-
const locationLineNumber = readObjectNumberProperty(locationValue, "lineNumber");
|
|
299
|
-
const locationColumnNumber = readObjectNumberProperty(locationValue, "columnNumber");
|
|
300
|
-
|
|
301
|
-
const hasLocation =
|
|
302
|
-
locationUrl !== undefined || locationLineNumber !== undefined || locationColumnNumber !== undefined;
|
|
303
|
-
|
|
304
|
-
return {
|
|
305
|
-
type: readObjectMethod<string>(value, "type") ?? "log",
|
|
306
|
-
text: readObjectMethod<string>(value, "text") ?? "",
|
|
307
|
-
timestamp: new Date().toISOString(),
|
|
308
|
-
...(hasLocation
|
|
309
|
-
? {
|
|
310
|
-
location: {
|
|
311
|
-
...(locationUrl !== undefined ? { url: locationUrl } : {}),
|
|
312
|
-
...(locationLineNumber !== undefined ? { lineNumber: locationLineNumber } : {}),
|
|
313
|
-
...(locationColumnNumber !== undefined ? { columnNumber: locationColumnNumber } : {})
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
: {})
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
function readNetworkSummary(
|
|
321
|
-
value: unknown,
|
|
322
|
-
requestId: string
|
|
323
|
-
): ManagedLocalNetworkRequestSummary {
|
|
324
|
-
const requestValue = readObjectMethod<unknown>(value, "request");
|
|
325
|
-
const method = readObjectMethod<string>(requestValue, "method");
|
|
326
|
-
const resourceType = readObjectMethod<string>(requestValue, "resourceType");
|
|
327
|
-
const status = readObjectMethod<number>(value, "status");
|
|
328
|
-
|
|
329
|
-
return {
|
|
330
|
-
requestId,
|
|
331
|
-
url: readObjectMethod<string>(value, "url") ?? "",
|
|
332
|
-
...(typeof method === "string" ? { method } : {}),
|
|
333
|
-
...(typeof resourceType === "string" ? { resourceType } : {}),
|
|
334
|
-
...(typeof status === "number" ? { status } : {}),
|
|
335
|
-
timestamp: new Date().toISOString()
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
function toBase64(value: ArrayBuffer | Uint8Array): string {
|
|
340
|
-
const bytes = value instanceof Uint8Array ? value : new Uint8Array(value);
|
|
341
|
-
return Buffer.from(bytes).toString("base64");
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
function parseScreenshotBase64(
|
|
345
|
-
value: string
|
|
346
|
-
): { mimeType: string; imageBase64: string } | undefined {
|
|
347
|
-
const trimmedValue = value.trim();
|
|
348
|
-
if (trimmedValue.length === 0) {
|
|
349
|
-
return undefined;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
const dataUrlMatch = /^data:([^;,]+);base64,(.+)$/i.exec(trimmedValue);
|
|
353
|
-
if (dataUrlMatch !== null) {
|
|
354
|
-
const mimeType = dataUrlMatch[1] ?? "image/png";
|
|
355
|
-
const imageBase64 = dataUrlMatch[2] ?? "";
|
|
356
|
-
if (imageBase64.length === 0) {
|
|
357
|
-
return undefined;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
return {
|
|
361
|
-
mimeType,
|
|
362
|
-
imageBase64
|
|
363
|
-
};
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
return {
|
|
367
|
-
mimeType: "image/png",
|
|
368
|
-
imageBase64: trimmedValue
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
type ManagedLocalScreenshotImage = {
|
|
373
|
-
mimeType: string;
|
|
374
|
-
encoding: "base64";
|
|
375
|
-
imageBase64: string;
|
|
376
|
-
width?: number;
|
|
377
|
-
height?: number;
|
|
378
|
-
};
|
|
379
|
-
|
|
380
|
-
function toOptionalPositiveNumber(value: unknown): number | undefined {
|
|
381
|
-
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : undefined;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
function readScreenshotImage(value: unknown): ManagedLocalScreenshotImage | undefined {
|
|
385
|
-
if (value instanceof Uint8Array || value instanceof ArrayBuffer) {
|
|
386
|
-
return {
|
|
387
|
-
mimeType: "image/png",
|
|
388
|
-
encoding: "base64",
|
|
389
|
-
imageBase64: toBase64(value)
|
|
390
|
-
};
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
if (typeof value === "string") {
|
|
394
|
-
const parsed = parseScreenshotBase64(value);
|
|
395
|
-
if (parsed === undefined) {
|
|
396
|
-
return undefined;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
return {
|
|
400
|
-
mimeType: parsed.mimeType,
|
|
401
|
-
encoding: "base64",
|
|
402
|
-
imageBase64: parsed.imageBase64
|
|
403
|
-
};
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
if (typeof value !== "object" || value === null) {
|
|
407
|
-
return undefined;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
const imageBase64 = readObjectStringProperty(value, "imageBase64");
|
|
411
|
-
if (imageBase64 === undefined || imageBase64.trim().length === 0) {
|
|
412
|
-
return undefined;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
const rawEncoding = readObjectStringProperty(value, "encoding");
|
|
416
|
-
if (rawEncoding !== undefined && rawEncoding !== "base64") {
|
|
417
|
-
return undefined;
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
const mimeType = readObjectStringProperty(value, "mimeType") ?? "image/png";
|
|
421
|
-
const width = toOptionalPositiveNumber(readObjectNumberProperty(value, "width"));
|
|
422
|
-
const height = toOptionalPositiveNumber(readObjectNumberProperty(value, "height"));
|
|
423
|
-
|
|
424
|
-
return {
|
|
425
|
-
mimeType,
|
|
426
|
-
encoding: "base64",
|
|
427
|
-
imageBase64: imageBase64.trim(),
|
|
428
|
-
...(width !== undefined ? { width } : {}),
|
|
429
|
-
...(height !== undefined ? { height } : {})
|
|
430
|
-
};
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
async function capturePageScreenshot(page: ManagedLocalPage): Promise<ManagedLocalScreenshotImage> {
|
|
434
|
-
const screenshotMethod = getObjectMethod(page, "screenshot");
|
|
435
|
-
if (typeof screenshotMethod !== "function") {
|
|
436
|
-
throw new Error("Screenshot is not supported by this driver runtime.");
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
const screenshot = await screenshotMethod.call(page, {
|
|
440
|
-
type: "png"
|
|
441
|
-
});
|
|
442
|
-
const parsedScreenshot = readScreenshotImage(screenshot);
|
|
443
|
-
if (parsedScreenshot === undefined) {
|
|
444
|
-
throw new Error("Unexpected screenshot payload from driver runtime.");
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
return parsedScreenshot;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
type ManagedLocalCookie = {
|
|
451
|
-
name: string;
|
|
452
|
-
value: string;
|
|
453
|
-
domain?: string;
|
|
454
|
-
path?: string;
|
|
455
|
-
expires?: number;
|
|
456
|
-
httpOnly?: boolean;
|
|
457
|
-
secure?: boolean;
|
|
458
|
-
sameSite?: string;
|
|
459
|
-
url?: string;
|
|
460
|
-
};
|
|
461
|
-
|
|
462
|
-
function normalizeCookies(value: unknown): ManagedLocalCookie[] {
|
|
463
|
-
if (!Array.isArray(value)) {
|
|
464
|
-
return [];
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
const cookies: ManagedLocalCookie[] = [];
|
|
468
|
-
for (const item of value) {
|
|
469
|
-
if (!isObjectRecord(item)) {
|
|
470
|
-
continue;
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
const name = readObjectStringProperty(item, "name");
|
|
474
|
-
const cookieValue = readObjectStringProperty(item, "value");
|
|
475
|
-
if (name === undefined || cookieValue === undefined) {
|
|
476
|
-
continue;
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
const domain = readObjectStringProperty(item, "domain");
|
|
480
|
-
const path = readObjectStringProperty(item, "path");
|
|
481
|
-
const sameSite = readObjectStringProperty(item, "sameSite");
|
|
482
|
-
const url = readObjectStringProperty(item, "url");
|
|
483
|
-
const expiresRaw = readObjectNumberProperty(item, "expires");
|
|
484
|
-
const httpOnlyRaw = item.httpOnly;
|
|
485
|
-
const secureRaw = item.secure;
|
|
486
|
-
|
|
487
|
-
cookies.push({
|
|
488
|
-
name,
|
|
489
|
-
value: cookieValue,
|
|
490
|
-
...(domain !== undefined ? { domain } : {}),
|
|
491
|
-
...(path !== undefined ? { path } : {}),
|
|
492
|
-
...(typeof expiresRaw === "number" && Number.isFinite(expiresRaw) ? { expires: expiresRaw } : {}),
|
|
493
|
-
...(typeof httpOnlyRaw === "boolean" ? { httpOnly: httpOnlyRaw } : {}),
|
|
494
|
-
...(typeof secureRaw === "boolean" ? { secure: secureRaw } : {}),
|
|
495
|
-
...(sameSite !== undefined ? { sameSite } : {}),
|
|
496
|
-
...(url !== undefined ? { url } : {})
|
|
497
|
-
});
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
return cookies;
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
function toCookieInput(cookie: ManagedLocalCookie): Record<string, unknown> | undefined {
|
|
504
|
-
const base: Record<string, unknown> = {
|
|
505
|
-
name: cookie.name,
|
|
506
|
-
value: cookie.value
|
|
507
|
-
};
|
|
508
|
-
|
|
509
|
-
if (cookie.url !== undefined) {
|
|
510
|
-
return {
|
|
511
|
-
...base,
|
|
512
|
-
url: cookie.url,
|
|
513
|
-
...(cookie.expires !== undefined ? { expires: cookie.expires } : {}),
|
|
514
|
-
...(cookie.httpOnly !== undefined ? { httpOnly: cookie.httpOnly } : {}),
|
|
515
|
-
...(cookie.secure !== undefined ? { secure: cookie.secure } : {}),
|
|
516
|
-
...(cookie.sameSite !== undefined ? { sameSite: cookie.sameSite } : {})
|
|
517
|
-
};
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
if (cookie.domain !== undefined && cookie.path !== undefined) {
|
|
521
|
-
return {
|
|
522
|
-
...base,
|
|
523
|
-
domain: cookie.domain,
|
|
524
|
-
path: cookie.path,
|
|
525
|
-
...(cookie.expires !== undefined ? { expires: cookie.expires } : {}),
|
|
526
|
-
...(cookie.httpOnly !== undefined ? { httpOnly: cookie.httpOnly } : {}),
|
|
527
|
-
...(cookie.secure !== undefined ? { secure: cookie.secure } : {}),
|
|
528
|
-
...(cookie.sameSite !== undefined ? { sameSite: cookie.sameSite } : {})
|
|
529
|
-
};
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
return undefined;
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
function parseStorageScope(scope: string | undefined): "local" | "session" | undefined {
|
|
536
|
-
if (scope === "local" || scope === "session") {
|
|
537
|
-
return scope;
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
return undefined;
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
function parseFrameIndex(frameId: string | undefined): number | undefined {
|
|
544
|
-
if (typeof frameId !== "string") {
|
|
545
|
-
return undefined;
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
const match = /^frame:(\d+)$/.exec(frameId.trim());
|
|
549
|
-
if (match === null) {
|
|
550
|
-
return undefined;
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
const parsed = Number.parseInt(match[1] ?? "", 10);
|
|
554
|
-
return Number.isFinite(parsed) ? parsed : undefined;
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
async function evaluateInPage<TResult>(
|
|
558
|
-
page: ManagedLocalPage,
|
|
559
|
-
callback: (arg: unknown) => unknown,
|
|
560
|
-
arg: unknown
|
|
561
|
-
): Promise<TResult> {
|
|
562
|
-
const evaluateMethod = getObjectMethod(page, "evaluate");
|
|
563
|
-
if (typeof evaluateMethod !== "function") {
|
|
564
|
-
throw new Error("Driver runtime page does not support evaluate.");
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
const result = await evaluateMethod.call(page, callback, arg);
|
|
568
|
-
return result as TResult;
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
async function readNetworkResponseBody(value: unknown): Promise<ManagedLocalNetworkResponseBody | undefined> {
|
|
572
|
-
const textMethod = getObjectMethod(value, "text");
|
|
573
|
-
if (typeof textMethod === "function") {
|
|
574
|
-
try {
|
|
575
|
-
const textResult = await textMethod.call(value);
|
|
576
|
-
if (typeof textResult === "string") {
|
|
577
|
-
return {
|
|
578
|
-
body: textResult,
|
|
579
|
-
encoding: "utf8"
|
|
580
|
-
};
|
|
581
|
-
}
|
|
582
|
-
} catch {
|
|
583
|
-
// Ignore body extraction failures from optional runtime hooks.
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
const bodyMethod = getObjectMethod(value, "body");
|
|
588
|
-
if (typeof bodyMethod !== "function") {
|
|
589
|
-
return undefined;
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
try {
|
|
593
|
-
const bodyResult = await bodyMethod.call(value);
|
|
594
|
-
if (typeof bodyResult === "string") {
|
|
595
|
-
return {
|
|
596
|
-
body: bodyResult,
|
|
597
|
-
encoding: "utf8"
|
|
598
|
-
};
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
if (bodyResult instanceof Uint8Array || bodyResult instanceof ArrayBuffer) {
|
|
602
|
-
return {
|
|
603
|
-
body: toBase64(bodyResult),
|
|
604
|
-
encoding: "base64"
|
|
605
|
-
};
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
return undefined;
|
|
609
|
-
} catch {
|
|
610
|
-
return undefined;
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
function attachTelemetryListeners(
|
|
615
|
-
page: ManagedLocalPage,
|
|
616
|
-
profileId: ProfileId,
|
|
617
|
-
targetId: TargetId,
|
|
618
|
-
telemetryState: ManagedLocalTargetTelemetryState,
|
|
619
|
-
operationState: ManagedLocalTargetOperationState
|
|
620
|
-
): void {
|
|
621
|
-
if (typeof page.on !== "function") {
|
|
622
|
-
return;
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
page.on("console", (message) => {
|
|
626
|
-
telemetryState.consoleEntries.push(readConsoleEntry(message));
|
|
627
|
-
});
|
|
628
|
-
|
|
629
|
-
page.on("response", async (response) => {
|
|
630
|
-
const requestId = createRequestId(profileId, targetId, telemetryState.nextRequestNumber);
|
|
631
|
-
telemetryState.nextRequestNumber += 1;
|
|
632
|
-
telemetryState.requestSummaries.push(readNetworkSummary(response, requestId));
|
|
633
|
-
|
|
634
|
-
const body = await readNetworkResponseBody(response);
|
|
635
|
-
if (body !== undefined) {
|
|
636
|
-
telemetryState.networkResponseBodies.set(requestId, body);
|
|
637
|
-
}
|
|
638
|
-
});
|
|
639
|
-
|
|
640
|
-
page.on("dialog", async (dialog) => {
|
|
641
|
-
if (operationState.dialogArmedCount <= 0) {
|
|
642
|
-
return;
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
operationState.dialogArmedCount -= 1;
|
|
646
|
-
const acceptMethod = getObjectMethod(dialog, "accept");
|
|
647
|
-
if (typeof acceptMethod === "function") {
|
|
648
|
-
try {
|
|
649
|
-
await acceptMethod.call(dialog);
|
|
650
|
-
return;
|
|
651
|
-
} catch {
|
|
652
|
-
// Fall through to dismiss.
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
const dismissMethod = getObjectMethod(dialog, "dismiss");
|
|
657
|
-
if (typeof dismissMethod === "function") {
|
|
658
|
-
try {
|
|
659
|
-
await dismissMethod.call(dialog);
|
|
660
|
-
} catch {
|
|
661
|
-
// Ignore dialog dismissal failures.
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
});
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
async function readDownloadArtifact(
|
|
668
|
-
value: unknown,
|
|
669
|
-
fallbackPath: string
|
|
670
|
-
): Promise<ManagedLocalDownloadArtifact> {
|
|
671
|
-
let resolvedPath = fallbackPath;
|
|
672
|
-
const pathMethod = getObjectMethod(value, "path");
|
|
673
|
-
if (typeof pathMethod === "function") {
|
|
674
|
-
try {
|
|
675
|
-
const pathResult = await pathMethod.call(value);
|
|
676
|
-
if (typeof pathResult === "string" && pathResult.trim().length > 0) {
|
|
677
|
-
resolvedPath = pathResult;
|
|
678
|
-
}
|
|
679
|
-
} catch {
|
|
680
|
-
// Ignore path() failures and keep fallback path.
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
const saveAsMethod = getObjectMethod(value, "saveAs");
|
|
685
|
-
if (typeof saveAsMethod === "function") {
|
|
686
|
-
try {
|
|
687
|
-
await saveAsMethod.call(value, fallbackPath);
|
|
688
|
-
resolvedPath = fallbackPath;
|
|
689
|
-
} catch {
|
|
690
|
-
// Ignore saveAs failures and keep best-effort path.
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
const suggestedFilename = readObjectMethod<string>(value, "suggestedFilename");
|
|
695
|
-
const url = readObjectMethod<string>(value, "url");
|
|
696
|
-
const mimeType = readObjectMethod<string>(value, "mimeType");
|
|
697
|
-
|
|
698
|
-
return {
|
|
699
|
-
path: resolvedPath,
|
|
700
|
-
...(typeof suggestedFilename === "string" ? { suggestedFilename } : {}),
|
|
701
|
-
...(typeof url === "string" ? { url } : {}),
|
|
702
|
-
...(typeof mimeType === "string" ? { mimeType } : {})
|
|
703
|
-
};
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
function createPlaywrightRuntime(): ManagedLocalDriverRuntime {
|
|
707
|
-
return {
|
|
708
|
-
launch: async (config) => {
|
|
709
|
-
const playwright = await import("playwright-core");
|
|
710
|
-
const browserType =
|
|
711
|
-
config.browserName === "firefox"
|
|
712
|
-
? playwright.firefox
|
|
713
|
-
: config.browserName === "webkit"
|
|
714
|
-
? playwright.webkit
|
|
715
|
-
: playwright.chromium;
|
|
716
|
-
|
|
717
|
-
return browserType.launch({
|
|
718
|
-
headless: config.headless,
|
|
719
|
-
channel: config.channel,
|
|
720
|
-
executablePath: config.executablePath,
|
|
721
|
-
timeout: config.launchTimeoutMs,
|
|
722
|
-
args: config.args.length === 0 ? undefined : config.args
|
|
723
|
-
});
|
|
724
|
-
}
|
|
725
|
-
};
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
export function createManagedLocalDriver(
|
|
729
|
-
config: ManagedLocalDriverConfig = {}
|
|
730
|
-
): BrowserDriver<ManagedLocalDriverStatus, ManagedLocalSnapshot> &
|
|
731
|
-
ManagedLocalTelemetryDriverExtensions &
|
|
732
|
-
BrowserDriverScreenshot<ManagedLocalScreenshot> {
|
|
733
|
-
const launchConfig: ManagedLocalBrowserLaunchConfig = {
|
|
734
|
-
browserName: config.browserName ?? DEFAULT_BROWSER_NAME,
|
|
735
|
-
headless: config.headless ?? DEFAULT_HEADLESS,
|
|
736
|
-
channel: config.channel,
|
|
737
|
-
executablePath: config.executablePath,
|
|
738
|
-
launchTimeoutMs: config.launchTimeoutMs,
|
|
739
|
-
args: config.args === undefined ? [] : [...config.args]
|
|
740
|
-
};
|
|
741
|
-
const runtime = config.runtime ?? createPlaywrightRuntime();
|
|
742
|
-
const profileStates = new Map<ProfileId, ManagedLocalProfileState>();
|
|
743
|
-
const profileInitInFlight = new Map<ProfileId, Promise<ManagedLocalProfileState>>();
|
|
744
|
-
const targetOperationStates = new Map<ProfileId, Map<TargetId, ManagedLocalTargetOperationState>>();
|
|
745
|
-
const targetTelemetryStates = new Map<ProfileId, Map<TargetId, ManagedLocalTargetTelemetryState>>();
|
|
746
|
-
let browser: ManagedLocalBrowser | undefined;
|
|
747
|
-
let launchInFlight: Promise<ManagedLocalBrowser> | undefined;
|
|
748
|
-
|
|
749
|
-
async function ensureBrowser(): Promise<ManagedLocalBrowser> {
|
|
750
|
-
if (browser !== undefined) {
|
|
751
|
-
return browser;
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
if (launchInFlight === undefined) {
|
|
755
|
-
launchInFlight = runtime
|
|
756
|
-
.launch({ ...launchConfig, args: [...launchConfig.args] })
|
|
757
|
-
.then((createdBrowser) => {
|
|
758
|
-
browser = createdBrowser;
|
|
759
|
-
return createdBrowser;
|
|
760
|
-
})
|
|
761
|
-
.finally(() => {
|
|
762
|
-
launchInFlight = undefined;
|
|
763
|
-
});
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
return launchInFlight;
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
async function ensureProfileState(profileId: ProfileId): Promise<ManagedLocalProfileState> {
|
|
770
|
-
const existingState = profileStates.get(profileId);
|
|
771
|
-
if (existingState !== undefined) {
|
|
772
|
-
if (existingState.context === undefined) {
|
|
773
|
-
const inFlight = profileInitInFlight.get(profileId);
|
|
774
|
-
if (inFlight !== undefined) {
|
|
775
|
-
return inFlight;
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
const initPromise = (async () => {
|
|
779
|
-
existingState.context = await (await ensureBrowser()).newContext();
|
|
780
|
-
return existingState;
|
|
781
|
-
})().finally(() => {
|
|
782
|
-
profileInitInFlight.delete(profileId);
|
|
783
|
-
});
|
|
784
|
-
|
|
785
|
-
profileInitInFlight.set(profileId, initPromise);
|
|
786
|
-
return initPromise;
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
return existingState;
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
const createdState = createProfileState();
|
|
793
|
-
profileStates.set(profileId, createdState);
|
|
794
|
-
const initPromise = (async () => {
|
|
795
|
-
createdState.context = await (await ensureBrowser()).newContext();
|
|
796
|
-
return createdState;
|
|
797
|
-
})()
|
|
798
|
-
.catch((error: unknown) => {
|
|
799
|
-
// Roll back profile state if launch/context setup failed before any tab existed.
|
|
800
|
-
if (createdState.tabOrder.length === 0) {
|
|
801
|
-
profileStates.delete(profileId);
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
throw error;
|
|
805
|
-
})
|
|
806
|
-
.finally(() => {
|
|
807
|
-
profileInitInFlight.delete(profileId);
|
|
808
|
-
});
|
|
809
|
-
|
|
810
|
-
profileInitInFlight.set(profileId, initPromise);
|
|
811
|
-
return initPromise;
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
async function closeProfileResources(
|
|
815
|
-
profileId: ProfileId,
|
|
816
|
-
profileState: ManagedLocalProfileState
|
|
817
|
-
): Promise<void> {
|
|
818
|
-
if (profileState.context !== undefined) {
|
|
819
|
-
await profileState.context.close();
|
|
820
|
-
profileState.context = undefined;
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
profileStates.delete(profileId);
|
|
824
|
-
targetOperationStates.delete(profileId);
|
|
825
|
-
targetTelemetryStates.delete(profileId);
|
|
826
|
-
|
|
827
|
-
if (profileStates.size === 0 && browser !== undefined) {
|
|
828
|
-
await browser.close();
|
|
829
|
-
browser = undefined;
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
function requireTargetInProfile(
|
|
834
|
-
profileId: ProfileId,
|
|
835
|
-
targetId: TargetId
|
|
836
|
-
): { profileState: ManagedLocalProfileState; tab: ManagedLocalTab } {
|
|
837
|
-
const profileState = profileStates.get(profileId);
|
|
838
|
-
const tab = profileState?.tabs.get(targetId);
|
|
839
|
-
if (profileState === undefined || tab === undefined) {
|
|
840
|
-
throw new Error(`Unknown targetId: ${targetId} (profile: ${profileId})`);
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
return { profileState, tab };
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
function getOrCreateTargetOperationState(
|
|
847
|
-
profileId: ProfileId,
|
|
848
|
-
targetId: TargetId,
|
|
849
|
-
validateTarget = true
|
|
850
|
-
): ManagedLocalTargetOperationState {
|
|
851
|
-
if (validateTarget) {
|
|
852
|
-
requireTargetInProfile(profileId, targetId);
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
let profileOperationStates = targetOperationStates.get(profileId);
|
|
856
|
-
if (profileOperationStates === undefined) {
|
|
857
|
-
profileOperationStates = new Map<TargetId, ManagedLocalTargetOperationState>();
|
|
858
|
-
targetOperationStates.set(profileId, profileOperationStates);
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
const existingState = profileOperationStates.get(targetId);
|
|
862
|
-
if (existingState !== undefined) {
|
|
863
|
-
return existingState;
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
const createdState = createTargetOperationState();
|
|
867
|
-
profileOperationStates.set(targetId, createdState);
|
|
868
|
-
return createdState;
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
function getOrCreateProfileTelemetryStates(
|
|
872
|
-
profileId: ProfileId
|
|
873
|
-
): Map<TargetId, ManagedLocalTargetTelemetryState> {
|
|
874
|
-
const existingStates = targetTelemetryStates.get(profileId);
|
|
875
|
-
if (existingStates !== undefined) {
|
|
876
|
-
return existingStates;
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
const createdStates = new Map<TargetId, ManagedLocalTargetTelemetryState>();
|
|
880
|
-
targetTelemetryStates.set(profileId, createdStates);
|
|
881
|
-
return createdStates;
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
function findTelemetryState(
|
|
885
|
-
targetId: TargetId,
|
|
886
|
-
profile?: ProfileId
|
|
887
|
-
): ManagedLocalTargetTelemetryState | undefined {
|
|
888
|
-
if (profile !== undefined) {
|
|
889
|
-
return targetTelemetryStates.get(resolveProfileId(profile))?.get(targetId);
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
for (const profileTelemetryStates of targetTelemetryStates.values()) {
|
|
893
|
-
const telemetryState = profileTelemetryStates.get(targetId);
|
|
894
|
-
if (telemetryState !== undefined) {
|
|
895
|
-
return telemetryState;
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
return undefined;
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
function ensureDownloadInFlight(
|
|
903
|
-
profileId: ProfileId,
|
|
904
|
-
targetId: TargetId,
|
|
905
|
-
tab: ManagedLocalTab,
|
|
906
|
-
operationState: ManagedLocalTargetOperationState,
|
|
907
|
-
requestedPath?: string
|
|
908
|
-
): Promise<ManagedLocalDownloadArtifact> {
|
|
909
|
-
if (requestedPath !== undefined) {
|
|
910
|
-
operationState.requestedDownloadPath = requestedPath;
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
if (operationState.downloadInFlight !== undefined) {
|
|
914
|
-
return operationState.downloadInFlight;
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
const fallbackPath =
|
|
918
|
-
operationState.requestedDownloadPath ??
|
|
919
|
-
createDownloadPath(profileId, targetId, Math.max(operationState.triggerCount, 1));
|
|
920
|
-
const waitForEventMethod = getObjectMethod(tab.page, "waitForEvent");
|
|
921
|
-
if (typeof waitForEventMethod !== "function") {
|
|
922
|
-
const artifact: ManagedLocalDownloadArtifact = { path: fallbackPath };
|
|
923
|
-
operationState.requestedDownloadPath = undefined;
|
|
924
|
-
operationState.latestDownload = artifact;
|
|
925
|
-
operationState.latestRawDownload = undefined;
|
|
926
|
-
const resolved = Promise.resolve(artifact);
|
|
927
|
-
operationState.downloadInFlight = resolved;
|
|
928
|
-
return resolved;
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
const inFlight = (async () => {
|
|
932
|
-
const rawDownload = await waitForEventMethod.call(tab.page, "download");
|
|
933
|
-
operationState.latestRawDownload = rawDownload;
|
|
934
|
-
const persistedPath =
|
|
935
|
-
operationState.requestedDownloadPath ??
|
|
936
|
-
createDownloadPath(profileId, targetId, Math.max(operationState.triggerCount, 1));
|
|
937
|
-
const artifact = await readDownloadArtifact(rawDownload, persistedPath);
|
|
938
|
-
operationState.latestDownload = artifact;
|
|
939
|
-
operationState.requestedDownloadPath = undefined;
|
|
940
|
-
return artifact;
|
|
941
|
-
})().finally(() => {
|
|
942
|
-
if (operationState.downloadInFlight === inFlight) {
|
|
943
|
-
operationState.downloadInFlight = undefined;
|
|
944
|
-
}
|
|
945
|
-
});
|
|
946
|
-
|
|
947
|
-
operationState.downloadInFlight = inFlight;
|
|
948
|
-
return inFlight;
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
async function resolveDownloadArtifactPath(
|
|
952
|
-
operationState: ManagedLocalTargetOperationState,
|
|
953
|
-
artifact: ManagedLocalDownloadArtifact,
|
|
954
|
-
requestedPath?: string
|
|
955
|
-
): Promise<ManagedLocalDownloadArtifact> {
|
|
956
|
-
if (requestedPath === undefined || requestedPath.trim().length === 0 || artifact.path === requestedPath) {
|
|
957
|
-
return artifact;
|
|
958
|
-
}
|
|
959
|
-
|
|
960
|
-
const failurePrefix = `Failed to persist download to requested path: ${requestedPath}`;
|
|
961
|
-
const saveAsMethod = getObjectMethod(operationState.latestRawDownload, "saveAs");
|
|
962
|
-
if (typeof saveAsMethod !== "function") {
|
|
963
|
-
throw new Error(failurePrefix);
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
try {
|
|
967
|
-
await saveAsMethod.call(operationState.latestRawDownload, requestedPath);
|
|
968
|
-
return { ...artifact, path: requestedPath };
|
|
969
|
-
} catch (error) {
|
|
970
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
971
|
-
throw new Error(message.length > 0 ? `${failurePrefix} (${message})` : failurePrefix);
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
return {
|
|
976
|
-
status: async () => ({
|
|
977
|
-
kind: "managed-local",
|
|
978
|
-
connected: browser !== undefined,
|
|
979
|
-
launched: browser !== undefined,
|
|
980
|
-
browserName: launchConfig.browserName,
|
|
981
|
-
headless: launchConfig.headless,
|
|
982
|
-
channel: launchConfig.channel,
|
|
983
|
-
executablePath: launchConfig.executablePath,
|
|
984
|
-
launchTimeoutMs: launchConfig.launchTimeoutMs
|
|
985
|
-
}),
|
|
986
|
-
listProfiles: async () => {
|
|
987
|
-
const knownProfiles = new Set<ProfileId>([DEFAULT_PROFILE_ID]);
|
|
988
|
-
for (const profileId of profileStates.keys()) {
|
|
989
|
-
knownProfiles.add(profileId);
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
return Array.from(knownProfiles).sort();
|
|
993
|
-
},
|
|
994
|
-
listTabs: async (profile) => {
|
|
995
|
-
const profileId = resolveProfileId(profile);
|
|
996
|
-
const profileState = profileStates.get(profileId);
|
|
997
|
-
return profileState === undefined ? [] : [...profileState.tabOrder];
|
|
998
|
-
},
|
|
999
|
-
openTab: async (url, profile) => {
|
|
1000
|
-
const profileId = resolveProfileId(profile);
|
|
1001
|
-
const profileState = await ensureProfileState(profileId);
|
|
1002
|
-
const targetId = createTargetId(profileId, profileState.nextTargetNumber);
|
|
1003
|
-
const context = profileState.context;
|
|
1004
|
-
if (context === undefined) {
|
|
1005
|
-
throw new Error(`No browser context available for profile: ${profileId}`);
|
|
1006
|
-
}
|
|
1007
|
-
|
|
1008
|
-
const page = await context.newPage();
|
|
1009
|
-
const telemetryState = createTargetTelemetryState();
|
|
1010
|
-
const operationState = getOrCreateTargetOperationState(profileId, targetId, false);
|
|
1011
|
-
attachTelemetryListeners(page, profileId, targetId, telemetryState, operationState);
|
|
1012
|
-
await page.goto(url);
|
|
1013
|
-
|
|
1014
|
-
profileState.nextTargetNumber += 1;
|
|
1015
|
-
profileState.tabs.set(targetId, { url, page });
|
|
1016
|
-
profileState.tabOrder.push(targetId);
|
|
1017
|
-
getOrCreateProfileTelemetryStates(profileId).set(targetId, telemetryState);
|
|
1018
|
-
if (profileState.focusedTargetId === undefined) {
|
|
1019
|
-
profileState.focusedTargetId = targetId;
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
return targetId;
|
|
1023
|
-
},
|
|
1024
|
-
focusTab: async (targetId, profile) => {
|
|
1025
|
-
const profileId = resolveProfileId(profile);
|
|
1026
|
-
const { profileState, tab } = requireTargetInProfile(profileId, targetId);
|
|
1027
|
-
await tab.page.bringToFront();
|
|
1028
|
-
profileState.focusedTargetId = targetId;
|
|
1029
|
-
},
|
|
1030
|
-
closeTab: async (targetId, profile) => {
|
|
1031
|
-
const profileId = resolveProfileId(profile);
|
|
1032
|
-
const { profileState, tab } = requireTargetInProfile(profileId, targetId);
|
|
1033
|
-
|
|
1034
|
-
await tab.page.close();
|
|
1035
|
-
|
|
1036
|
-
profileState.tabs.delete(targetId);
|
|
1037
|
-
profileState.tabOrder = profileState.tabOrder.filter((existingTargetId) => existingTargetId !== targetId);
|
|
1038
|
-
if (profileState.focusedTargetId === targetId) {
|
|
1039
|
-
profileState.focusedTargetId = profileState.tabOrder[0];
|
|
1040
|
-
}
|
|
1041
|
-
|
|
1042
|
-
const profileOperationStates = targetOperationStates.get(profileId);
|
|
1043
|
-
profileOperationStates?.delete(targetId);
|
|
1044
|
-
if (profileOperationStates !== undefined && profileOperationStates.size === 0) {
|
|
1045
|
-
targetOperationStates.delete(profileId);
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
|
-
const profileTelemetryStates = targetTelemetryStates.get(profileId);
|
|
1049
|
-
profileTelemetryStates?.delete(targetId);
|
|
1050
|
-
if (profileTelemetryStates !== undefined && profileTelemetryStates.size === 0) {
|
|
1051
|
-
targetTelemetryStates.delete(profileId);
|
|
1052
|
-
}
|
|
1053
|
-
|
|
1054
|
-
if (profileState.tabOrder.length === 0) {
|
|
1055
|
-
await closeProfileResources(profileId, profileState);
|
|
1056
|
-
}
|
|
1057
|
-
},
|
|
1058
|
-
snapshot: async (targetId, profile) => {
|
|
1059
|
-
const profileId = resolveProfileId(profile);
|
|
1060
|
-
const tab = profileStates.get(profileId)?.tabs.get(targetId);
|
|
1061
|
-
if (tab === undefined) {
|
|
1062
|
-
return {
|
|
1063
|
-
kind: "managed-local",
|
|
1064
|
-
profile: profileId,
|
|
1065
|
-
targetId,
|
|
1066
|
-
hasTarget: false
|
|
1067
|
-
};
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
const requestSummaries =
|
|
1071
|
-
targetTelemetryStates
|
|
1072
|
-
.get(profileId)
|
|
1073
|
-
?.get(targetId)
|
|
1074
|
-
?.requestSummaries.map((summary) => ({ ...summary })) ?? [];
|
|
1075
|
-
|
|
1076
|
-
return {
|
|
1077
|
-
kind: "managed-local",
|
|
1078
|
-
profile: profileId,
|
|
1079
|
-
targetId,
|
|
1080
|
-
hasTarget: true,
|
|
1081
|
-
url: tab.page.url(),
|
|
1082
|
-
title: await tab.page.title(),
|
|
1083
|
-
html: await tab.page.content(),
|
|
1084
|
-
requestSummaries
|
|
1085
|
-
};
|
|
1086
|
-
},
|
|
1087
|
-
screenshot: async (targetId, profile) => {
|
|
1088
|
-
const profileId = resolveProfileId(profile);
|
|
1089
|
-
const tab = profileStates.get(profileId)?.tabs.get(targetId);
|
|
1090
|
-
if (tab === undefined) {
|
|
1091
|
-
return {
|
|
1092
|
-
kind: "managed-local",
|
|
1093
|
-
profile: profileId,
|
|
1094
|
-
targetId,
|
|
1095
|
-
hasTarget: false
|
|
1096
|
-
};
|
|
1097
|
-
}
|
|
1098
|
-
|
|
1099
|
-
const screenshot = await capturePageScreenshot(tab.page);
|
|
1100
|
-
return {
|
|
1101
|
-
kind: "managed-local",
|
|
1102
|
-
profile: profileId,
|
|
1103
|
-
targetId,
|
|
1104
|
-
hasTarget: true,
|
|
1105
|
-
...screenshot
|
|
1106
|
-
};
|
|
1107
|
-
},
|
|
1108
|
-
act: async (action, targetId, profile) => {
|
|
1109
|
-
const profileId = resolveProfileId(profile);
|
|
1110
|
-
const tab = profileStates.get(profileId)?.tabs.get(targetId);
|
|
1111
|
-
if (tab === undefined) {
|
|
1112
|
-
return {
|
|
1113
|
-
actionType: action.type,
|
|
1114
|
-
profile: profileId,
|
|
1115
|
-
targetId,
|
|
1116
|
-
targetKnown: false,
|
|
1117
|
-
ok: false,
|
|
1118
|
-
error: `Unknown targetId: ${targetId} (profile: ${profileId})`
|
|
1119
|
-
};
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
const payload = action.payload;
|
|
1123
|
-
const profileState = profileStates.get(profileId);
|
|
1124
|
-
const operationState = getOrCreateTargetOperationState(profileId, targetId);
|
|
1125
|
-
const baseResult = {
|
|
1126
|
-
actionType: action.type,
|
|
1127
|
-
profile: profileId,
|
|
1128
|
-
targetId,
|
|
1129
|
-
targetKnown: true
|
|
1130
|
-
};
|
|
1131
|
-
|
|
1132
|
-
const performAction = async (): Promise<{
|
|
1133
|
-
ok: boolean;
|
|
1134
|
-
executed: boolean;
|
|
1135
|
-
error?: string;
|
|
1136
|
-
data?: Record<string, unknown>;
|
|
1137
|
-
}> => {
|
|
1138
|
-
switch (action.type) {
|
|
1139
|
-
case "navigate":
|
|
1140
|
-
case "goto": {
|
|
1141
|
-
const nextUrl = readActionPayloadString(payload, "url");
|
|
1142
|
-
if (nextUrl === undefined) {
|
|
1143
|
-
return {
|
|
1144
|
-
ok: false,
|
|
1145
|
-
executed: false,
|
|
1146
|
-
error: "action.payload.url is required for navigate action."
|
|
1147
|
-
};
|
|
1148
|
-
}
|
|
1149
|
-
|
|
1150
|
-
await tab.page.goto(nextUrl);
|
|
1151
|
-
tab.url = nextUrl;
|
|
1152
|
-
return { ok: true, executed: true };
|
|
1153
|
-
}
|
|
1154
|
-
case "click": {
|
|
1155
|
-
const selector = readActionPayloadString(payload, "selector");
|
|
1156
|
-
if (selector === undefined) {
|
|
1157
|
-
return {
|
|
1158
|
-
ok: false,
|
|
1159
|
-
executed: false,
|
|
1160
|
-
error: "action.payload.selector is required for click action."
|
|
1161
|
-
};
|
|
1162
|
-
}
|
|
1163
|
-
|
|
1164
|
-
const locator = tab.page.locator(selector);
|
|
1165
|
-
if (operationState.uploadFiles.length > 0) {
|
|
1166
|
-
const setInputFilesMethod = getObjectMethod(locator, "setInputFiles");
|
|
1167
|
-
if (typeof setInputFilesMethod === "function") {
|
|
1168
|
-
await setInputFilesMethod.call(locator, [...operationState.uploadFiles]);
|
|
1169
|
-
operationState.uploadFiles = [];
|
|
1170
|
-
return { ok: true, executed: true };
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
|
-
|
|
1174
|
-
await locator.click();
|
|
1175
|
-
return { ok: true, executed: true };
|
|
1176
|
-
}
|
|
1177
|
-
case "fill": {
|
|
1178
|
-
const selector = readActionPayloadString(payload, "selector");
|
|
1179
|
-
const value = readActionPayloadString(payload, "value");
|
|
1180
|
-
if (selector === undefined || value === undefined) {
|
|
1181
|
-
return {
|
|
1182
|
-
ok: false,
|
|
1183
|
-
executed: false,
|
|
1184
|
-
error: "action.payload.selector and action.payload.value are required for fill action."
|
|
1185
|
-
};
|
|
1186
|
-
}
|
|
1187
|
-
|
|
1188
|
-
await tab.page.locator(selector).fill(value);
|
|
1189
|
-
return { ok: true, executed: true };
|
|
1190
|
-
}
|
|
1191
|
-
case "type": {
|
|
1192
|
-
const selector = readActionPayloadString(payload, "selector");
|
|
1193
|
-
const text = readActionPayloadString(payload, "text");
|
|
1194
|
-
if (selector === undefined || text === undefined) {
|
|
1195
|
-
return {
|
|
1196
|
-
ok: false,
|
|
1197
|
-
executed: false,
|
|
1198
|
-
error: "action.payload.selector and action.payload.text are required for type action."
|
|
1199
|
-
};
|
|
1200
|
-
}
|
|
1201
|
-
|
|
1202
|
-
await tab.page.locator(selector).type(text);
|
|
1203
|
-
return { ok: true, executed: true };
|
|
1204
|
-
}
|
|
1205
|
-
case "press": {
|
|
1206
|
-
const key = readActionPayloadString(payload, "key");
|
|
1207
|
-
if (key === undefined || tab.page.keyboard === undefined) {
|
|
1208
|
-
return {
|
|
1209
|
-
ok: false,
|
|
1210
|
-
executed: false,
|
|
1211
|
-
error: "action.payload.key is required for press action."
|
|
1212
|
-
};
|
|
1213
|
-
}
|
|
1214
|
-
|
|
1215
|
-
await tab.page.keyboard.press(key);
|
|
1216
|
-
return { ok: true, executed: true };
|
|
1217
|
-
}
|
|
1218
|
-
case "networkMockAdd": {
|
|
1219
|
-
const urlPattern = readActionPayloadString(payload, "urlPattern");
|
|
1220
|
-
if (urlPattern === undefined) {
|
|
1221
|
-
return {
|
|
1222
|
-
ok: false,
|
|
1223
|
-
executed: false,
|
|
1224
|
-
error: "action.payload.urlPattern is required for networkMockAdd action."
|
|
1225
|
-
};
|
|
1226
|
-
}
|
|
1227
|
-
|
|
1228
|
-
const routeMethod = getObjectMethod(tab.page, "route");
|
|
1229
|
-
if (typeof routeMethod !== "function") {
|
|
1230
|
-
return {
|
|
1231
|
-
ok: true,
|
|
1232
|
-
executed: false
|
|
1233
|
-
};
|
|
1234
|
-
}
|
|
1235
|
-
|
|
1236
|
-
const methodFilter = readActionPayloadString(payload, "method")?.toUpperCase();
|
|
1237
|
-
const status = readActionPayloadNumber(payload, "status") ?? 200;
|
|
1238
|
-
if (!Number.isInteger(status) || status < 100 || status > 599) {
|
|
1239
|
-
return {
|
|
1240
|
-
ok: false,
|
|
1241
|
-
executed: false,
|
|
1242
|
-
error: "action.payload.status must be an integer between 100 and 599 for networkMockAdd action."
|
|
1243
|
-
};
|
|
1244
|
-
}
|
|
1245
|
-
const body = readActionPayloadString(payload, "body") ?? "";
|
|
1246
|
-
const contentType = readActionPayloadString(payload, "contentType") ?? "text/plain";
|
|
1247
|
-
const mockId = `mock:${operationState.nextNetworkMockNumber}`;
|
|
1248
|
-
operationState.nextNetworkMockNumber += 1;
|
|
1249
|
-
const handler: ManagedLocalNetworkMockHandler = async (...args) => {
|
|
1250
|
-
const route = args[0];
|
|
1251
|
-
const explicitRequest = args[1];
|
|
1252
|
-
const request = explicitRequest ?? readObjectMethod<unknown>(route, "request");
|
|
1253
|
-
if (methodFilter !== undefined) {
|
|
1254
|
-
const requestMethod = readObjectMethod<string>(request, "method");
|
|
1255
|
-
if (requestMethod?.toUpperCase() !== methodFilter) {
|
|
1256
|
-
const continueMethod = getObjectMethod(route, "continue");
|
|
1257
|
-
if (typeof continueMethod === "function") {
|
|
1258
|
-
await continueMethod.call(route);
|
|
1259
|
-
return;
|
|
1260
|
-
}
|
|
1261
|
-
|
|
1262
|
-
const fallbackMethod = getObjectMethod(route, "fallback");
|
|
1263
|
-
if (typeof fallbackMethod === "function") {
|
|
1264
|
-
await fallbackMethod.call(route);
|
|
1265
|
-
return;
|
|
1266
|
-
}
|
|
1267
|
-
|
|
1268
|
-
return;
|
|
1269
|
-
}
|
|
1270
|
-
}
|
|
1271
|
-
|
|
1272
|
-
const fulfillMethod = getObjectMethod(route, "fulfill");
|
|
1273
|
-
if (typeof fulfillMethod !== "function") {
|
|
1274
|
-
return;
|
|
1275
|
-
}
|
|
1276
|
-
|
|
1277
|
-
await fulfillMethod.call(route, {
|
|
1278
|
-
status,
|
|
1279
|
-
body,
|
|
1280
|
-
headers: {
|
|
1281
|
-
"content-type": contentType
|
|
1282
|
-
}
|
|
1283
|
-
});
|
|
1284
|
-
};
|
|
1285
|
-
|
|
1286
|
-
await routeMethod.call(tab.page, urlPattern, handler);
|
|
1287
|
-
operationState.networkMocks.set(mockId, {
|
|
1288
|
-
urlPattern,
|
|
1289
|
-
handler
|
|
1290
|
-
});
|
|
1291
|
-
return {
|
|
1292
|
-
ok: true,
|
|
1293
|
-
executed: true,
|
|
1294
|
-
data: {
|
|
1295
|
-
mockId,
|
|
1296
|
-
urlPattern,
|
|
1297
|
-
...(methodFilter !== undefined ? { method: methodFilter } : {}),
|
|
1298
|
-
status
|
|
1299
|
-
}
|
|
1300
|
-
};
|
|
1301
|
-
}
|
|
1302
|
-
case "networkMockClear": {
|
|
1303
|
-
const unrouteMethod = getObjectMethod(tab.page, "unroute");
|
|
1304
|
-
if (typeof unrouteMethod !== "function") {
|
|
1305
|
-
return {
|
|
1306
|
-
ok: true,
|
|
1307
|
-
executed: false
|
|
1308
|
-
};
|
|
1309
|
-
}
|
|
1310
|
-
|
|
1311
|
-
const mockId = readActionPayloadString(payload, "mockId");
|
|
1312
|
-
if (mockId !== undefined) {
|
|
1313
|
-
const binding = operationState.networkMocks.get(mockId);
|
|
1314
|
-
if (binding === undefined) {
|
|
1315
|
-
return {
|
|
1316
|
-
ok: false,
|
|
1317
|
-
executed: false,
|
|
1318
|
-
error: `Unknown network mock id: ${mockId}`
|
|
1319
|
-
};
|
|
1320
|
-
}
|
|
1321
|
-
|
|
1322
|
-
await unrouteMethod.call(tab.page, binding.urlPattern, binding.handler);
|
|
1323
|
-
operationState.networkMocks.delete(mockId);
|
|
1324
|
-
return {
|
|
1325
|
-
ok: true,
|
|
1326
|
-
executed: true,
|
|
1327
|
-
data: {
|
|
1328
|
-
cleared: 1,
|
|
1329
|
-
mockId
|
|
1330
|
-
}
|
|
1331
|
-
};
|
|
1332
|
-
}
|
|
1333
|
-
|
|
1334
|
-
let cleared = 0;
|
|
1335
|
-
for (const [registeredId, binding] of operationState.networkMocks.entries()) {
|
|
1336
|
-
try {
|
|
1337
|
-
await unrouteMethod.call(tab.page, binding.urlPattern, binding.handler);
|
|
1338
|
-
} catch {
|
|
1339
|
-
// Ignore per-route cleanup failures.
|
|
1340
|
-
}
|
|
1341
|
-
operationState.networkMocks.delete(registeredId);
|
|
1342
|
-
cleared += 1;
|
|
1343
|
-
}
|
|
1344
|
-
|
|
1345
|
-
return {
|
|
1346
|
-
ok: true,
|
|
1347
|
-
executed: true,
|
|
1348
|
-
data: {
|
|
1349
|
-
cleared
|
|
1350
|
-
}
|
|
1351
|
-
};
|
|
1352
|
-
}
|
|
1353
|
-
case "domQuery": {
|
|
1354
|
-
const selector = readActionPayloadString(payload, "selector");
|
|
1355
|
-
if (selector === undefined) {
|
|
1356
|
-
return {
|
|
1357
|
-
ok: false,
|
|
1358
|
-
executed: false,
|
|
1359
|
-
error: "action.payload.selector is required for domQuery action."
|
|
1360
|
-
};
|
|
1361
|
-
}
|
|
1362
|
-
|
|
1363
|
-
const customDomQueryMethod = getObjectMethod(tab.page, "domQuery");
|
|
1364
|
-
if (typeof customDomQueryMethod === "function") {
|
|
1365
|
-
const customResult = await customDomQueryMethod.call(tab.page, selector);
|
|
1366
|
-
return {
|
|
1367
|
-
ok: true,
|
|
1368
|
-
executed: true,
|
|
1369
|
-
data: isObjectRecord(customResult)
|
|
1370
|
-
? customResult
|
|
1371
|
-
: {
|
|
1372
|
-
selector,
|
|
1373
|
-
found: false
|
|
1374
|
-
}
|
|
1375
|
-
};
|
|
1376
|
-
}
|
|
1377
|
-
|
|
1378
|
-
const queryResult = await evaluateInPage<Record<string, unknown>>(
|
|
1379
|
-
tab.page,
|
|
1380
|
-
(rawArg) => {
|
|
1381
|
-
const selectorValue =
|
|
1382
|
-
rawArg !== null &&
|
|
1383
|
-
typeof rawArg === "object" &&
|
|
1384
|
-
typeof (rawArg as { selector?: unknown }).selector === "string"
|
|
1385
|
-
? (rawArg as { selector: string }).selector
|
|
1386
|
-
: "";
|
|
1387
|
-
const element = selectorValue.length > 0 ? document.querySelector(selectorValue) : null;
|
|
1388
|
-
if (element === null) {
|
|
1389
|
-
return {
|
|
1390
|
-
selector: selectorValue,
|
|
1391
|
-
found: false
|
|
1392
|
-
};
|
|
1393
|
-
}
|
|
1394
|
-
|
|
1395
|
-
const attributes: Record<string, string> = {};
|
|
1396
|
-
for (const attribute of Array.from(element.attributes)) {
|
|
1397
|
-
attributes[attribute.name] = attribute.value;
|
|
1398
|
-
}
|
|
1399
|
-
|
|
1400
|
-
return {
|
|
1401
|
-
selector: selectorValue,
|
|
1402
|
-
found: true,
|
|
1403
|
-
node: {
|
|
1404
|
-
tagName: element.tagName.toLowerCase(),
|
|
1405
|
-
id: element.id || undefined,
|
|
1406
|
-
className: element.className || undefined,
|
|
1407
|
-
text: (element.textContent || "").trim().slice(0, 300),
|
|
1408
|
-
attributes
|
|
1409
|
-
}
|
|
1410
|
-
};
|
|
1411
|
-
},
|
|
1412
|
-
{ selector }
|
|
1413
|
-
);
|
|
1414
|
-
|
|
1415
|
-
return {
|
|
1416
|
-
ok: true,
|
|
1417
|
-
executed: true,
|
|
1418
|
-
data: isObjectRecord(queryResult)
|
|
1419
|
-
? queryResult
|
|
1420
|
-
: {
|
|
1421
|
-
selector,
|
|
1422
|
-
found: false
|
|
1423
|
-
}
|
|
1424
|
-
};
|
|
1425
|
-
}
|
|
1426
|
-
case "domQueryAll": {
|
|
1427
|
-
const selector = readActionPayloadString(payload, "selector");
|
|
1428
|
-
if (selector === undefined) {
|
|
1429
|
-
return {
|
|
1430
|
-
ok: false,
|
|
1431
|
-
executed: false,
|
|
1432
|
-
error: "action.payload.selector is required for domQueryAll action."
|
|
1433
|
-
};
|
|
1434
|
-
}
|
|
1435
|
-
|
|
1436
|
-
const customDomQueryAllMethod = getObjectMethod(tab.page, "domQueryAll");
|
|
1437
|
-
if (typeof customDomQueryAllMethod === "function") {
|
|
1438
|
-
const customResult = await customDomQueryAllMethod.call(tab.page, selector);
|
|
1439
|
-
return {
|
|
1440
|
-
ok: true,
|
|
1441
|
-
executed: true,
|
|
1442
|
-
data: isObjectRecord(customResult)
|
|
1443
|
-
? customResult
|
|
1444
|
-
: {
|
|
1445
|
-
selector,
|
|
1446
|
-
count: 0,
|
|
1447
|
-
nodes: []
|
|
1448
|
-
}
|
|
1449
|
-
};
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
|
-
const queryResult = await evaluateInPage<Record<string, unknown>>(
|
|
1453
|
-
tab.page,
|
|
1454
|
-
(rawArg) => {
|
|
1455
|
-
const selectorValue =
|
|
1456
|
-
rawArg !== null &&
|
|
1457
|
-
typeof rawArg === "object" &&
|
|
1458
|
-
typeof (rawArg as { selector?: unknown }).selector === "string"
|
|
1459
|
-
? (rawArg as { selector: string }).selector
|
|
1460
|
-
: "";
|
|
1461
|
-
const elements = selectorValue.length > 0 ? document.querySelectorAll(selectorValue) : [];
|
|
1462
|
-
const nodes = Array.from(elements).map((element, index) => {
|
|
1463
|
-
const attributes: Record<string, string> = {};
|
|
1464
|
-
for (const attribute of Array.from(element.attributes)) {
|
|
1465
|
-
attributes[attribute.name] = attribute.value;
|
|
1466
|
-
}
|
|
1467
|
-
|
|
1468
|
-
return {
|
|
1469
|
-
index,
|
|
1470
|
-
tagName: element.tagName.toLowerCase(),
|
|
1471
|
-
id: element.id || undefined,
|
|
1472
|
-
className: element.className || undefined,
|
|
1473
|
-
text: (element.textContent || "").trim().slice(0, 300),
|
|
1474
|
-
attributes
|
|
1475
|
-
};
|
|
1476
|
-
});
|
|
1477
|
-
|
|
1478
|
-
return {
|
|
1479
|
-
selector: selectorValue,
|
|
1480
|
-
count: nodes.length,
|
|
1481
|
-
nodes
|
|
1482
|
-
};
|
|
1483
|
-
},
|
|
1484
|
-
{ selector }
|
|
1485
|
-
);
|
|
1486
|
-
|
|
1487
|
-
return {
|
|
1488
|
-
ok: true,
|
|
1489
|
-
executed: true,
|
|
1490
|
-
data: isObjectRecord(queryResult)
|
|
1491
|
-
? queryResult
|
|
1492
|
-
: {
|
|
1493
|
-
selector,
|
|
1494
|
-
count: 0,
|
|
1495
|
-
nodes: []
|
|
1496
|
-
}
|
|
1497
|
-
};
|
|
1498
|
-
}
|
|
1499
|
-
case "elementScreenshot": {
|
|
1500
|
-
const selector = readActionPayloadString(payload, "selector");
|
|
1501
|
-
if (selector === undefined) {
|
|
1502
|
-
return {
|
|
1503
|
-
ok: false,
|
|
1504
|
-
executed: false,
|
|
1505
|
-
error: "action.payload.selector is required for elementScreenshot action."
|
|
1506
|
-
};
|
|
1507
|
-
}
|
|
1508
|
-
|
|
1509
|
-
const customElementScreenshotMethod = getObjectMethod(tab.page, "elementScreenshot");
|
|
1510
|
-
if (typeof customElementScreenshotMethod === "function") {
|
|
1511
|
-
const customResult = await customElementScreenshotMethod.call(tab.page, selector);
|
|
1512
|
-
return {
|
|
1513
|
-
ok: true,
|
|
1514
|
-
executed: true,
|
|
1515
|
-
data: isObjectRecord(customResult)
|
|
1516
|
-
? customResult
|
|
1517
|
-
: {
|
|
1518
|
-
selector,
|
|
1519
|
-
found: false
|
|
1520
|
-
}
|
|
1521
|
-
};
|
|
1522
|
-
}
|
|
1523
|
-
|
|
1524
|
-
const found = await evaluateInPage<boolean>(
|
|
1525
|
-
tab.page,
|
|
1526
|
-
(rawArg) => {
|
|
1527
|
-
const selectorValue =
|
|
1528
|
-
rawArg !== null &&
|
|
1529
|
-
typeof rawArg === "object" &&
|
|
1530
|
-
typeof (rawArg as { selector?: unknown }).selector === "string"
|
|
1531
|
-
? (rawArg as { selector: string }).selector
|
|
1532
|
-
: "";
|
|
1533
|
-
return selectorValue.length > 0 && document.querySelector(selectorValue) !== null;
|
|
1534
|
-
},
|
|
1535
|
-
{ selector }
|
|
1536
|
-
);
|
|
1537
|
-
if (!found) {
|
|
1538
|
-
return {
|
|
1539
|
-
ok: true,
|
|
1540
|
-
executed: true,
|
|
1541
|
-
data: {
|
|
1542
|
-
selector,
|
|
1543
|
-
found: false
|
|
1544
|
-
}
|
|
1545
|
-
};
|
|
1546
|
-
}
|
|
1547
|
-
|
|
1548
|
-
const locator = tab.page.locator(selector);
|
|
1549
|
-
const locatorScreenshotMethod = getObjectMethod(locator, "screenshot");
|
|
1550
|
-
if (typeof locatorScreenshotMethod !== "function") {
|
|
1551
|
-
return {
|
|
1552
|
-
ok: true,
|
|
1553
|
-
executed: false
|
|
1554
|
-
};
|
|
1555
|
-
}
|
|
1556
|
-
|
|
1557
|
-
const rawScreenshot = await locatorScreenshotMethod.call(locator, { type: "png" });
|
|
1558
|
-
const parsedScreenshot = readScreenshotImage(rawScreenshot);
|
|
1559
|
-
if (parsedScreenshot === undefined) {
|
|
1560
|
-
return {
|
|
1561
|
-
ok: false,
|
|
1562
|
-
executed: false,
|
|
1563
|
-
error: "Unable to parse element screenshot payload."
|
|
1564
|
-
};
|
|
1565
|
-
}
|
|
1566
|
-
|
|
1567
|
-
return {
|
|
1568
|
-
ok: true,
|
|
1569
|
-
executed: true,
|
|
1570
|
-
data: {
|
|
1571
|
-
selector,
|
|
1572
|
-
found: true,
|
|
1573
|
-
...parsedScreenshot
|
|
1574
|
-
}
|
|
1575
|
-
};
|
|
1576
|
-
}
|
|
1577
|
-
case "a11ySnapshot": {
|
|
1578
|
-
const selector = readActionPayloadString(payload, "selector");
|
|
1579
|
-
const customA11ySnapshotMethod = getObjectMethod(tab.page, "a11ySnapshot");
|
|
1580
|
-
if (typeof customA11ySnapshotMethod === "function") {
|
|
1581
|
-
const customResult = await customA11ySnapshotMethod.call(tab.page, selector);
|
|
1582
|
-
return {
|
|
1583
|
-
ok: true,
|
|
1584
|
-
executed: true,
|
|
1585
|
-
data: isObjectRecord(customResult)
|
|
1586
|
-
? customResult
|
|
1587
|
-
: {
|
|
1588
|
-
found: false
|
|
1589
|
-
}
|
|
1590
|
-
};
|
|
1591
|
-
}
|
|
1592
|
-
|
|
1593
|
-
const accessibilityValue =
|
|
1594
|
-
typeof tab.page === "object" && tab.page !== null
|
|
1595
|
-
? (tab.page as Record<string, unknown>).accessibility
|
|
1596
|
-
: undefined;
|
|
1597
|
-
const accessibilitySnapshotMethod = getObjectMethod(accessibilityValue, "snapshot");
|
|
1598
|
-
if (typeof accessibilitySnapshotMethod === "function") {
|
|
1599
|
-
const snapshot = await accessibilitySnapshotMethod.call(accessibilityValue, {});
|
|
1600
|
-
return {
|
|
1601
|
-
ok: true,
|
|
1602
|
-
executed: true,
|
|
1603
|
-
data: {
|
|
1604
|
-
selector,
|
|
1605
|
-
found: true,
|
|
1606
|
-
snapshot
|
|
1607
|
-
}
|
|
1608
|
-
};
|
|
1609
|
-
}
|
|
1610
|
-
|
|
1611
|
-
const fallbackSnapshot = await evaluateInPage<Record<string, unknown>>(
|
|
1612
|
-
tab.page,
|
|
1613
|
-
(rawArg) => {
|
|
1614
|
-
const selectorValue =
|
|
1615
|
-
rawArg !== null &&
|
|
1616
|
-
typeof rawArg === "object" &&
|
|
1617
|
-
typeof (rawArg as { selector?: unknown }).selector === "string"
|
|
1618
|
-
? (rawArg as { selector: string }).selector
|
|
1619
|
-
: "";
|
|
1620
|
-
const root =
|
|
1621
|
-
selectorValue.length > 0
|
|
1622
|
-
? document.querySelector(selectorValue)
|
|
1623
|
-
: document.body ?? document.documentElement;
|
|
1624
|
-
if (root === null) {
|
|
1625
|
-
return {
|
|
1626
|
-
selector: selectorValue,
|
|
1627
|
-
found: false
|
|
1628
|
-
};
|
|
1629
|
-
}
|
|
1630
|
-
|
|
1631
|
-
const build = (element: Element, depth: number): Record<string, unknown> => {
|
|
1632
|
-
const role =
|
|
1633
|
-
element.getAttribute("role") ??
|
|
1634
|
-
(element.tagName.toLowerCase() === "a"
|
|
1635
|
-
? "link"
|
|
1636
|
-
: element.tagName.toLowerCase() === "button"
|
|
1637
|
-
? "button"
|
|
1638
|
-
: element.tagName.toLowerCase() === "input"
|
|
1639
|
-
? "textbox"
|
|
1640
|
-
: "generic");
|
|
1641
|
-
const name =
|
|
1642
|
-
element.getAttribute("aria-label") ??
|
|
1643
|
-
element.getAttribute("alt") ??
|
|
1644
|
-
(element.textContent || "").trim().slice(0, 120);
|
|
1645
|
-
if (depth >= 5) {
|
|
1646
|
-
return {
|
|
1647
|
-
role,
|
|
1648
|
-
...(name.length > 0 ? { name } : {})
|
|
1649
|
-
};
|
|
1650
|
-
}
|
|
1651
|
-
|
|
1652
|
-
const children = Array.from(element.children)
|
|
1653
|
-
.slice(0, 30)
|
|
1654
|
-
.map((child) => build(child, depth + 1));
|
|
1655
|
-
return {
|
|
1656
|
-
role,
|
|
1657
|
-
...(name.length > 0 ? { name } : {}),
|
|
1658
|
-
...(children.length > 0 ? { children } : {})
|
|
1659
|
-
};
|
|
1660
|
-
};
|
|
1661
|
-
|
|
1662
|
-
return {
|
|
1663
|
-
selector: selectorValue,
|
|
1664
|
-
found: true,
|
|
1665
|
-
snapshot: build(root, 0)
|
|
1666
|
-
};
|
|
1667
|
-
},
|
|
1668
|
-
{ selector: selector ?? "" }
|
|
1669
|
-
);
|
|
1670
|
-
|
|
1671
|
-
return {
|
|
1672
|
-
ok: true,
|
|
1673
|
-
executed: true,
|
|
1674
|
-
data: isObjectRecord(fallbackSnapshot)
|
|
1675
|
-
? fallbackSnapshot
|
|
1676
|
-
: {
|
|
1677
|
-
selector,
|
|
1678
|
-
found: false
|
|
1679
|
-
}
|
|
1680
|
-
};
|
|
1681
|
-
}
|
|
1682
|
-
case "cookieGet": {
|
|
1683
|
-
const cookieName = readActionPayloadString(payload, "name");
|
|
1684
|
-
const customCookieGetMethod = getObjectMethod(tab.page, "cookieGet");
|
|
1685
|
-
if (typeof customCookieGetMethod === "function") {
|
|
1686
|
-
const customResult = await customCookieGetMethod.call(tab.page, cookieName);
|
|
1687
|
-
return {
|
|
1688
|
-
ok: true,
|
|
1689
|
-
executed: true,
|
|
1690
|
-
data: isObjectRecord(customResult)
|
|
1691
|
-
? customResult
|
|
1692
|
-
: {
|
|
1693
|
-
cookies: []
|
|
1694
|
-
}
|
|
1695
|
-
};
|
|
1696
|
-
}
|
|
1697
|
-
|
|
1698
|
-
const context = profileState?.context;
|
|
1699
|
-
const cookiesMethod = context === undefined ? undefined : getObjectMethod(context, "cookies");
|
|
1700
|
-
if (typeof cookiesMethod !== "function") {
|
|
1701
|
-
return {
|
|
1702
|
-
ok: true,
|
|
1703
|
-
executed: false
|
|
1704
|
-
};
|
|
1705
|
-
}
|
|
1706
|
-
|
|
1707
|
-
let rawCookies: unknown;
|
|
1708
|
-
try {
|
|
1709
|
-
rawCookies = await cookiesMethod.call(context, [tab.page.url()]);
|
|
1710
|
-
} catch {
|
|
1711
|
-
rawCookies = await cookiesMethod.call(context);
|
|
1712
|
-
}
|
|
1713
|
-
const cookies = normalizeCookies(rawCookies).filter(
|
|
1714
|
-
(cookie) => cookieName === undefined || cookie.name === cookieName
|
|
1715
|
-
);
|
|
1716
|
-
return {
|
|
1717
|
-
ok: true,
|
|
1718
|
-
executed: true,
|
|
1719
|
-
data: {
|
|
1720
|
-
cookies,
|
|
1721
|
-
...(cookieName !== undefined ? { name: cookieName } : {})
|
|
1722
|
-
}
|
|
1723
|
-
};
|
|
1724
|
-
}
|
|
1725
|
-
case "cookieSet": {
|
|
1726
|
-
const cookieName = readActionPayloadString(payload, "name");
|
|
1727
|
-
const cookieValue = readActionPayloadString(payload, "value");
|
|
1728
|
-
const cookieUrl = readActionPayloadString(payload, "url") ?? tab.page.url();
|
|
1729
|
-
if (cookieName === undefined || cookieValue === undefined) {
|
|
1730
|
-
return {
|
|
1731
|
-
ok: false,
|
|
1732
|
-
executed: false,
|
|
1733
|
-
error: "action.payload.name and action.payload.value are required for cookieSet action."
|
|
1734
|
-
};
|
|
1735
|
-
}
|
|
1736
|
-
|
|
1737
|
-
const customCookieSetMethod = getObjectMethod(tab.page, "cookieSet");
|
|
1738
|
-
if (typeof customCookieSetMethod === "function") {
|
|
1739
|
-
const customResult = await customCookieSetMethod.call(tab.page, {
|
|
1740
|
-
name: cookieName,
|
|
1741
|
-
value: cookieValue,
|
|
1742
|
-
url: cookieUrl
|
|
1743
|
-
});
|
|
1744
|
-
return {
|
|
1745
|
-
ok: true,
|
|
1746
|
-
executed: true,
|
|
1747
|
-
data: isObjectRecord(customResult)
|
|
1748
|
-
? customResult
|
|
1749
|
-
: {
|
|
1750
|
-
set: true,
|
|
1751
|
-
cookie: {
|
|
1752
|
-
name: cookieName,
|
|
1753
|
-
value: cookieValue,
|
|
1754
|
-
url: cookieUrl
|
|
1755
|
-
}
|
|
1756
|
-
}
|
|
1757
|
-
};
|
|
1758
|
-
}
|
|
1759
|
-
|
|
1760
|
-
const context = profileState?.context;
|
|
1761
|
-
const addCookiesMethod = context === undefined ? undefined : getObjectMethod(context, "addCookies");
|
|
1762
|
-
if (typeof addCookiesMethod !== "function") {
|
|
1763
|
-
return {
|
|
1764
|
-
ok: true,
|
|
1765
|
-
executed: false
|
|
1766
|
-
};
|
|
1767
|
-
}
|
|
1768
|
-
|
|
1769
|
-
await addCookiesMethod.call(context, [
|
|
1770
|
-
{
|
|
1771
|
-
name: cookieName,
|
|
1772
|
-
value: cookieValue,
|
|
1773
|
-
url: cookieUrl
|
|
1774
|
-
}
|
|
1775
|
-
]);
|
|
1776
|
-
|
|
1777
|
-
return {
|
|
1778
|
-
ok: true,
|
|
1779
|
-
executed: true,
|
|
1780
|
-
data: {
|
|
1781
|
-
set: true,
|
|
1782
|
-
cookie: {
|
|
1783
|
-
name: cookieName,
|
|
1784
|
-
value: cookieValue,
|
|
1785
|
-
url: cookieUrl
|
|
1786
|
-
}
|
|
1787
|
-
}
|
|
1788
|
-
};
|
|
1789
|
-
}
|
|
1790
|
-
case "cookieClear": {
|
|
1791
|
-
const cookieName = readActionPayloadString(payload, "name");
|
|
1792
|
-
const customCookieClearMethod = getObjectMethod(tab.page, "cookieClear");
|
|
1793
|
-
if (typeof customCookieClearMethod === "function") {
|
|
1794
|
-
const customResult = await customCookieClearMethod.call(tab.page, cookieName);
|
|
1795
|
-
return {
|
|
1796
|
-
ok: true,
|
|
1797
|
-
executed: true,
|
|
1798
|
-
data: isObjectRecord(customResult)
|
|
1799
|
-
? customResult
|
|
1800
|
-
: {
|
|
1801
|
-
cleared: true
|
|
1802
|
-
}
|
|
1803
|
-
};
|
|
1804
|
-
}
|
|
1805
|
-
|
|
1806
|
-
const context = profileState?.context;
|
|
1807
|
-
const clearCookiesMethod = context === undefined ? undefined : getObjectMethod(context, "clearCookies");
|
|
1808
|
-
if (typeof clearCookiesMethod !== "function") {
|
|
1809
|
-
return {
|
|
1810
|
-
ok: true,
|
|
1811
|
-
executed: false
|
|
1812
|
-
};
|
|
1813
|
-
}
|
|
1814
|
-
|
|
1815
|
-
if (cookieName === undefined) {
|
|
1816
|
-
await clearCookiesMethod.call(context);
|
|
1817
|
-
return {
|
|
1818
|
-
ok: true,
|
|
1819
|
-
executed: true,
|
|
1820
|
-
data: {
|
|
1821
|
-
cleared: true,
|
|
1822
|
-
count: -1
|
|
1823
|
-
}
|
|
1824
|
-
};
|
|
1825
|
-
}
|
|
1826
|
-
|
|
1827
|
-
const cookiesMethod = getObjectMethod(context, "cookies");
|
|
1828
|
-
const addCookiesMethod = getObjectMethod(context, "addCookies");
|
|
1829
|
-
if (typeof cookiesMethod !== "function" || typeof addCookiesMethod !== "function") {
|
|
1830
|
-
return {
|
|
1831
|
-
ok: true,
|
|
1832
|
-
executed: false
|
|
1833
|
-
};
|
|
1834
|
-
}
|
|
1835
|
-
|
|
1836
|
-
let rawCookies: unknown;
|
|
1837
|
-
try {
|
|
1838
|
-
rawCookies = await cookiesMethod.call(context, [tab.page.url()]);
|
|
1839
|
-
} catch {
|
|
1840
|
-
rawCookies = await cookiesMethod.call(context);
|
|
1841
|
-
}
|
|
1842
|
-
const currentCookies = normalizeCookies(rawCookies);
|
|
1843
|
-
const keptCookieInputs = currentCookies
|
|
1844
|
-
.filter((cookie) => cookie.name !== cookieName)
|
|
1845
|
-
.map((cookie) => toCookieInput(cookie))
|
|
1846
|
-
.filter((cookie): cookie is Record<string, unknown> => cookie !== undefined);
|
|
1847
|
-
const removedCount = currentCookies.length - keptCookieInputs.length;
|
|
1848
|
-
|
|
1849
|
-
await clearCookiesMethod.call(context);
|
|
1850
|
-
if (keptCookieInputs.length > 0) {
|
|
1851
|
-
await addCookiesMethod.call(context, keptCookieInputs);
|
|
1852
|
-
}
|
|
1853
|
-
|
|
1854
|
-
return {
|
|
1855
|
-
ok: true,
|
|
1856
|
-
executed: true,
|
|
1857
|
-
data: {
|
|
1858
|
-
cleared: true,
|
|
1859
|
-
name: cookieName,
|
|
1860
|
-
count: removedCount
|
|
1861
|
-
}
|
|
1862
|
-
};
|
|
1863
|
-
}
|
|
1864
|
-
case "storageGet": {
|
|
1865
|
-
const scope = parseStorageScope(readActionPayloadString(payload, "scope"));
|
|
1866
|
-
const key = readActionPayloadString(payload, "key");
|
|
1867
|
-
if (scope === undefined || key === undefined) {
|
|
1868
|
-
return {
|
|
1869
|
-
ok: false,
|
|
1870
|
-
executed: false,
|
|
1871
|
-
error: "action.payload.scope and action.payload.key are required for storageGet action."
|
|
1872
|
-
};
|
|
1873
|
-
}
|
|
1874
|
-
|
|
1875
|
-
const customStorageGetMethod = getObjectMethod(tab.page, "storageGet");
|
|
1876
|
-
if (typeof customStorageGetMethod === "function") {
|
|
1877
|
-
const customResult = await customStorageGetMethod.call(tab.page, scope, key);
|
|
1878
|
-
return {
|
|
1879
|
-
ok: true,
|
|
1880
|
-
executed: true,
|
|
1881
|
-
data: isObjectRecord(customResult)
|
|
1882
|
-
? customResult
|
|
1883
|
-
: {
|
|
1884
|
-
scope,
|
|
1885
|
-
key,
|
|
1886
|
-
exists: false
|
|
1887
|
-
}
|
|
1888
|
-
};
|
|
1889
|
-
}
|
|
1890
|
-
|
|
1891
|
-
const result = await evaluateInPage<Record<string, unknown>>(
|
|
1892
|
-
tab.page,
|
|
1893
|
-
(rawArg) => {
|
|
1894
|
-
const scopeValue =
|
|
1895
|
-
rawArg !== null &&
|
|
1896
|
-
typeof rawArg === "object" &&
|
|
1897
|
-
typeof (rawArg as { scope?: unknown }).scope === "string"
|
|
1898
|
-
? (rawArg as { scope: string }).scope
|
|
1899
|
-
: "local";
|
|
1900
|
-
const keyValue =
|
|
1901
|
-
rawArg !== null &&
|
|
1902
|
-
typeof rawArg === "object" &&
|
|
1903
|
-
typeof (rawArg as { key?: unknown }).key === "string"
|
|
1904
|
-
? (rawArg as { key: string }).key
|
|
1905
|
-
: "";
|
|
1906
|
-
const storage = scopeValue === "session" ? window.sessionStorage : window.localStorage;
|
|
1907
|
-
const value = storage.getItem(keyValue);
|
|
1908
|
-
return {
|
|
1909
|
-
scope: scopeValue,
|
|
1910
|
-
key: keyValue,
|
|
1911
|
-
exists: value !== null,
|
|
1912
|
-
...(value !== null ? { value } : {})
|
|
1913
|
-
};
|
|
1914
|
-
},
|
|
1915
|
-
{ scope, key }
|
|
1916
|
-
);
|
|
1917
|
-
|
|
1918
|
-
return {
|
|
1919
|
-
ok: true,
|
|
1920
|
-
executed: true,
|
|
1921
|
-
data: isObjectRecord(result)
|
|
1922
|
-
? result
|
|
1923
|
-
: {
|
|
1924
|
-
scope,
|
|
1925
|
-
key,
|
|
1926
|
-
exists: false
|
|
1927
|
-
}
|
|
1928
|
-
};
|
|
1929
|
-
}
|
|
1930
|
-
case "storageSet": {
|
|
1931
|
-
const scope = parseStorageScope(readActionPayloadString(payload, "scope"));
|
|
1932
|
-
const key = readActionPayloadString(payload, "key");
|
|
1933
|
-
const value = readActionPayloadString(payload, "value");
|
|
1934
|
-
if (scope === undefined || key === undefined || value === undefined) {
|
|
1935
|
-
return {
|
|
1936
|
-
ok: false,
|
|
1937
|
-
executed: false,
|
|
1938
|
-
error: "action.payload.scope, action.payload.key and action.payload.value are required for storageSet action."
|
|
1939
|
-
};
|
|
1940
|
-
}
|
|
1941
|
-
|
|
1942
|
-
const customStorageSetMethod = getObjectMethod(tab.page, "storageSet");
|
|
1943
|
-
if (typeof customStorageSetMethod === "function") {
|
|
1944
|
-
const customResult = await customStorageSetMethod.call(tab.page, scope, key, value);
|
|
1945
|
-
return {
|
|
1946
|
-
ok: true,
|
|
1947
|
-
executed: true,
|
|
1948
|
-
data: isObjectRecord(customResult)
|
|
1949
|
-
? customResult
|
|
1950
|
-
: {
|
|
1951
|
-
scope,
|
|
1952
|
-
key,
|
|
1953
|
-
value,
|
|
1954
|
-
set: true
|
|
1955
|
-
}
|
|
1956
|
-
};
|
|
1957
|
-
}
|
|
1958
|
-
|
|
1959
|
-
await evaluateInPage(
|
|
1960
|
-
tab.page,
|
|
1961
|
-
(rawArg) => {
|
|
1962
|
-
const scopeValue =
|
|
1963
|
-
rawArg !== null &&
|
|
1964
|
-
typeof rawArg === "object" &&
|
|
1965
|
-
typeof (rawArg as { scope?: unknown }).scope === "string"
|
|
1966
|
-
? (rawArg as { scope: string }).scope
|
|
1967
|
-
: "local";
|
|
1968
|
-
const keyValue =
|
|
1969
|
-
rawArg !== null &&
|
|
1970
|
-
typeof rawArg === "object" &&
|
|
1971
|
-
typeof (rawArg as { key?: unknown }).key === "string"
|
|
1972
|
-
? (rawArg as { key: string }).key
|
|
1973
|
-
: "";
|
|
1974
|
-
const valueText =
|
|
1975
|
-
rawArg !== null &&
|
|
1976
|
-
typeof rawArg === "object" &&
|
|
1977
|
-
typeof (rawArg as { value?: unknown }).value === "string"
|
|
1978
|
-
? (rawArg as { value: string }).value
|
|
1979
|
-
: "";
|
|
1980
|
-
const storage = scopeValue === "session" ? window.sessionStorage : window.localStorage;
|
|
1981
|
-
storage.setItem(keyValue, valueText);
|
|
1982
|
-
},
|
|
1983
|
-
{ scope, key, value }
|
|
1984
|
-
);
|
|
1985
|
-
|
|
1986
|
-
return {
|
|
1987
|
-
ok: true,
|
|
1988
|
-
executed: true,
|
|
1989
|
-
data: {
|
|
1990
|
-
scope,
|
|
1991
|
-
key,
|
|
1992
|
-
value,
|
|
1993
|
-
set: true
|
|
1994
|
-
}
|
|
1995
|
-
};
|
|
1996
|
-
}
|
|
1997
|
-
case "frameList": {
|
|
1998
|
-
const customFrameListMethod = getObjectMethod(tab.page, "frameList");
|
|
1999
|
-
if (typeof customFrameListMethod === "function") {
|
|
2000
|
-
const customResult = await customFrameListMethod.call(tab.page);
|
|
2001
|
-
return {
|
|
2002
|
-
ok: true,
|
|
2003
|
-
executed: true,
|
|
2004
|
-
data: isObjectRecord(customResult)
|
|
2005
|
-
? customResult
|
|
2006
|
-
: {
|
|
2007
|
-
frames: []
|
|
2008
|
-
}
|
|
2009
|
-
};
|
|
2010
|
-
}
|
|
2011
|
-
|
|
2012
|
-
const framesMethod = getObjectMethod(tab.page, "frames");
|
|
2013
|
-
if (typeof framesMethod !== "function") {
|
|
2014
|
-
return {
|
|
2015
|
-
ok: true,
|
|
2016
|
-
executed: false
|
|
2017
|
-
};
|
|
2018
|
-
}
|
|
2019
|
-
|
|
2020
|
-
const rawFrames = await framesMethod.call(tab.page);
|
|
2021
|
-
if (!Array.isArray(rawFrames)) {
|
|
2022
|
-
return {
|
|
2023
|
-
ok: true,
|
|
2024
|
-
executed: true,
|
|
2025
|
-
data: {
|
|
2026
|
-
frames: []
|
|
2027
|
-
}
|
|
2028
|
-
};
|
|
2029
|
-
}
|
|
2030
|
-
|
|
2031
|
-
const mainFrameMethod = getObjectMethod(tab.page, "mainFrame");
|
|
2032
|
-
const mainFrame = typeof mainFrameMethod === "function" ? await mainFrameMethod.call(tab.page) : undefined;
|
|
2033
|
-
const frames = rawFrames.map((frame, index) => {
|
|
2034
|
-
const name = readObjectMethod<string>(frame, "name");
|
|
2035
|
-
const url = readObjectMethod<string>(frame, "url") ?? "";
|
|
2036
|
-
return {
|
|
2037
|
-
frameId: `frame:${index}`,
|
|
2038
|
-
index,
|
|
2039
|
-
...(typeof name === "string" && name.length > 0 ? { name } : {}),
|
|
2040
|
-
url,
|
|
2041
|
-
isMainFrame: frame === mainFrame
|
|
2042
|
-
};
|
|
2043
|
-
});
|
|
2044
|
-
|
|
2045
|
-
return {
|
|
2046
|
-
ok: true,
|
|
2047
|
-
executed: true,
|
|
2048
|
-
data: {
|
|
2049
|
-
frames
|
|
2050
|
-
}
|
|
2051
|
-
};
|
|
2052
|
-
}
|
|
2053
|
-
case "frameSnapshot": {
|
|
2054
|
-
const frameId = readActionPayloadString(payload, "frameId");
|
|
2055
|
-
if (frameId === undefined) {
|
|
2056
|
-
return {
|
|
2057
|
-
ok: false,
|
|
2058
|
-
executed: false,
|
|
2059
|
-
error: "action.payload.frameId is required for frameSnapshot action."
|
|
2060
|
-
};
|
|
2061
|
-
}
|
|
2062
|
-
|
|
2063
|
-
const customFrameSnapshotMethod = getObjectMethod(tab.page, "frameSnapshot");
|
|
2064
|
-
if (typeof customFrameSnapshotMethod === "function") {
|
|
2065
|
-
const customResult = await customFrameSnapshotMethod.call(tab.page, frameId);
|
|
2066
|
-
return {
|
|
2067
|
-
ok: true,
|
|
2068
|
-
executed: true,
|
|
2069
|
-
data: isObjectRecord(customResult)
|
|
2070
|
-
? customResult
|
|
2071
|
-
: {
|
|
2072
|
-
frameId,
|
|
2073
|
-
found: false
|
|
2074
|
-
}
|
|
2075
|
-
};
|
|
2076
|
-
}
|
|
2077
|
-
|
|
2078
|
-
const frameIndex = parseFrameIndex(frameId);
|
|
2079
|
-
if (frameIndex === undefined) {
|
|
2080
|
-
return {
|
|
2081
|
-
ok: false,
|
|
2082
|
-
executed: false,
|
|
2083
|
-
error: `Invalid frameId: ${frameId}`
|
|
2084
|
-
};
|
|
2085
|
-
}
|
|
2086
|
-
|
|
2087
|
-
const framesMethod = getObjectMethod(tab.page, "frames");
|
|
2088
|
-
if (typeof framesMethod !== "function") {
|
|
2089
|
-
return {
|
|
2090
|
-
ok: true,
|
|
2091
|
-
executed: false
|
|
2092
|
-
};
|
|
2093
|
-
}
|
|
2094
|
-
|
|
2095
|
-
const rawFrames = await framesMethod.call(tab.page);
|
|
2096
|
-
if (!Array.isArray(rawFrames) || frameIndex >= rawFrames.length) {
|
|
2097
|
-
return {
|
|
2098
|
-
ok: true,
|
|
2099
|
-
executed: true,
|
|
2100
|
-
data: {
|
|
2101
|
-
frameId,
|
|
2102
|
-
found: false
|
|
2103
|
-
}
|
|
2104
|
-
};
|
|
2105
|
-
}
|
|
2106
|
-
|
|
2107
|
-
const frame = rawFrames[frameIndex];
|
|
2108
|
-
const contentMethod = getObjectMethod(frame, "content");
|
|
2109
|
-
if (typeof contentMethod !== "function") {
|
|
2110
|
-
return {
|
|
2111
|
-
ok: true,
|
|
2112
|
-
executed: false
|
|
2113
|
-
};
|
|
2114
|
-
}
|
|
2115
|
-
|
|
2116
|
-
const mainFrameMethod = getObjectMethod(tab.page, "mainFrame");
|
|
2117
|
-
const mainFrame = typeof mainFrameMethod === "function" ? await mainFrameMethod.call(tab.page) : undefined;
|
|
2118
|
-
const frameName = readObjectMethod<string>(frame, "name");
|
|
2119
|
-
const frameUrl = readObjectMethod<string>(frame, "url") ?? "";
|
|
2120
|
-
const frameHtml = await contentMethod.call(frame);
|
|
2121
|
-
|
|
2122
|
-
return {
|
|
2123
|
-
ok: true,
|
|
2124
|
-
executed: true,
|
|
2125
|
-
data: {
|
|
2126
|
-
frameId,
|
|
2127
|
-
index: frameIndex,
|
|
2128
|
-
found: true,
|
|
2129
|
-
...(typeof frameName === "string" && frameName.length > 0 ? { name: frameName } : {}),
|
|
2130
|
-
url: frameUrl,
|
|
2131
|
-
isMainFrame: frame === mainFrame,
|
|
2132
|
-
html: typeof frameHtml === "string" ? frameHtml : ""
|
|
2133
|
-
}
|
|
2134
|
-
};
|
|
2135
|
-
}
|
|
2136
|
-
default:
|
|
2137
|
-
return {
|
|
2138
|
-
ok: true,
|
|
2139
|
-
executed: false
|
|
2140
|
-
};
|
|
2141
|
-
}
|
|
2142
|
-
};
|
|
2143
|
-
|
|
2144
|
-
try {
|
|
2145
|
-
return {
|
|
2146
|
-
...baseResult,
|
|
2147
|
-
...(await performAction())
|
|
2148
|
-
};
|
|
2149
|
-
} catch (error) {
|
|
2150
|
-
return {
|
|
2151
|
-
...baseResult,
|
|
2152
|
-
ok: false,
|
|
2153
|
-
executed: false,
|
|
2154
|
-
error: toErrorMessage(error)
|
|
2155
|
-
};
|
|
2156
|
-
}
|
|
2157
|
-
},
|
|
2158
|
-
armUpload: async (targetId, files, profile) => {
|
|
2159
|
-
const profileId = resolveProfileId(profile);
|
|
2160
|
-
const operationState = getOrCreateTargetOperationState(profileId, targetId);
|
|
2161
|
-
operationState.uploadFiles = [...files];
|
|
2162
|
-
},
|
|
2163
|
-
armDialog: async (targetId, profile) => {
|
|
2164
|
-
const profileId = resolveProfileId(profile);
|
|
2165
|
-
const operationState = getOrCreateTargetOperationState(profileId, targetId);
|
|
2166
|
-
operationState.dialogArmedCount += 1;
|
|
2167
|
-
},
|
|
2168
|
-
waitDownload: async (targetId, profile, path) => {
|
|
2169
|
-
const profileId = resolveProfileId(profile);
|
|
2170
|
-
const { tab } = requireTargetInProfile(profileId, targetId);
|
|
2171
|
-
const operationState = getOrCreateTargetOperationState(profileId, targetId);
|
|
2172
|
-
const requestedPath = path !== undefined && path.trim().length > 0 ? path : undefined;
|
|
2173
|
-
if (operationState.downloadInFlight === undefined && operationState.latestDownload !== undefined) {
|
|
2174
|
-
const download = await resolveDownloadArtifactPath(
|
|
2175
|
-
operationState,
|
|
2176
|
-
{ ...operationState.latestDownload },
|
|
2177
|
-
requestedPath
|
|
2178
|
-
);
|
|
2179
|
-
|
|
2180
|
-
operationState.latestDownload = undefined;
|
|
2181
|
-
operationState.latestRawDownload = undefined;
|
|
2182
|
-
operationState.requestedDownloadPath = undefined;
|
|
2183
|
-
return {
|
|
2184
|
-
path: download.path,
|
|
2185
|
-
profile: profileId,
|
|
2186
|
-
targetId,
|
|
2187
|
-
uploadFiles: [...operationState.uploadFiles],
|
|
2188
|
-
dialogArmedCount: operationState.dialogArmedCount,
|
|
2189
|
-
triggerCount: operationState.triggerCount,
|
|
2190
|
-
...(download.suggestedFilename !== undefined
|
|
2191
|
-
? { suggestedFilename: download.suggestedFilename }
|
|
2192
|
-
: {}),
|
|
2193
|
-
...(download.url !== undefined ? { url: download.url } : {}),
|
|
2194
|
-
...(download.mimeType !== undefined ? { mimeType: download.mimeType } : {})
|
|
2195
|
-
};
|
|
2196
|
-
}
|
|
2197
|
-
|
|
2198
|
-
const inFlightDownload = await ensureDownloadInFlight(
|
|
2199
|
-
profileId,
|
|
2200
|
-
targetId,
|
|
2201
|
-
tab,
|
|
2202
|
-
operationState,
|
|
2203
|
-
requestedPath
|
|
2204
|
-
);
|
|
2205
|
-
const download = await resolveDownloadArtifactPath(operationState, inFlightDownload, requestedPath);
|
|
2206
|
-
operationState.latestDownload = undefined;
|
|
2207
|
-
operationState.latestRawDownload = undefined;
|
|
2208
|
-
operationState.requestedDownloadPath = undefined;
|
|
2209
|
-
|
|
2210
|
-
return {
|
|
2211
|
-
path: download.path,
|
|
2212
|
-
profile: profileId,
|
|
2213
|
-
targetId,
|
|
2214
|
-
uploadFiles: [...operationState.uploadFiles],
|
|
2215
|
-
dialogArmedCount: operationState.dialogArmedCount,
|
|
2216
|
-
triggerCount: operationState.triggerCount,
|
|
2217
|
-
...(download.suggestedFilename !== undefined
|
|
2218
|
-
? { suggestedFilename: download.suggestedFilename }
|
|
2219
|
-
: {}),
|
|
2220
|
-
...(download.url !== undefined ? { url: download.url } : {}),
|
|
2221
|
-
...(download.mimeType !== undefined ? { mimeType: download.mimeType } : {})
|
|
2222
|
-
};
|
|
2223
|
-
},
|
|
2224
|
-
triggerDownload: async (targetId, profile) => {
|
|
2225
|
-
const profileId = resolveProfileId(profile);
|
|
2226
|
-
const { tab } = requireTargetInProfile(profileId, targetId);
|
|
2227
|
-
const operationState = getOrCreateTargetOperationState(profileId, targetId);
|
|
2228
|
-
operationState.triggerCount += 1;
|
|
2229
|
-
operationState.latestDownload = undefined;
|
|
2230
|
-
operationState.latestRawDownload = undefined;
|
|
2231
|
-
operationState.requestedDownloadPath = undefined;
|
|
2232
|
-
void ensureDownloadInFlight(profileId, targetId, tab, operationState);
|
|
2233
|
-
},
|
|
2234
|
-
getConsoleEntries: (targetId, profile) => {
|
|
2235
|
-
const telemetryState = findTelemetryState(targetId, profile);
|
|
2236
|
-
return telemetryState === undefined ? [] : telemetryState.consoleEntries.map((entry) => ({ ...entry }));
|
|
2237
|
-
},
|
|
2238
|
-
getNetworkResponseBody: (requestId, targetId, profile) => {
|
|
2239
|
-
const body = findTelemetryState(targetId, profile)?.networkResponseBodies.get(requestId);
|
|
2240
|
-
return body === undefined ? undefined : { ...body };
|
|
2241
|
-
}
|
|
2242
|
-
};
|
|
2243
|
-
}
|