@orkestrel/browser 0.0.14 → 0.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/src/core/index.cjs +2574 -2355
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +1185 -720
- package/dist/src/core/index.d.ts +1185 -720
- package/dist/src/core/index.js +2544 -2322
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +218 -190
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +159 -110
- package/dist/src/server/index.d.ts +159 -110
- package/dist/src/server/index.js +212 -181
- package/dist/src/server/index.js.map +1 -1
- package/package.json +15 -15
|
@@ -14,35 +14,35 @@ let _orkestrel_emitter = require("@orkestrel/emitter");
|
|
|
14
14
|
let _orkestrel_websocket = require("@orkestrel/websocket");
|
|
15
15
|
let node_events = require("node:events");
|
|
16
16
|
//#region src/server/constants.ts
|
|
17
|
-
/**
|
|
17
|
+
/** Sets the default CDP port probed for an existing browser and used for launches. */
|
|
18
18
|
var BROWSER_DEFAULT_CDP_PORT = 9222;
|
|
19
|
-
/**
|
|
19
|
+
/** Sets the default host probed for an existing browser and used for launches (avoids `localhost` resolving to `::1` when Chromium binds `127.0.0.1`). */
|
|
20
20
|
var BROWSER_DEFAULT_HOST = "127.0.0.1";
|
|
21
|
-
/**
|
|
21
|
+
/** Names the protocol prefix for CDP discovery requests. */
|
|
22
22
|
var BROWSER_CDP_PROTOCOL = "http";
|
|
23
|
-
/**
|
|
23
|
+
/** Names the path appended to the CDP host to fetch version metadata (endpoint discovery). */
|
|
24
24
|
var BROWSER_CDP_VERSION_PATH = "/json/version";
|
|
25
|
-
/**
|
|
25
|
+
/** Names the path appended to the CDP host to list open targets (pages, workers, etc). */
|
|
26
26
|
var BROWSER_CDP_LIST_PATH = "/json/list";
|
|
27
|
-
/**
|
|
27
|
+
/** Lists the flags always passed to a launched browser process, alongside the caller's own. */
|
|
28
28
|
var BROWSER_LAUNCH_ARGS = Object.freeze(["--no-first-run", "--no-default-browser-check"]);
|
|
29
|
-
/**
|
|
29
|
+
/** Names the flag that enables headless mode on a launched browser process. */
|
|
30
30
|
var BROWSER_HEADLESS_ARG = "--headless=new";
|
|
31
|
-
/**
|
|
31
|
+
/** Names the prefix for isolated browser profiles created beneath the operating-system temp directory. */
|
|
32
32
|
var BROWSER_PROFILE_PREFIX = "orkestrel-browser-";
|
|
33
|
-
/**
|
|
33
|
+
/** Bounds each launched-process exit window during TERM-to-KILL teardown. */
|
|
34
34
|
var BROWSER_KILL_GRACE_MS = 3e3;
|
|
35
|
-
/**
|
|
35
|
+
/** Bounds the `discover: false` port-occupancy probe before launching — short, because it only needs to detect an already-listening CDP endpoint, not perform full discovery. */
|
|
36
36
|
var BROWSER_PORT_PROBE_TIMEOUT_MS = 200;
|
|
37
|
-
/**
|
|
37
|
+
/** Defers once, briefly, when a transport loss is observed on an owned process, giving a near-simultaneous process-exit event (which libuv may reap slightly later than the socket close) first say over the diagnosis. */
|
|
38
38
|
var BROWSER_TRANSPORT_LOSS_DEFER_MS = 50;
|
|
39
|
-
/**
|
|
39
|
+
/** Names the machine-readable error-context cause for an owned browser process exiting. */
|
|
40
40
|
var BROWSER_PROCESS_EXIT_CAUSE = "process-exit";
|
|
41
|
-
/**
|
|
41
|
+
/** Names the machine-readable error-context cause for a CDP transport disconnecting while its browser remains alive. */
|
|
42
42
|
var BROWSER_TRANSPORT_LOSS_CAUSE = "transport-loss";
|
|
43
|
-
/**
|
|
43
|
+
/** Lists the environment variables checked (in order) for an explicit browser executable path override. */
|
|
44
44
|
var BROWSER_ENV_PATH_KEYS = Object.freeze(["PLAYWRIGHT_EXECUTABLE_PATH", "CHROME_PATH"]);
|
|
45
|
-
/**
|
|
45
|
+
/** Lists the well-known Chrome/Chromium/Edge executable paths with no platform-specific root, keyed by `process.platform`. */
|
|
46
46
|
var BROWSER_EXECUTABLE_PATHS = Object.freeze({
|
|
47
47
|
linux: Object.freeze([
|
|
48
48
|
"/usr/bin/google-chrome",
|
|
@@ -62,18 +62,18 @@ var BROWSER_EXECUTABLE_PATHS = Object.freeze({
|
|
|
62
62
|
]),
|
|
63
63
|
win32: Object.freeze([])
|
|
64
64
|
});
|
|
65
|
-
/** Windows install-root-relative suffixes for Chrome/Edge/Chromium, joined against each candidate root (`PROGRAMFILES`, `PROGRAMFILES(X86)`, `LOCALAPPDATA`). */
|
|
65
|
+
/** Lists the Windows install-root-relative suffixes for Chrome/Edge/Chromium, joined against each candidate root (`PROGRAMFILES`, `PROGRAMFILES(X86)`, `LOCALAPPDATA`). */
|
|
66
66
|
var BROWSER_WINDOWS_SUFFIXES = Object.freeze([
|
|
67
67
|
"Google\\Chrome\\Application\\chrome.exe",
|
|
68
68
|
"Microsoft\\Edge\\Application\\msedge.exe",
|
|
69
69
|
"Chromium\\Application\\chrome.exe"
|
|
70
70
|
]);
|
|
71
|
-
/**
|
|
71
|
+
/** Lists the fallback Windows install roots used when the corresponding environment variable is absent. */
|
|
72
72
|
var BROWSER_WINDOWS_ROOT_FALLBACKS = Object.freeze({
|
|
73
73
|
PROGRAMFILES: "C:\\Program Files",
|
|
74
74
|
"PROGRAMFILES(X86)": "C:\\Program Files (x86)"
|
|
75
75
|
});
|
|
76
|
-
/**
|
|
76
|
+
/** Lists the command names probed on PATH when no well-known executable path exists. */
|
|
77
77
|
var BROWSER_EXECUTABLE_NAMES = Object.freeze([
|
|
78
78
|
"google-chrome",
|
|
79
79
|
"google-chrome-stable",
|
|
@@ -83,19 +83,19 @@ var BROWSER_EXECUTABLE_NAMES = Object.freeze([
|
|
|
83
83
|
"chromium-browser",
|
|
84
84
|
"chrome"
|
|
85
85
|
]);
|
|
86
|
-
/**
|
|
86
|
+
/** Names the environment variable that carries an additional Playwright browser store base directory. */
|
|
87
87
|
var BROWSER_STORE_ENV_KEY = "PLAYWRIGHT_BROWSERS_PATH";
|
|
88
|
-
/**
|
|
88
|
+
/** Lists the well-known Playwright browser store base directories checked in addition to `PLAYWRIGHT_BROWSERS_PATH`. */
|
|
89
89
|
var BROWSER_STORE_DEFAULT_DIRS = Object.freeze(["/opt/pw-browsers"]);
|
|
90
|
-
/**
|
|
90
|
+
/** Names the per-OS default Playwright browser cache directory, relative to the home directory (win32 uses `LOCALAPPDATA` directly). */
|
|
91
91
|
var BROWSER_STORE_CACHE_DIRS = Object.freeze({
|
|
92
92
|
linux: ".cache/ms-playwright",
|
|
93
93
|
darwin: "Library/Caches/ms-playwright"
|
|
94
94
|
});
|
|
95
|
-
/**
|
|
95
|
+
/** Names the top-level Chromium symlink/binary Playwright maintains inside a browser store base. */
|
|
96
96
|
var BROWSER_STORE_LINK_NAME = "chromium";
|
|
97
97
|
/**
|
|
98
|
-
*
|
|
98
|
+
* Lists the case-insensitive substrings identifying an executable path/name's browser
|
|
99
99
|
* engine, checked by `parseBrowserEngine` in the order `edge` → `chromium` → `chrome`.
|
|
100
100
|
*/
|
|
101
101
|
var BROWSER_ENGINE_HINTS = Object.freeze({
|
|
@@ -119,7 +119,7 @@ var BROWSER_ENGINE_HINTS = Object.freeze({
|
|
|
119
119
|
"chrome"
|
|
120
120
|
])
|
|
121
121
|
});
|
|
122
|
-
/**
|
|
122
|
+
/** Names the glob pattern (relative to a store base) matching a versioned Chromium binary, keyed by `process.platform`. */
|
|
123
123
|
var BROWSER_STORE_GLOBS = Object.freeze({
|
|
124
124
|
linux: "chromium-*/chrome-linux*/chrome",
|
|
125
125
|
darwin: "chromium-*/chrome-mac*/Chromium.app/Contents/MacOS/Chromium",
|
|
@@ -128,7 +128,7 @@ var BROWSER_STORE_GLOBS = Object.freeze({
|
|
|
128
128
|
//#endregion
|
|
129
129
|
//#region src/server/errors.ts
|
|
130
130
|
/**
|
|
131
|
-
*
|
|
131
|
+
* Reports that a CDP connection, discovery, or launch attempt failed.
|
|
132
132
|
*/
|
|
133
133
|
var BrowserConnectionError = class extends _src_core.BrowserError {
|
|
134
134
|
constructor(message, context) {
|
|
@@ -137,7 +137,7 @@ var BrowserConnectionError = class extends _src_core.BrowserError {
|
|
|
137
137
|
}
|
|
138
138
|
};
|
|
139
139
|
/**
|
|
140
|
-
*
|
|
140
|
+
* Reports that an operation requiring an active connection was attempted while disconnected.
|
|
141
141
|
*/
|
|
142
142
|
var BrowserNotConnectedError = class extends _src_core.BrowserError {
|
|
143
143
|
constructor(context) {
|
|
@@ -146,7 +146,7 @@ var BrowserNotConnectedError = class extends _src_core.BrowserError {
|
|
|
146
146
|
}
|
|
147
147
|
};
|
|
148
148
|
/**
|
|
149
|
-
*
|
|
149
|
+
* Reports that an operation was attempted after the Browser was destroyed.
|
|
150
150
|
*/
|
|
151
151
|
var BrowserDestroyedError = class extends _src_core.BrowserError {
|
|
152
152
|
constructor(context) {
|
|
@@ -155,28 +155,28 @@ var BrowserDestroyedError = class extends _src_core.BrowserError {
|
|
|
155
155
|
}
|
|
156
156
|
};
|
|
157
157
|
/**
|
|
158
|
-
*
|
|
158
|
+
* Narrows an unknown value to BrowserConnectionError.
|
|
159
159
|
*
|
|
160
160
|
* @param value - Value to check
|
|
161
|
-
* @returns True
|
|
161
|
+
* @returns True if value is a BrowserConnectionError instance; false otherwise
|
|
162
162
|
*/
|
|
163
163
|
function isBrowserConnectionError(value) {
|
|
164
164
|
return (0, _orkestrel_contract.isInstance)(value, BrowserConnectionError);
|
|
165
165
|
}
|
|
166
166
|
/**
|
|
167
|
-
*
|
|
167
|
+
* Narrows an unknown value to BrowserNotConnectedError.
|
|
168
168
|
*
|
|
169
169
|
* @param value - Value to check
|
|
170
|
-
* @returns True
|
|
170
|
+
* @returns True if value is a BrowserNotConnectedError instance; false otherwise
|
|
171
171
|
*/
|
|
172
172
|
function isBrowserNotConnectedError(value) {
|
|
173
173
|
return (0, _orkestrel_contract.isInstance)(value, BrowserNotConnectedError);
|
|
174
174
|
}
|
|
175
175
|
/**
|
|
176
|
-
*
|
|
176
|
+
* Narrows an unknown value to BrowserDestroyedError.
|
|
177
177
|
*
|
|
178
178
|
* @param value - Value to check
|
|
179
|
-
* @returns True
|
|
179
|
+
* @returns True if value is a BrowserDestroyedError instance; false otherwise
|
|
180
180
|
*/
|
|
181
181
|
function isBrowserDestroyedError(value) {
|
|
182
182
|
return (0, _orkestrel_contract.isInstance)(value, BrowserDestroyedError);
|
|
@@ -184,7 +184,7 @@ function isBrowserDestroyedError(value) {
|
|
|
184
184
|
//#endregion
|
|
185
185
|
//#region src/server/helpers.ts
|
|
186
186
|
/**
|
|
187
|
-
*
|
|
187
|
+
* Enumerates every Chrome/Chromium/Edge executable discoverable on this
|
|
188
188
|
* machine, deduplicated by normalized absolute path.
|
|
189
189
|
*
|
|
190
190
|
* @remarks
|
|
@@ -198,7 +198,7 @@ function isBrowserDestroyedError(value) {
|
|
|
198
198
|
* and the per-OS Playwright cache directory) — the top-level `chromium`
|
|
199
199
|
* link/binary, then the highest-revision `chromium-*` install found
|
|
200
200
|
*
|
|
201
|
-
* Each candidate is classified into a {@link BrowserEngine}
|
|
201
|
+
* Each candidate is classified into a {@link BrowserEngine} through
|
|
202
202
|
* `parseBrowserEngine` (unclassifiable candidates default to `'chromium'`),
|
|
203
203
|
* and `options.engine` (when given) narrows the result to that engine.
|
|
204
204
|
*
|
|
@@ -209,13 +209,13 @@ function findSystemBrowsers(options) {
|
|
|
209
209
|
const platform = process.platform;
|
|
210
210
|
const env = options?.env ?? process.env;
|
|
211
211
|
const candidates = [];
|
|
212
|
-
candidates.push(...
|
|
213
|
-
const paths = options?.paths ??
|
|
214
|
-
candidates.push(...
|
|
212
|
+
candidates.push(...findEnvOverrides(env));
|
|
213
|
+
const paths = options?.paths ?? buildInstallPaths(platform, env);
|
|
214
|
+
candidates.push(...findInstallPaths(paths));
|
|
215
215
|
const names = options?.names ?? BROWSER_EXECUTABLE_NAMES;
|
|
216
|
-
candidates.push(...
|
|
217
|
-
const stores = options?.stores ??
|
|
218
|
-
for (const store of stores) candidates.push(...
|
|
216
|
+
candidates.push(...probePathNames(names, platform));
|
|
217
|
+
const stores = options?.stores ?? buildStoreBases(env, platform);
|
|
218
|
+
for (const store of stores) candidates.push(...findStorePaths(store, platform));
|
|
219
219
|
const seen = /* @__PURE__ */ new Set();
|
|
220
220
|
const browsers = [];
|
|
221
221
|
for (const executable of candidates) {
|
|
@@ -232,7 +232,7 @@ function findSystemBrowsers(options) {
|
|
|
232
232
|
return browsers;
|
|
233
233
|
}
|
|
234
234
|
/**
|
|
235
|
-
*
|
|
235
|
+
* Locates a Chrome/Chromium/Edge executable on this machine — the first entry
|
|
236
236
|
* of {@link findSystemBrowsers}.
|
|
237
237
|
*
|
|
238
238
|
* @param options - Overrides for the candidate sources; see {@link SystemBrowserOptions}
|
|
@@ -242,7 +242,7 @@ function findSystemBrowser(options) {
|
|
|
242
242
|
return findSystemBrowsers(options)[0];
|
|
243
243
|
}
|
|
244
244
|
/**
|
|
245
|
-
*
|
|
245
|
+
* Classifies an executable path/name into a {@link BrowserEngine} by
|
|
246
246
|
* case-insensitive hint, checked in the order edge → chromium → chrome.
|
|
247
247
|
*
|
|
248
248
|
* @param executable - Executable path or command name to classify
|
|
@@ -256,19 +256,19 @@ function parseBrowserEngine(executable) {
|
|
|
256
256
|
"chrome"
|
|
257
257
|
]) if (BROWSER_ENGINE_HINTS[engine].some((hint) => lower.includes(hint))) return engine;
|
|
258
258
|
}
|
|
259
|
-
/**
|
|
259
|
+
/** Normalizes an executable path for cross-source deduplication (case-insensitive on Windows). */
|
|
260
260
|
function normalizeExecutablePath(path, platform) {
|
|
261
261
|
const normalized = platform === "win32" ? node_path.win32.normalize(path) : node_path.posix.normalize(path);
|
|
262
262
|
return platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
263
263
|
}
|
|
264
|
-
/**
|
|
264
|
+
/** Classifies a `/json/version` `Browser` string into a {@link BrowserEngine} (`Edg/` → edge, `Chrome/` → chrome, else chromium). */
|
|
265
265
|
function browserToEngine(browser) {
|
|
266
266
|
if (browser?.includes("Edg/") === true) return "edge";
|
|
267
267
|
if (browser?.includes("Chrome/") === true) return "chrome";
|
|
268
268
|
return "chromium";
|
|
269
269
|
}
|
|
270
270
|
/**
|
|
271
|
-
*
|
|
271
|
+
* Resolves a persistent caller profile or creates an isolated temporary one.
|
|
272
272
|
*
|
|
273
273
|
* @param path - Optional caller-owned persistent user-data directory
|
|
274
274
|
* @returns The resolved profile and whether the library owns its lifecycle
|
|
@@ -284,7 +284,7 @@ async function createBrowserProfile(path) {
|
|
|
284
284
|
};
|
|
285
285
|
}
|
|
286
286
|
/**
|
|
287
|
-
*
|
|
287
|
+
* Removes a library-owned isolated browser profile.
|
|
288
288
|
*
|
|
289
289
|
* @remarks
|
|
290
290
|
* The path must remain a direct child of the operating-system temp directory
|
|
@@ -297,7 +297,7 @@ async function removeBrowserProfile(profile) {
|
|
|
297
297
|
if (!profile.temporary) return;
|
|
298
298
|
const path = (0, node_path.resolve)(profile.path);
|
|
299
299
|
const temporary = (0, node_path.resolve)((0, node_os.tmpdir)());
|
|
300
|
-
if ((0, node_path.dirname)(path) !== temporary || !(0, node_path.basename)(path).startsWith("orkestrel-browser-")) throw new
|
|
300
|
+
if ((0, node_path.dirname)(path) !== temporary || !(0, node_path.basename)(path).startsWith("orkestrel-browser-")) throw new _src_core.BrowserError("Refusing to remove an unsafe browser profile path", void 0, { path });
|
|
301
301
|
await (0, node_fs_promises.rm)(path, {
|
|
302
302
|
recursive: true,
|
|
303
303
|
force: true,
|
|
@@ -305,12 +305,8 @@ async function removeBrowserProfile(profile) {
|
|
|
305
305
|
retryDelay: 100
|
|
306
306
|
});
|
|
307
307
|
}
|
|
308
|
-
/**
|
|
309
|
-
function
|
|
310
|
-
return findAllEnvOverrides(env)[0];
|
|
311
|
-
}
|
|
312
|
-
/** Check the env-override keys (`PLAYWRIGHT_EXECUTABLE_PATH`, `CHROME_PATH`) in order, returning every one that exists. */
|
|
313
|
-
function findAllEnvOverrides(env) {
|
|
308
|
+
/** Checks the env-override keys (`PLAYWRIGHT_EXECUTABLE_PATH`, `CHROME_PATH`) in order and returns every one that exists. */
|
|
309
|
+
function findEnvOverrides(env) {
|
|
314
310
|
const found = [];
|
|
315
311
|
for (const key of BROWSER_ENV_PATH_KEYS) {
|
|
316
312
|
const value = env[key];
|
|
@@ -318,36 +314,28 @@ function findAllEnvOverrides(env) {
|
|
|
318
314
|
}
|
|
319
315
|
return found;
|
|
320
316
|
}
|
|
321
|
-
/**
|
|
322
|
-
function
|
|
317
|
+
/** Builds the default well-known install-path candidates for a platform, deriving Windows roots from env vars. */
|
|
318
|
+
function buildInstallPaths(platform, env) {
|
|
323
319
|
if (platform !== "win32") return BROWSER_EXECUTABLE_PATHS[platform] ?? [];
|
|
324
|
-
const roots =
|
|
320
|
+
const roots = buildWindowsRoots(env);
|
|
325
321
|
const paths = [];
|
|
326
322
|
for (const root of roots) for (const suffix of BROWSER_WINDOWS_SUFFIXES) paths.push(node_path.win32.join(root, suffix));
|
|
327
323
|
return paths;
|
|
328
324
|
}
|
|
329
|
-
/**
|
|
330
|
-
function
|
|
325
|
+
/** Derives Windows install roots from env vars, falling back to well-known literals when absent. */
|
|
326
|
+
function buildWindowsRoots(env) {
|
|
331
327
|
return [
|
|
332
328
|
env["PROGRAMFILES"] ?? BROWSER_WINDOWS_ROOT_FALLBACKS["PROGRAMFILES"],
|
|
333
329
|
env["PROGRAMFILES(X86)"] ?? BROWSER_WINDOWS_ROOT_FALLBACKS["PROGRAMFILES(X86)"],
|
|
334
330
|
env["LOCALAPPDATA"]
|
|
335
331
|
].filter((root) => root !== void 0);
|
|
336
332
|
}
|
|
337
|
-
/**
|
|
338
|
-
function
|
|
339
|
-
return findAllInstallPaths(paths)[0];
|
|
340
|
-
}
|
|
341
|
-
/** Return every candidate path that exists on disk, in the given order. */
|
|
342
|
-
function findAllInstallPaths(paths) {
|
|
333
|
+
/** Returns every candidate path that exists on disk, in the given order. */
|
|
334
|
+
function findInstallPaths(paths) {
|
|
343
335
|
return paths.filter((path) => (0, node_fs.existsSync)(path));
|
|
344
336
|
}
|
|
345
|
-
/**
|
|
337
|
+
/** Probes PATH (`which`/`where`) for every resolvable command name, in the given order. */
|
|
346
338
|
function probePathNames(names, platform) {
|
|
347
|
-
return probeAllPathNames(names, platform)[0];
|
|
348
|
-
}
|
|
349
|
-
/** Probe PATH (`which`/`where`) for every resolvable command name, in the given order. */
|
|
350
|
-
function probeAllPathNames(names, platform) {
|
|
351
339
|
const finder = platform === "win32" ? "where" : "which";
|
|
352
340
|
const found = [];
|
|
353
341
|
for (const name of names) {
|
|
@@ -382,8 +370,8 @@ function readFirstLine(output) {
|
|
|
382
370
|
if (text.length > 0) return text;
|
|
383
371
|
}
|
|
384
372
|
}
|
|
385
|
-
/**
|
|
386
|
-
function
|
|
373
|
+
/** Builds the default Playwright browser store base directories to search for a managed Chromium. */
|
|
374
|
+
function buildStoreBases(env, platform) {
|
|
387
375
|
const bases = [];
|
|
388
376
|
const envBase = env[BROWSER_STORE_ENV_KEY];
|
|
389
377
|
if (envBase !== void 0 && envBase.length > 0) bases.push(envBase);
|
|
@@ -398,12 +386,8 @@ function defaultStoreBases(env, platform) {
|
|
|
398
386
|
}
|
|
399
387
|
return bases;
|
|
400
388
|
}
|
|
401
|
-
/**
|
|
402
|
-
function
|
|
403
|
-
return findAllInStore(base, platform)[0];
|
|
404
|
-
}
|
|
405
|
-
/** Search one store base for the top-level `chromium` link and every `chromium-*` install, highest revision first. */
|
|
406
|
-
function findAllInStore(base, platform) {
|
|
389
|
+
/** Searches one store base for the top-level `chromium` link and every `chromium-*` install, highest revision first. */
|
|
390
|
+
function findStorePaths(base, platform) {
|
|
407
391
|
const joiner = platform === "win32" ? node_path.win32.join : node_path.posix.join;
|
|
408
392
|
const found = [];
|
|
409
393
|
const linkPath = joiner(base, BROWSER_STORE_LINK_NAME);
|
|
@@ -417,7 +401,7 @@ function findAllInStore(base, platform) {
|
|
|
417
401
|
return found;
|
|
418
402
|
}
|
|
419
403
|
/**
|
|
420
|
-
*
|
|
404
|
+
* Launches a browser process with raw-CDP debugging flags.
|
|
421
405
|
*
|
|
422
406
|
* @remarks
|
|
423
407
|
* POSIX launches own an isolated process group so lifecycle teardown can
|
|
@@ -447,11 +431,12 @@ function launchBrowserProcess(executable, port, headless, profile, extra) {
|
|
|
447
431
|
});
|
|
448
432
|
}
|
|
449
433
|
/**
|
|
450
|
-
*
|
|
434
|
+
* Polls a browser's CDP version endpoint until it responds or the timeout elapses.
|
|
451
435
|
*
|
|
452
436
|
* @param port - Port the browser exposes its CDP endpoint on
|
|
453
437
|
* @param timeout - Maximum time to wait in milliseconds
|
|
454
438
|
* @param host - Host the browser exposes its CDP endpoint on (default `127.0.0.1`)
|
|
439
|
+
* @param signal - Optional external abort; an abort while waiting rethrows rather than resolving
|
|
455
440
|
* @returns The browser's WebSocket debugger URL
|
|
456
441
|
*
|
|
457
442
|
* @throws When the endpoint does not become ready before the timeout
|
|
@@ -475,15 +460,33 @@ async function waitForCDPReady(port, timeout, host = BROWSER_DEFAULT_HOST, signa
|
|
|
475
460
|
const delay = Math.min(_src_core.BROWSER_WAIT_POLL_INTERVAL_MS, Math.max(0, deadline - Date.now()));
|
|
476
461
|
if (delay > 0) await (0, node_timers_promises.setTimeout)(delay, void 0, { signal });
|
|
477
462
|
}
|
|
478
|
-
throw new
|
|
463
|
+
throw new BrowserConnectionError(`CDP endpoint on port ${port} did not become ready within ${timeout}ms`, {
|
|
464
|
+
port,
|
|
465
|
+
timeout
|
|
466
|
+
});
|
|
479
467
|
}
|
|
480
468
|
/**
|
|
481
|
-
*
|
|
469
|
+
* Fetches the current CDP target list from a browser's `/json/list` endpoint.
|
|
470
|
+
*
|
|
471
|
+
* @remarks
|
|
472
|
+
* The endpoint is a network boundary, so an unreachable host, a non-2xx
|
|
473
|
+
* response, and a body that is not a JSON array each come back as a failed
|
|
474
|
+
* `Result` carrying a coded `BrowserConnectionError`. An entry missing a
|
|
475
|
+
* required string field is skipped rather than failing the whole list.
|
|
482
476
|
*
|
|
483
477
|
* @param port - Port the browser exposes its CDP endpoint on
|
|
484
478
|
* @param timeout - Request timeout in milliseconds
|
|
485
479
|
* @param host - Host the browser exposes its CDP endpoint on (default `127.0.0.1`)
|
|
486
|
-
* @returns
|
|
480
|
+
* @returns The normalized CDP targets, or the fault that stopped the request
|
|
481
|
+
*
|
|
482
|
+
* @example
|
|
483
|
+
* ```ts
|
|
484
|
+
* import { fetchCDPTargets } from '@orkestrel/browser/server'
|
|
485
|
+
*
|
|
486
|
+
* const result = await fetchCDPTargets(9222, 2000)
|
|
487
|
+
* if (result.success) log(result.value.length)
|
|
488
|
+
* else log(result.error.code)
|
|
489
|
+
* ```
|
|
487
490
|
*/
|
|
488
491
|
async function fetchCDPTargets(port, timeout, host = BROWSER_DEFAULT_HOST) {
|
|
489
492
|
const url = `${BROWSER_CDP_PROTOCOL}://${host}:${port}${BROWSER_CDP_LIST_PATH}`;
|
|
@@ -491,23 +494,41 @@ async function fetchCDPTargets(port, timeout, host = BROWSER_DEFAULT_HOST) {
|
|
|
491
494
|
const timer = setTimeout(() => controller.abort(), timeout);
|
|
492
495
|
try {
|
|
493
496
|
const response = await fetch(url, { signal: controller.signal });
|
|
494
|
-
if (!response.ok) return
|
|
497
|
+
if (!response.ok) return {
|
|
498
|
+
success: false,
|
|
499
|
+
error: new BrowserConnectionError("CDP target list request was refused", {
|
|
500
|
+
url,
|
|
501
|
+
status: response.status
|
|
502
|
+
})
|
|
503
|
+
};
|
|
495
504
|
const list = await response.json();
|
|
496
|
-
if (!(0, _orkestrel_contract.isArray)(list)) return
|
|
505
|
+
if (!(0, _orkestrel_contract.isArray)(list)) return {
|
|
506
|
+
success: false,
|
|
507
|
+
error: new BrowserConnectionError("CDP target list is not a JSON array", { url })
|
|
508
|
+
};
|
|
497
509
|
const targets = [];
|
|
498
510
|
for (const entry of list) {
|
|
499
511
|
if (!(0, _orkestrel_contract.isRecord)(entry)) continue;
|
|
500
512
|
if (!(0, _orkestrel_contract.isString)(entry["id"]) || !(0, _orkestrel_contract.isString)(entry["type"]) || !(0, _orkestrel_contract.isString)(entry["title"]) || !(0, _orkestrel_contract.isString)(entry["url"])) continue;
|
|
501
513
|
targets.push({
|
|
502
514
|
id: entry["id"],
|
|
503
|
-
|
|
515
|
+
category: entry["type"],
|
|
504
516
|
title: entry["title"],
|
|
505
517
|
url: entry["url"]
|
|
506
518
|
});
|
|
507
519
|
}
|
|
508
|
-
return
|
|
509
|
-
|
|
510
|
-
|
|
520
|
+
return {
|
|
521
|
+
success: true,
|
|
522
|
+
value: targets
|
|
523
|
+
};
|
|
524
|
+
} catch (error) {
|
|
525
|
+
return {
|
|
526
|
+
success: false,
|
|
527
|
+
error: new BrowserConnectionError("CDP target list is unreachable", {
|
|
528
|
+
url,
|
|
529
|
+
error
|
|
530
|
+
})
|
|
531
|
+
};
|
|
511
532
|
} finally {
|
|
512
533
|
clearTimeout(timer);
|
|
513
534
|
}
|
|
@@ -515,27 +536,36 @@ async function fetchCDPTargets(port, timeout, host = BROWSER_DEFAULT_HOST) {
|
|
|
515
536
|
//#endregion
|
|
516
537
|
//#region src/server/transports/WebSocketCDPTransport.ts
|
|
517
538
|
/**
|
|
518
|
-
*
|
|
539
|
+
* Provides a raw CDP text transport backed by `@orkestrel/websocket`.
|
|
519
540
|
*
|
|
520
541
|
* @remarks
|
|
521
542
|
* Performs and validates the RFC 6455 HTTP upgrade, then delegates frame
|
|
522
543
|
* handling to `@orkestrel/websocket`. Concurrent and repeated `start()` /
|
|
523
544
|
* `close()` calls share their active transition, and a later `start()` opens a
|
|
524
545
|
* fresh socket after the prior one closes.
|
|
546
|
+
*
|
|
547
|
+
* @example
|
|
548
|
+
* ```ts
|
|
549
|
+
* import { WebSocketCDPTransport } from '@orkestrel/browser/server'
|
|
550
|
+
*
|
|
551
|
+
* const transport = new WebSocketCDPTransport({ url: 'ws://127.0.0.1:9222/devtools/browser/abc' })
|
|
552
|
+
* await transport.start()
|
|
553
|
+
* await transport.close()
|
|
554
|
+
* ```
|
|
525
555
|
*/
|
|
526
556
|
var WebSocketCDPTransport = class {
|
|
527
557
|
#emitter;
|
|
528
558
|
#url;
|
|
529
559
|
#timeout;
|
|
530
560
|
#socket;
|
|
531
|
-
#starting;
|
|
532
|
-
#closing;
|
|
561
|
+
#starting = new _src_core.BrowserTransition();
|
|
562
|
+
#closing = new _src_core.BrowserTransition();
|
|
533
563
|
#request;
|
|
534
564
|
#resolve;
|
|
535
565
|
#reject;
|
|
536
566
|
#timer;
|
|
537
567
|
#closeResolve;
|
|
538
|
-
#
|
|
568
|
+
#socketCloseHandler = this.#handleSocketClose.bind(this);
|
|
539
569
|
constructor(options) {
|
|
540
570
|
this.#emitter = new _orkestrel_emitter.Emitter({
|
|
541
571
|
...options.on !== void 0 ? { on: options.on } : {},
|
|
@@ -548,43 +578,27 @@ var WebSocketCDPTransport = class {
|
|
|
548
578
|
return this.#emitter;
|
|
549
579
|
}
|
|
550
580
|
async start() {
|
|
551
|
-
|
|
581
|
+
const closing = this.#closing.pending;
|
|
582
|
+
if (closing !== void 0) await closing;
|
|
552
583
|
if (this.#socket?.readyState === _orkestrel_websocket.WEBSOCKET_READY_OPEN) return;
|
|
553
|
-
const active = this.#starting;
|
|
584
|
+
const active = this.#starting.pending;
|
|
554
585
|
if (active !== void 0) {
|
|
555
586
|
await active;
|
|
556
587
|
return;
|
|
557
588
|
}
|
|
558
589
|
this.#socket?.destroy();
|
|
559
590
|
this.#socket = void 0;
|
|
560
|
-
|
|
561
|
-
this.#starting = transition;
|
|
562
|
-
try {
|
|
563
|
-
await transition;
|
|
564
|
-
} finally {
|
|
565
|
-
if (this.#starting === transition) this.#starting = void 0;
|
|
566
|
-
}
|
|
591
|
+
await this.#starting.execute(() => this.#start());
|
|
567
592
|
}
|
|
568
593
|
async send(data) {
|
|
569
594
|
const socket = this.#socket;
|
|
570
|
-
if (socket === void 0 || socket.readyState !== _orkestrel_websocket.WEBSOCKET_READY_OPEN) throw new
|
|
595
|
+
if (socket === void 0 || socket.readyState !== _orkestrel_websocket.WEBSOCKET_READY_OPEN) throw new BrowserConnectionError("WebSocket CDP transport is not open", { url: this.#url });
|
|
571
596
|
socket.send(data);
|
|
572
597
|
}
|
|
573
598
|
async close() {
|
|
574
|
-
|
|
575
|
-
if (active !== void 0) {
|
|
576
|
-
await active;
|
|
577
|
-
return;
|
|
578
|
-
}
|
|
579
|
-
const transition = this.#stop();
|
|
580
|
-
this.#closing = transition;
|
|
581
|
-
try {
|
|
582
|
-
await transition;
|
|
583
|
-
} finally {
|
|
584
|
-
if (this.#closing === transition) this.#closing = void 0;
|
|
585
|
-
}
|
|
599
|
+
await this.#closing.execute(() => this.#close());
|
|
586
600
|
}
|
|
587
|
-
async #
|
|
601
|
+
async #start() {
|
|
588
602
|
const parsed = (0, _orkestrel_contract.attempt)(() => new URL(this.#url));
|
|
589
603
|
if (!parsed.success) throw new BrowserConnectionError(`WebSocket CDP URL is invalid: ${this.#url}`, {
|
|
590
604
|
url: this.#url,
|
|
@@ -620,19 +634,19 @@ var WebSocketCDPTransport = class {
|
|
|
620
634
|
this.#socket = socket;
|
|
621
635
|
this.#bind(socket);
|
|
622
636
|
}
|
|
623
|
-
async #
|
|
637
|
+
async #close() {
|
|
624
638
|
const request = this.#request;
|
|
625
639
|
if (request !== void 0) {
|
|
626
640
|
this.#rejectRequest(request, new BrowserConnectionError(`WebSocket CDP connection to ${this.#url} was closed before it finished connecting`, { url: this.#url }));
|
|
627
641
|
request.destroy();
|
|
628
642
|
}
|
|
629
|
-
await this.#starting?.catch(() => void 0);
|
|
643
|
+
await this.#starting.pending?.catch(() => void 0);
|
|
630
644
|
const socket = this.#socket;
|
|
631
645
|
this.#socket = void 0;
|
|
632
646
|
if (socket === void 0 || socket.readyState === _orkestrel_websocket.WEBSOCKET_READY_CLOSED) return;
|
|
633
647
|
const closed = Promise.withResolvers();
|
|
634
648
|
this.#closeResolve = closed.resolve;
|
|
635
|
-
socket.emitter.on("close", this.#
|
|
649
|
+
socket.emitter.on("close", this.#socketCloseHandler);
|
|
636
650
|
try {
|
|
637
651
|
socket.close();
|
|
638
652
|
if (socket.readyState !== _orkestrel_websocket.WEBSOCKET_READY_CLOSED) await closed.promise;
|
|
@@ -640,7 +654,7 @@ var WebSocketCDPTransport = class {
|
|
|
640
654
|
socket.destroy();
|
|
641
655
|
if (socket.readyState !== _orkestrel_websocket.WEBSOCKET_READY_CLOSED) await closed.promise;
|
|
642
656
|
} finally {
|
|
643
|
-
socket.emitter.off("close", this.#
|
|
657
|
+
socket.emitter.off("close", this.#socketCloseHandler);
|
|
644
658
|
this.#closeResolve = void 0;
|
|
645
659
|
}
|
|
646
660
|
}
|
|
@@ -715,16 +729,46 @@ var WebSocketCDPTransport = class {
|
|
|
715
729
|
}
|
|
716
730
|
};
|
|
717
731
|
//#endregion
|
|
732
|
+
//#region src/server/writers/FileBrowserWriter.ts
|
|
733
|
+
/**
|
|
734
|
+
* Persists captured browser bytes to the filesystem through `node:fs/promises`.
|
|
735
|
+
*
|
|
736
|
+
* @remarks
|
|
737
|
+
* Creates every missing parent directory of the target path before writing, so
|
|
738
|
+
* a caller may name a nested destination that does not exist yet.
|
|
739
|
+
*
|
|
740
|
+
* @example
|
|
741
|
+
* ```ts
|
|
742
|
+
* import { FileBrowserWriter } from '@orkestrel/browser/server'
|
|
743
|
+
*
|
|
744
|
+
* const writer = new FileBrowserWriter()
|
|
745
|
+
* await writer.write('shots/hero.png', new Uint8Array([137, 80, 78, 71]))
|
|
746
|
+
* ```
|
|
747
|
+
*/
|
|
748
|
+
var FileBrowserWriter = class {
|
|
749
|
+
/**
|
|
750
|
+
* Writes the captured bytes to a path, creating its parent directories.
|
|
751
|
+
*
|
|
752
|
+
* @param path - Destination file path
|
|
753
|
+
* @param data - Captured bytes to persist
|
|
754
|
+
* @returns A promise resolving once the bytes are on disk
|
|
755
|
+
*/
|
|
756
|
+
async write(path, data) {
|
|
757
|
+
await (0, node_fs_promises.mkdir)((0, node_path.dirname)(path), { recursive: true });
|
|
758
|
+
await (0, node_fs_promises.writeFile)(path, data);
|
|
759
|
+
}
|
|
760
|
+
};
|
|
761
|
+
//#endregion
|
|
718
762
|
//#region src/server/factories.ts
|
|
719
763
|
/**
|
|
720
|
-
*
|
|
764
|
+
* Creates a raw-CDP Browser façade.
|
|
721
765
|
*
|
|
722
766
|
* @param options - Connection, launch, and viewport configuration
|
|
723
767
|
* @returns A {@link BrowserInterface}
|
|
724
768
|
*
|
|
725
769
|
* @example
|
|
726
770
|
* ```ts
|
|
727
|
-
* import { createBrowser } from '@
|
|
771
|
+
* import { createBrowser } from '@orkestrel/browser/server'
|
|
728
772
|
*
|
|
729
773
|
* const browser = createBrowser()
|
|
730
774
|
* await browser.connect()
|
|
@@ -734,7 +778,7 @@ function createBrowser(options) {
|
|
|
734
778
|
return new Browser(options);
|
|
735
779
|
}
|
|
736
780
|
/**
|
|
737
|
-
*
|
|
781
|
+
* Creates a Node `WebSocket`-backed CDP transport.
|
|
738
782
|
*
|
|
739
783
|
* @param options - The CDP WebSocket debugger URL (and optional timeout)
|
|
740
784
|
* @returns A {@link CDPTransportInterface}
|
|
@@ -743,20 +787,27 @@ function createCDPTransport(options) {
|
|
|
743
787
|
return new WebSocketCDPTransport(options);
|
|
744
788
|
}
|
|
745
789
|
/**
|
|
746
|
-
*
|
|
790
|
+
* Creates a filesystem-backed browser writer.
|
|
747
791
|
*
|
|
748
|
-
* @returns A {@link
|
|
792
|
+
* @returns A {@link BrowserWriterInterface} that persists bytes through `node:fs/promises`
|
|
749
793
|
*/
|
|
750
|
-
function
|
|
751
|
-
return
|
|
752
|
-
await (0, node_fs_promises.mkdir)((0, node_path.dirname)(path), { recursive: true });
|
|
753
|
-
await (0, node_fs_promises.writeFile)(path, data);
|
|
754
|
-
} };
|
|
794
|
+
function createBrowserWriter() {
|
|
795
|
+
return new FileBrowserWriter();
|
|
755
796
|
}
|
|
756
797
|
//#endregion
|
|
757
798
|
//#region src/server/Browser.ts
|
|
758
799
|
/**
|
|
759
800
|
* Discovers, launches, connects to, and owns Chromium-family browser sessions.
|
|
801
|
+
*
|
|
802
|
+
* @example
|
|
803
|
+
* ```ts
|
|
804
|
+
* import { Browser } from '@orkestrel/browser/server'
|
|
805
|
+
*
|
|
806
|
+
* const browser = new Browser({ headless: true })
|
|
807
|
+
* await browser.connect()
|
|
808
|
+
* const page = await browser.create({ url: 'https://example.com' })
|
|
809
|
+
* await browser.destroy()
|
|
810
|
+
* ```
|
|
760
811
|
*/
|
|
761
812
|
var Browser = class {
|
|
762
813
|
#emitter;
|
|
@@ -775,14 +826,14 @@ var Browser = class {
|
|
|
775
826
|
#profile;
|
|
776
827
|
#contexts = [];
|
|
777
828
|
#destroyed = false;
|
|
778
|
-
#connecting;
|
|
779
|
-
#disconnecting;
|
|
829
|
+
#connecting = new _src_core.BrowserTransition();
|
|
830
|
+
#disconnecting = new _src_core.BrowserTransition();
|
|
780
831
|
#exitCleanup;
|
|
781
832
|
#shutdown;
|
|
782
833
|
#transport;
|
|
783
|
-
#
|
|
784
|
-
#
|
|
785
|
-
#
|
|
834
|
+
#transportCloseHandler = this.#handleTransportLoss.bind(this);
|
|
835
|
+
#transportErrorHandler = this.#handleTransportLoss.bind(this);
|
|
836
|
+
#processExitHandler = this.#handleProcessExit.bind(this);
|
|
786
837
|
constructor(options) {
|
|
787
838
|
this.#emitter = new _orkestrel_emitter.Emitter({
|
|
788
839
|
...options?.on !== void 0 ? { on: options.on } : {},
|
|
@@ -809,9 +860,6 @@ var Browser = class {
|
|
|
809
860
|
get owned() {
|
|
810
861
|
return this.#owned;
|
|
811
862
|
}
|
|
812
|
-
get connected() {
|
|
813
|
-
return this.#status === "connected";
|
|
814
|
-
}
|
|
815
863
|
get pid() {
|
|
816
864
|
return this.#servingPid ?? this.#process?.pid;
|
|
817
865
|
}
|
|
@@ -822,19 +870,13 @@ var Browser = class {
|
|
|
822
870
|
}
|
|
823
871
|
async connect() {
|
|
824
872
|
if (this.#destroyed) throw new BrowserDestroyedError();
|
|
825
|
-
const active = this.#connecting;
|
|
873
|
+
const active = this.#connecting.pending;
|
|
826
874
|
if (active !== void 0) {
|
|
827
875
|
await active;
|
|
828
876
|
return;
|
|
829
877
|
}
|
|
830
878
|
if (this.#status === "connected") return;
|
|
831
|
-
|
|
832
|
-
this.#connecting = attempt;
|
|
833
|
-
try {
|
|
834
|
-
await attempt;
|
|
835
|
-
} finally {
|
|
836
|
-
if (this.#connecting === attempt) this.#connecting = void 0;
|
|
837
|
-
}
|
|
879
|
+
await this.#connecting.execute(() => this.#establish());
|
|
838
880
|
}
|
|
839
881
|
adopt() {
|
|
840
882
|
if (this.#destroyed) throw new BrowserDestroyedError();
|
|
@@ -843,19 +885,13 @@ var Browser = class {
|
|
|
843
885
|
}
|
|
844
886
|
async disconnect() {
|
|
845
887
|
if (this.#destroyed) return;
|
|
846
|
-
const disconnecting = this.#disconnecting;
|
|
847
|
-
const connecting = this.#connecting;
|
|
888
|
+
const disconnecting = this.#disconnecting.pending;
|
|
889
|
+
const connecting = this.#connecting.pending;
|
|
848
890
|
if (disconnecting !== void 0) await disconnecting;
|
|
849
891
|
if (connecting !== void 0) await connecting.catch(() => void 0);
|
|
850
892
|
if (this.#destroyed) return;
|
|
851
893
|
if (this.#status !== "connected" || this.#client === void 0) return;
|
|
852
|
-
|
|
853
|
-
this.#disconnecting = attempt;
|
|
854
|
-
try {
|
|
855
|
-
await attempt;
|
|
856
|
-
} finally {
|
|
857
|
-
if (this.#disconnecting === attempt) this.#disconnecting = void 0;
|
|
858
|
-
}
|
|
894
|
+
await this.#disconnecting.execute(() => this.#detach());
|
|
859
895
|
}
|
|
860
896
|
context(index) {
|
|
861
897
|
const i = index ?? 0;
|
|
@@ -879,7 +915,7 @@ var Browser = class {
|
|
|
879
915
|
const result = await client.send("Target.createBrowserContext", params);
|
|
880
916
|
if (!(0, _orkestrel_contract.isRecord)(result) || !(0, _orkestrel_contract.isString)(result["browserContextId"])) throw new BrowserConnectionError("Failed to create isolated browser context");
|
|
881
917
|
const id = result["browserContextId"];
|
|
882
|
-
const context = new _src_core.BrowserContext(client, id, options?.emulation?.viewport ?? this.#options.viewport,
|
|
918
|
+
const context = new _src_core.BrowserContext(client, id, options?.emulation?.viewport ?? this.#options.viewport, createBrowserWriter(), options?.emulation, options?.downloads, options);
|
|
883
919
|
try {
|
|
884
920
|
if (options?.downloads !== void 0) await client.send("Browser.setDownloadBehavior", {
|
|
885
921
|
behavior: options.downloads.named === true ? "allowAndName" : "allow",
|
|
@@ -900,7 +936,7 @@ var Browser = class {
|
|
|
900
936
|
if (this.#status !== "connected" || client === void 0) throw new BrowserNotConnectedError();
|
|
901
937
|
let context = this.#contexts[0];
|
|
902
938
|
if (context === void 0) {
|
|
903
|
-
context = new _src_core.BrowserContext(client, void 0, this.#options.viewport,
|
|
939
|
+
context = new _src_core.BrowserContext(client, void 0, this.#options.viewport, createBrowserWriter());
|
|
904
940
|
this.#registerContext(context);
|
|
905
941
|
}
|
|
906
942
|
const page = await context.create(options);
|
|
@@ -928,7 +964,8 @@ var Browser = class {
|
|
|
928
964
|
return shutdown;
|
|
929
965
|
}
|
|
930
966
|
async #establish() {
|
|
931
|
-
|
|
967
|
+
const disconnecting = this.#disconnecting.pending;
|
|
968
|
+
if (disconnecting !== void 0) await disconnecting;
|
|
932
969
|
await this.#settleExit();
|
|
933
970
|
if (this.#destroyed) throw new BrowserDestroyedError();
|
|
934
971
|
if (this.#status === "connected") return;
|
|
@@ -946,7 +983,7 @@ var Browser = class {
|
|
|
946
983
|
}
|
|
947
984
|
if (this.#options.cdp?.discover ?? true) {
|
|
948
985
|
const discovery = await this.#raceAbort(this.#discoverCDP(this.#signal()));
|
|
949
|
-
if (discovery.
|
|
986
|
+
if (discovery.endpoint !== void 0) {
|
|
950
987
|
this.#engine = browserToEngine(discovery.browser);
|
|
951
988
|
await this.#connectCDP(discovery.endpoint);
|
|
952
989
|
return;
|
|
@@ -1064,24 +1101,24 @@ var Browser = class {
|
|
|
1064
1101
|
#bindTransport(transport) {
|
|
1065
1102
|
this.#unbindTransport();
|
|
1066
1103
|
this.#transport = transport;
|
|
1067
|
-
transport.emitter.on("close", this.#
|
|
1068
|
-
transport.emitter.on("error", this.#
|
|
1104
|
+
transport.emitter.on("close", this.#transportCloseHandler);
|
|
1105
|
+
transport.emitter.on("error", this.#transportErrorHandler);
|
|
1069
1106
|
}
|
|
1070
1107
|
#unbindTransport() {
|
|
1071
1108
|
const transport = this.#transport;
|
|
1072
1109
|
if (transport === void 0) return;
|
|
1073
|
-
transport.emitter.off("close", this.#
|
|
1074
|
-
transport.emitter.off("error", this.#
|
|
1110
|
+
transport.emitter.off("close", this.#transportCloseHandler);
|
|
1111
|
+
transport.emitter.off("error", this.#transportErrorHandler);
|
|
1075
1112
|
this.#transport = void 0;
|
|
1076
1113
|
}
|
|
1077
1114
|
#bindProcess(process) {
|
|
1078
1115
|
this.#unbindProcess();
|
|
1079
1116
|
if (this.#servingPid !== void 0) return;
|
|
1080
|
-
process.once("exit", this.#
|
|
1081
|
-
if (process.exitCode !== null || process.signalCode !== null) queueMicrotask(this.#
|
|
1117
|
+
process.once("exit", this.#processExitHandler);
|
|
1118
|
+
if (process.exitCode !== null || process.signalCode !== null) queueMicrotask(this.#processExitHandler);
|
|
1082
1119
|
}
|
|
1083
1120
|
#unbindProcess() {
|
|
1084
|
-
this.#process?.off("exit", this.#
|
|
1121
|
+
this.#process?.off("exit", this.#processExitHandler);
|
|
1085
1122
|
}
|
|
1086
1123
|
#timeout() {
|
|
1087
1124
|
return this.#options.timeout ?? _src_core.BROWSER_DEFAULT_TIMEOUT_MS;
|
|
@@ -1094,13 +1131,9 @@ var Browser = class {
|
|
|
1094
1131
|
if (!response.ok) return this.#notFound();
|
|
1095
1132
|
const info = await response.json();
|
|
1096
1133
|
if (!(0, _orkestrel_contract.isRecord)(info)) return this.#notFound();
|
|
1097
|
-
const endpoint = (0, _orkestrel_contract.isString)(info["webSocketDebuggerUrl"]) ? info["webSocketDebuggerUrl"] : void 0;
|
|
1098
|
-
const browser = (0, _orkestrel_contract.isString)(info["Browser"]) ? info["Browser"] : void 0;
|
|
1099
1134
|
return {
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
browser,
|
|
1103
|
-
connection: endpoint !== void 0 ? "cdp" : void 0
|
|
1135
|
+
endpoint: (0, _orkestrel_contract.isString)(info["webSocketDebuggerUrl"]) ? info["webSocketDebuggerUrl"] : void 0,
|
|
1136
|
+
browser: (0, _orkestrel_contract.isString)(info["Browser"]) ? info["Browser"] : void 0
|
|
1104
1137
|
};
|
|
1105
1138
|
} catch (error) {
|
|
1106
1139
|
if (signal?.aborted === true) throw error;
|
|
@@ -1109,10 +1142,8 @@ var Browser = class {
|
|
|
1109
1142
|
}
|
|
1110
1143
|
#notFound() {
|
|
1111
1144
|
return {
|
|
1112
|
-
found: false,
|
|
1113
1145
|
endpoint: void 0,
|
|
1114
|
-
browser: void 0
|
|
1115
|
-
connection: void 0
|
|
1146
|
+
browser: void 0
|
|
1116
1147
|
};
|
|
1117
1148
|
}
|
|
1118
1149
|
async #assertPortFree() {
|
|
@@ -1313,13 +1344,13 @@ var Browser = class {
|
|
|
1313
1344
|
if (!(0, _orkestrel_contract.isRecord)(target) || !(0, _orkestrel_contract.isString)(target["targetId"]) || target["type"] !== "page" || !(0, _orkestrel_contract.isString)(target["title"]) || !(0, _orkestrel_contract.isString)(target["url"])) continue;
|
|
1314
1345
|
pages.push({
|
|
1315
1346
|
id: target["targetId"],
|
|
1316
|
-
|
|
1347
|
+
category: target["type"],
|
|
1317
1348
|
title: target["title"],
|
|
1318
1349
|
url: target["url"]
|
|
1319
1350
|
});
|
|
1320
1351
|
}
|
|
1321
1352
|
if (pages.length === 0 || this.#contexts.length > 0) return;
|
|
1322
|
-
const context = new _src_core.BrowserContext(client, void 0, this.#options.viewport,
|
|
1353
|
+
const context = new _src_core.BrowserContext(client, void 0, this.#options.viewport, createBrowserWriter());
|
|
1323
1354
|
await context.sync(pages);
|
|
1324
1355
|
this.#registerContext(context);
|
|
1325
1356
|
}
|
|
@@ -1383,8 +1414,8 @@ var Browser = class {
|
|
|
1383
1414
|
}
|
|
1384
1415
|
}
|
|
1385
1416
|
async #settle() {
|
|
1386
|
-
await this.#connecting?.catch(() => void 0);
|
|
1387
|
-
await this.#disconnecting?.catch(() => void 0);
|
|
1417
|
+
await this.#connecting.pending?.catch(() => void 0);
|
|
1418
|
+
await this.#disconnecting.pending?.catch(() => void 0);
|
|
1388
1419
|
await this.#settleExit();
|
|
1389
1420
|
}
|
|
1390
1421
|
async #settleExit() {
|
|
@@ -1578,21 +1609,20 @@ exports.Browser = Browser;
|
|
|
1578
1609
|
exports.BrowserConnectionError = BrowserConnectionError;
|
|
1579
1610
|
exports.BrowserDestroyedError = BrowserDestroyedError;
|
|
1580
1611
|
exports.BrowserNotConnectedError = BrowserNotConnectedError;
|
|
1612
|
+
exports.FileBrowserWriter = FileBrowserWriter;
|
|
1581
1613
|
exports.WebSocketCDPTransport = WebSocketCDPTransport;
|
|
1582
1614
|
exports.browserToEngine = browserToEngine;
|
|
1615
|
+
exports.buildInstallPaths = buildInstallPaths;
|
|
1616
|
+
exports.buildStoreBases = buildStoreBases;
|
|
1617
|
+
exports.buildWindowsRoots = buildWindowsRoots;
|
|
1583
1618
|
exports.createBrowser = createBrowser;
|
|
1584
1619
|
exports.createBrowserProfile = createBrowserProfile;
|
|
1620
|
+
exports.createBrowserWriter = createBrowserWriter;
|
|
1585
1621
|
exports.createCDPTransport = createCDPTransport;
|
|
1586
|
-
exports.createScreenshotWriter = createScreenshotWriter;
|
|
1587
|
-
exports.defaultInstallPaths = defaultInstallPaths;
|
|
1588
|
-
exports.defaultStoreBases = defaultStoreBases;
|
|
1589
1622
|
exports.fetchCDPTargets = fetchCDPTargets;
|
|
1590
|
-
exports.
|
|
1591
|
-
exports.
|
|
1592
|
-
exports.
|
|
1593
|
-
exports.findEnvOverride = findEnvOverride;
|
|
1594
|
-
exports.findInStore = findInStore;
|
|
1595
|
-
exports.findInstallPath = findInstallPath;
|
|
1623
|
+
exports.findEnvOverrides = findEnvOverrides;
|
|
1624
|
+
exports.findInstallPaths = findInstallPaths;
|
|
1625
|
+
exports.findStorePaths = findStorePaths;
|
|
1596
1626
|
exports.findSystemBrowser = findSystemBrowser;
|
|
1597
1627
|
exports.findSystemBrowsers = findSystemBrowsers;
|
|
1598
1628
|
exports.isBrowserConnectionError = isBrowserConnectionError;
|
|
@@ -1601,11 +1631,9 @@ exports.isBrowserNotConnectedError = isBrowserNotConnectedError;
|
|
|
1601
1631
|
exports.launchBrowserProcess = launchBrowserProcess;
|
|
1602
1632
|
exports.normalizeExecutablePath = normalizeExecutablePath;
|
|
1603
1633
|
exports.parseBrowserEngine = parseBrowserEngine;
|
|
1604
|
-
exports.probeAllPathNames = probeAllPathNames;
|
|
1605
1634
|
exports.probePathNames = probePathNames;
|
|
1606
1635
|
exports.readFirstLine = readFirstLine;
|
|
1607
1636
|
exports.removeBrowserProfile = removeBrowserProfile;
|
|
1608
1637
|
exports.waitForCDPReady = waitForCDPReady;
|
|
1609
|
-
exports.windowsRoots = windowsRoots;
|
|
1610
1638
|
|
|
1611
1639
|
//# sourceMappingURL=index.cjs.map
|