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