@orkestrel/browser 0.0.14 → 0.0.16

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.
@@ -14,35 +14,75 @@ 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
- /** Default CDP port probed for an existing browser and used for launches. */
17
+ /**
18
+ * Sets the default CDP port probed for an existing browser and used for launches, `9222`.
19
+ */
18
20
  var BROWSER_DEFAULT_CDP_PORT = 9222;
19
- /** Default host probed for an existing browser and used for launches (avoids `localhost` resolving to `::1` when Chromium binds `127.0.0.1`). */
21
+ /**
22
+ * Sets the default host probed for an existing browser and used for launches, `'127.0.0.1'`,
23
+ * which avoids `localhost` resolving to `::1` when Chromium binds `127.0.0.1`.
24
+ */
20
25
  var BROWSER_DEFAULT_HOST = "127.0.0.1";
21
- /** Protocol prefix for CDP discovery requests. */
26
+ /** Names the protocol prefix for CDP discovery requests, `'http'`. */
22
27
  var BROWSER_CDP_PROTOCOL = "http";
23
- /** Path appended to the CDP host to fetch version metadata (endpoint discovery). */
28
+ /**
29
+ * Names the path appended to the CDP host to fetch version metadata, `'/json/version'`, which
30
+ * is where endpoint discovery reads.
31
+ */
24
32
  var BROWSER_CDP_VERSION_PATH = "/json/version";
25
- /** Path appended to the CDP host to list open targets (pages, workers, etc). */
33
+ /**
34
+ * Names the path appended to the CDP host to list open targets — pages, workers, and every
35
+ * other target category Chromium reports — `'/json/list'`.
36
+ */
26
37
  var BROWSER_CDP_LIST_PATH = "/json/list";
27
- /** Flags always passed to a launched browser process, alongside the caller's own. */
38
+ /** Lists the flags always passed to a launched browser process, alongside the caller's own. */
28
39
  var BROWSER_LAUNCH_ARGS = Object.freeze(["--no-first-run", "--no-default-browser-check"]);
29
- /** Flag enabling headless mode on a launched browser process. */
40
+ /**
41
+ * Names the flag that enables headless mode on a launched browser process, `'--headless=new'`.
42
+ */
30
43
  var BROWSER_HEADLESS_ARG = "--headless=new";
31
- /** Prefix for isolated browser profiles created beneath the operating-system temp directory. */
44
+ /**
45
+ * Names the prefix for isolated browser profiles created beneath the operating-system temp
46
+ * directory, `'orkestrel-browser-'`.
47
+ */
32
48
  var BROWSER_PROFILE_PREFIX = "orkestrel-browser-";
33
- /** Bound for each launched-process exit window during TERM-to-KILL teardown. */
49
+ /**
50
+ * Bounds each launched-process exit window during TERM-to-KILL teardown at `3_000`
51
+ * milliseconds.
52
+ */
34
53
  var BROWSER_KILL_GRACE_MS = 3e3;
35
- /** Bound for the `discover: false` port-occupancy probe before launching — short, since it only needs to detect an already-listening CDP endpoint, not perform full discovery. */
54
+ /**
55
+ * Bounds the `discover: false` port-occupancy probe before launching at `200` milliseconds,
56
+ * which is short because the probe only needs to detect an already-listening CDP endpoint
57
+ * rather than perform full discovery.
58
+ */
36
59
  var BROWSER_PORT_PROBE_TIMEOUT_MS = 200;
37
- /** Brief defer applied once 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. */
60
+ /**
61
+ * Defers once for `50` milliseconds when a transport loss is observed on an owned process,
62
+ * giving a near-simultaneous process-exit event, which libuv may reap slightly later than the
63
+ * socket close, first say over the diagnosis.
64
+ */
38
65
  var BROWSER_TRANSPORT_LOSS_DEFER_MS = 50;
39
- /** Machine-readable error-context cause for an owned browser process exiting. */
66
+ /**
67
+ * Names the machine-readable error-context cause for an owned browser process exiting,
68
+ * `'process-exit'`.
69
+ */
40
70
  var BROWSER_PROCESS_EXIT_CAUSE = "process-exit";
41
- /** Machine-readable error-context cause for a CDP transport disconnecting while its browser remains alive. */
71
+ /**
72
+ * Names the machine-readable error-context cause for a CDP transport disconnecting while its
73
+ * browser remains alive, `'transport-loss'`.
74
+ */
42
75
  var BROWSER_TRANSPORT_LOSS_CAUSE = "transport-loss";
43
- /** Environment variables checked (in order) for an explicit browser executable path override. */
76
+ /**
77
+ * Lists the environment variables checked, in order, for an explicit browser executable path
78
+ * override: `PLAYWRIGHT_EXECUTABLE_PATH`, then `CHROME_PATH`.
79
+ */
44
80
  var BROWSER_ENV_PATH_KEYS = Object.freeze(["PLAYWRIGHT_EXECUTABLE_PATH", "CHROME_PATH"]);
45
- /** Well-known Chrome/Chromium/Edge executable paths with no platform-specific root, keyed by `process.platform`. */
81
+ /**
82
+ * Lists the well-known Chrome/Chromium/Edge executable paths with no platform-specific root,
83
+ * keyed by `process.platform`, leaving `win32` empty because its roots come from
84
+ * `BROWSER_WINDOWS_SUFFIXES`.
85
+ */
46
86
  var BROWSER_EXECUTABLE_PATHS = Object.freeze({
47
87
  linux: Object.freeze([
48
88
  "/usr/bin/google-chrome",
@@ -62,18 +102,21 @@ var BROWSER_EXECUTABLE_PATHS = Object.freeze({
62
102
  ]),
63
103
  win32: Object.freeze([])
64
104
  });
65
- /** Windows install-root-relative suffixes for Chrome/Edge/Chromium, joined against each candidate root (`PROGRAMFILES`, `PROGRAMFILES(X86)`, `LOCALAPPDATA`). */
105
+ /** Lists the Windows install-root-relative suffixes for Chrome/Edge/Chromium, joined against each candidate root (`PROGRAMFILES`, `PROGRAMFILES(X86)`, `LOCALAPPDATA`). */
66
106
  var BROWSER_WINDOWS_SUFFIXES = Object.freeze([
67
107
  "Google\\Chrome\\Application\\chrome.exe",
68
108
  "Microsoft\\Edge\\Application\\msedge.exe",
69
109
  "Chromium\\Application\\chrome.exe"
70
110
  ]);
71
- /** Fallback Windows install roots used when the corresponding environment variable is absent. */
111
+ /**
112
+ * Lists the fallback Windows install roots used when `PROGRAMFILES`, `PROGRAMFILES(X86)`, or
113
+ * `LOCALAPPDATA` is absent.
114
+ */
72
115
  var BROWSER_WINDOWS_ROOT_FALLBACKS = Object.freeze({
73
116
  PROGRAMFILES: "C:\\Program Files",
74
117
  "PROGRAMFILES(X86)": "C:\\Program Files (x86)"
75
118
  });
76
- /** Command names probed on PATH when no well-known executable path exists. */
119
+ /** Lists the command names probed on PATH when no well-known executable path exists. */
77
120
  var BROWSER_EXECUTABLE_NAMES = Object.freeze([
78
121
  "google-chrome",
79
122
  "google-chrome-stable",
@@ -83,19 +126,28 @@ var BROWSER_EXECUTABLE_NAMES = Object.freeze([
83
126
  "chromium-browser",
84
127
  "chrome"
85
128
  ]);
86
- /** Environment variable naming an additional Playwright browser store base directory. */
129
+ /**
130
+ * Names the environment variable that carries an additional Playwright browser store base
131
+ * directory, `'PLAYWRIGHT_BROWSERS_PATH'`.
132
+ */
87
133
  var BROWSER_STORE_ENV_KEY = "PLAYWRIGHT_BROWSERS_PATH";
88
- /** Well-known Playwright browser store base directories checked in addition to `PLAYWRIGHT_BROWSERS_PATH`. */
134
+ /**
135
+ * Lists the well-known Playwright browser store base directories checked in addition to
136
+ * `PLAYWRIGHT_BROWSERS_PATH`, starting with `/opt/pw-browsers`.
137
+ */
89
138
  var BROWSER_STORE_DEFAULT_DIRS = Object.freeze(["/opt/pw-browsers"]);
90
- /** Per-OS default Playwright browser cache directory, relative to the home directory (win32 uses `LOCALAPPDATA` directly). */
139
+ /** Names the per-OS default Playwright browser cache directory, relative to the home directory (win32 uses `LOCALAPPDATA` directly). */
91
140
  var BROWSER_STORE_CACHE_DIRS = Object.freeze({
92
141
  linux: ".cache/ms-playwright",
93
142
  darwin: "Library/Caches/ms-playwright"
94
143
  });
95
- /** Name of the top-level Chromium symlink/binary Playwright maintains inside a browser store base. */
144
+ /**
145
+ * Names the top-level Chromium symlink or binary Playwright maintains inside a browser store
146
+ * base, `'chromium'`.
147
+ */
96
148
  var BROWSER_STORE_LINK_NAME = "chromium";
97
149
  /**
98
- * Case-insensitive substrings identifying an executable path/name's browser
150
+ * Lists the case-insensitive substrings identifying an executable path/name's browser
99
151
  * engine, checked by `parseBrowserEngine` in the order `edge` → `chromium` → `chrome`.
100
152
  */
101
153
  var BROWSER_ENGINE_HINTS = Object.freeze({
@@ -119,7 +171,7 @@ var BROWSER_ENGINE_HINTS = Object.freeze({
119
171
  "chrome"
120
172
  ])
121
173
  });
122
- /** Glob pattern (relative to a store base) matching a versioned Chromium binary, keyed by `process.platform`. */
174
+ /** Names the glob pattern (relative to a store base) matching a versioned Chromium binary, keyed by `process.platform`. */
123
175
  var BROWSER_STORE_GLOBS = Object.freeze({
124
176
  linux: "chromium-*/chrome-linux*/chrome",
125
177
  darwin: "chromium-*/chrome-mac*/Chromium.app/Contents/MacOS/Chromium",
@@ -128,7 +180,8 @@ var BROWSER_STORE_GLOBS = Object.freeze({
128
180
  //#endregion
129
181
  //#region src/server/errors.ts
130
182
  /**
131
- * A CDP connection, discovery, or launch attempt failed.
183
+ * Reports that a CDP connection, discovery, or launch attempt failed, under the code
184
+ * `BROWSER_CONNECTION_ERROR`.
132
185
  */
133
186
  var BrowserConnectionError = class extends _src_core.BrowserError {
134
187
  constructor(message, context) {
@@ -137,7 +190,8 @@ var BrowserConnectionError = class extends _src_core.BrowserError {
137
190
  }
138
191
  };
139
192
  /**
140
- * An operation requiring an active connection was attempted while disconnected.
193
+ * Reports that an operation requiring an active connection was attempted while disconnected,
194
+ * under the code `BROWSER_NOT_CONNECTED_ERROR`.
141
195
  */
142
196
  var BrowserNotConnectedError = class extends _src_core.BrowserError {
143
197
  constructor(context) {
@@ -146,7 +200,8 @@ var BrowserNotConnectedError = class extends _src_core.BrowserError {
146
200
  }
147
201
  };
148
202
  /**
149
- * An operation was attempted after the Browser was destroyed.
203
+ * Reports that an operation was attempted after the browser wrapper was destroyed, under the
204
+ * code `BROWSER_DESTROYED_ERROR`.
150
205
  */
151
206
  var BrowserDestroyedError = class extends _src_core.BrowserError {
152
207
  constructor(context) {
@@ -155,28 +210,28 @@ var BrowserDestroyedError = class extends _src_core.BrowserError {
155
210
  }
156
211
  };
157
212
  /**
158
- * Narrow an unknown value to BrowserConnectionError.
213
+ * Narrows an unknown value to a `BrowserConnectionError`.
159
214
  *
160
215
  * @param value - Value to check
161
- * @returns True when value is a BrowserConnectionError instance
216
+ * @returns True if value is a BrowserConnectionError instance; false otherwise
162
217
  */
163
218
  function isBrowserConnectionError(value) {
164
219
  return (0, _orkestrel_contract.isInstance)(value, BrowserConnectionError);
165
220
  }
166
221
  /**
167
- * Narrow an unknown value to BrowserNotConnectedError.
222
+ * Narrows an unknown value to a `BrowserNotConnectedError`.
168
223
  *
169
224
  * @param value - Value to check
170
- * @returns True when value is a BrowserNotConnectedError instance
225
+ * @returns True if value is a BrowserNotConnectedError instance; false otherwise
171
226
  */
172
227
  function isBrowserNotConnectedError(value) {
173
228
  return (0, _orkestrel_contract.isInstance)(value, BrowserNotConnectedError);
174
229
  }
175
230
  /**
176
- * Narrow an unknown value to BrowserDestroyedError.
231
+ * Narrows an unknown value to a `BrowserDestroyedError`.
177
232
  *
178
233
  * @param value - Value to check
179
- * @returns True when value is a BrowserDestroyedError instance
234
+ * @returns True if value is a BrowserDestroyedError instance; false otherwise
180
235
  */
181
236
  function isBrowserDestroyedError(value) {
182
237
  return (0, _orkestrel_contract.isInstance)(value, BrowserDestroyedError);
@@ -184,7 +239,7 @@ function isBrowserDestroyedError(value) {
184
239
  //#endregion
185
240
  //#region src/server/helpers.ts
186
241
  /**
187
- * Enumerate every Chrome/Chromium/Edge executable discoverable on this
242
+ * Enumerates every Chrome/Chromium/Edge executable discoverable on this
188
243
  * machine, deduplicated by normalized absolute path.
189
244
  *
190
245
  * @remarks
@@ -198,7 +253,7 @@ function isBrowserDestroyedError(value) {
198
253
  * and the per-OS Playwright cache directory) — the top-level `chromium`
199
254
  * link/binary, then the highest-revision `chromium-*` install found
200
255
  *
201
- * Each candidate is classified into a {@link BrowserEngine} via
256
+ * Each candidate is classified into a {@link BrowserEngine} through
202
257
  * `parseBrowserEngine` (unclassifiable candidates default to `'chromium'`),
203
258
  * and `options.engine` (when given) narrows the result to that engine.
204
259
  *
@@ -209,13 +264,13 @@ function findSystemBrowsers(options) {
209
264
  const platform = process.platform;
210
265
  const env = options?.env ?? process.env;
211
266
  const candidates = [];
212
- candidates.push(...findAllEnvOverrides(env));
213
- const paths = options?.paths ?? defaultInstallPaths(platform, env);
214
- candidates.push(...findAllInstallPaths(paths));
267
+ candidates.push(...findEnvOverrides(env));
268
+ const paths = options?.paths ?? buildInstallPaths(platform, env);
269
+ candidates.push(...findInstallPaths(paths));
215
270
  const names = options?.names ?? BROWSER_EXECUTABLE_NAMES;
216
- candidates.push(...probeAllPathNames(names, platform));
217
- const stores = options?.stores ?? defaultStoreBases(env, platform);
218
- for (const store of stores) candidates.push(...findAllInStore(store, platform));
271
+ candidates.push(...probePathNames(names, platform));
272
+ const stores = options?.stores ?? buildStoreBases(env, platform);
273
+ for (const store of stores) candidates.push(...findStorePaths(store, platform));
219
274
  const seen = /* @__PURE__ */ new Set();
220
275
  const browsers = [];
221
276
  for (const executable of candidates) {
@@ -232,7 +287,7 @@ function findSystemBrowsers(options) {
232
287
  return browsers;
233
288
  }
234
289
  /**
235
- * Locate a Chrome/Chromium/Edge executable on this machine — the first entry
290
+ * Locates a Chrome/Chromium/Edge executable on this machine — the first entry
236
291
  * of {@link findSystemBrowsers}.
237
292
  *
238
293
  * @param options - Overrides for the candidate sources; see {@link SystemBrowserOptions}
@@ -242,7 +297,7 @@ function findSystemBrowser(options) {
242
297
  return findSystemBrowsers(options)[0];
243
298
  }
244
299
  /**
245
- * Classify an executable path/name into a {@link BrowserEngine} by
300
+ * Classifies an executable path/name into a {@link BrowserEngine} by
246
301
  * case-insensitive hint, checked in the order edge → chromium → chrome.
247
302
  *
248
303
  * @param executable - Executable path or command name to classify
@@ -256,19 +311,19 @@ function parseBrowserEngine(executable) {
256
311
  "chrome"
257
312
  ]) if (BROWSER_ENGINE_HINTS[engine].some((hint) => lower.includes(hint))) return engine;
258
313
  }
259
- /** Normalize an executable path for cross-source deduplication (case-insensitive on Windows). */
314
+ /** Normalizes an executable path for cross-source deduplication (case-insensitive on Windows). */
260
315
  function normalizeExecutablePath(path, platform) {
261
316
  const normalized = platform === "win32" ? node_path.win32.normalize(path) : node_path.posix.normalize(path);
262
317
  return platform === "win32" ? normalized.toLowerCase() : normalized;
263
318
  }
264
- /** Classify a `/json/version` `Browser` string into a {@link BrowserEngine} (`Edg/` → edge, `Chrome/` → chrome, else chromium). */
319
+ /** Classifies a `/json/version` `Browser` string into a {@link BrowserEngine} (`Edg/` → edge, `Chrome/` → chrome, else chromium). */
265
320
  function browserToEngine(browser) {
266
321
  if (browser?.includes("Edg/") === true) return "edge";
267
322
  if (browser?.includes("Chrome/") === true) return "chrome";
268
323
  return "chromium";
269
324
  }
270
325
  /**
271
- * Resolve a persistent caller profile or create an isolated temporary one.
326
+ * Resolves a persistent caller profile or creates an isolated temporary one.
272
327
  *
273
328
  * @param path - Optional caller-owned persistent user-data directory
274
329
  * @returns The resolved profile and whether the library owns its lifecycle
@@ -284,7 +339,7 @@ async function createBrowserProfile(path) {
284
339
  };
285
340
  }
286
341
  /**
287
- * Remove a library-owned isolated browser profile.
342
+ * Removes a library-owned isolated browser profile.
288
343
  *
289
344
  * @remarks
290
345
  * The path must remain a direct child of the operating-system temp directory
@@ -297,7 +352,7 @@ async function removeBrowserProfile(profile) {
297
352
  if (!profile.temporary) return;
298
353
  const path = (0, node_path.resolve)(profile.path);
299
354
  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 Error("Refusing to remove an unsafe browser profile path");
355
+ 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
356
  await (0, node_fs_promises.rm)(path, {
302
357
  recursive: true,
303
358
  force: true,
@@ -305,12 +360,8 @@ async function removeBrowserProfile(profile) {
305
360
  retryDelay: 100
306
361
  });
307
362
  }
308
- /** Check the env-override keys (`PLAYWRIGHT_EXECUTABLE_PATH`, `CHROME_PATH`) in order for an existing file. */
309
- function findEnvOverride(env) {
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) {
363
+ /** Checks the env-override keys (`PLAYWRIGHT_EXECUTABLE_PATH`, `CHROME_PATH`) in order and returns every one that exists. */
364
+ function findEnvOverrides(env) {
314
365
  const found = [];
315
366
  for (const key of BROWSER_ENV_PATH_KEYS) {
316
367
  const value = env[key];
@@ -318,36 +369,28 @@ function findAllEnvOverrides(env) {
318
369
  }
319
370
  return found;
320
371
  }
321
- /** Build the default well-known install-path candidates for a platform, deriving Windows roots from env vars. */
322
- function defaultInstallPaths(platform, env) {
372
+ /** Builds the default well-known install-path candidates for a platform, deriving Windows roots from env vars. */
373
+ function buildInstallPaths(platform, env) {
323
374
  if (platform !== "win32") return BROWSER_EXECUTABLE_PATHS[platform] ?? [];
324
- const roots = windowsRoots(env);
375
+ const roots = buildWindowsRoots(env);
325
376
  const paths = [];
326
377
  for (const root of roots) for (const suffix of BROWSER_WINDOWS_SUFFIXES) paths.push(node_path.win32.join(root, suffix));
327
378
  return paths;
328
379
  }
329
- /** Derive Windows install roots from env vars, falling back to well-known literals when absent. */
330
- function windowsRoots(env) {
380
+ /** Derives Windows install roots from env vars, falling back to well-known literals when absent. */
381
+ function buildWindowsRoots(env) {
331
382
  return [
332
383
  env["PROGRAMFILES"] ?? BROWSER_WINDOWS_ROOT_FALLBACKS["PROGRAMFILES"],
333
384
  env["PROGRAMFILES(X86)"] ?? BROWSER_WINDOWS_ROOT_FALLBACKS["PROGRAMFILES(X86)"],
334
385
  env["LOCALAPPDATA"]
335
386
  ].filter((root) => root !== void 0);
336
387
  }
337
- /** Return the first candidate path that exists on disk. */
338
- function findInstallPath(paths) {
339
- return findAllInstallPaths(paths)[0];
340
- }
341
- /** Return every candidate path that exists on disk, in the given order. */
342
- function findAllInstallPaths(paths) {
388
+ /** Returns every candidate path that exists on disk, in the given order. */
389
+ function findInstallPaths(paths) {
343
390
  return paths.filter((path) => (0, node_fs.existsSync)(path));
344
391
  }
345
- /** Probe PATH (`which`/`where`) for the first resolvable command name. */
392
+ /** Probes PATH (`which`/`where`) for every resolvable command name, in the given order. */
346
393
  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
394
  const finder = platform === "win32" ? "where" : "which";
352
395
  const found = [];
353
396
  for (const name of names) {
@@ -382,8 +425,8 @@ function readFirstLine(output) {
382
425
  if (text.length > 0) return text;
383
426
  }
384
427
  }
385
- /** Build the default Playwright browser store base directories to search for a managed Chromium. */
386
- function defaultStoreBases(env, platform) {
428
+ /** Builds the default Playwright browser store base directories to search for a managed Chromium. */
429
+ function buildStoreBases(env, platform) {
387
430
  const bases = [];
388
431
  const envBase = env[BROWSER_STORE_ENV_KEY];
389
432
  if (envBase !== void 0 && envBase.length > 0) bases.push(envBase);
@@ -398,12 +441,8 @@ function defaultStoreBases(env, platform) {
398
441
  }
399
442
  return bases;
400
443
  }
401
- /** Search one store base for the top-level `chromium` link, else the highest-revision `chromium-*` install. */
402
- function findInStore(base, platform) {
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) {
444
+ /** Searches one store base for the top-level `chromium` link and every `chromium-*` install, highest revision first. */
445
+ function findStorePaths(base, platform) {
407
446
  const joiner = platform === "win32" ? node_path.win32.join : node_path.posix.join;
408
447
  const found = [];
409
448
  const linkPath = joiner(base, BROWSER_STORE_LINK_NAME);
@@ -417,7 +456,7 @@ function findAllInStore(base, platform) {
417
456
  return found;
418
457
  }
419
458
  /**
420
- * Launch a browser process with raw-CDP debugging flags.
459
+ * Launches a browser process with raw-CDP debugging flags.
421
460
  *
422
461
  * @remarks
423
462
  * POSIX launches own an isolated process group so lifecycle teardown can
@@ -447,11 +486,12 @@ function launchBrowserProcess(executable, port, headless, profile, extra) {
447
486
  });
448
487
  }
449
488
  /**
450
- * Poll a browser's CDP version endpoint until it responds or the timeout elapses.
489
+ * Polls a browser's CDP version endpoint until it responds or the timeout elapses.
451
490
  *
452
491
  * @param port - Port the browser exposes its CDP endpoint on
453
492
  * @param timeout - Maximum time to wait in milliseconds
454
493
  * @param host - Host the browser exposes its CDP endpoint on (default `127.0.0.1`)
494
+ * @param signal - Optional external abort; an abort while waiting rethrows rather than resolving
455
495
  * @returns The browser's WebSocket debugger URL
456
496
  *
457
497
  * @throws When the endpoint does not become ready before the timeout
@@ -475,15 +515,34 @@ async function waitForCDPReady(port, timeout, host = BROWSER_DEFAULT_HOST, signa
475
515
  const delay = Math.min(_src_core.BROWSER_WAIT_POLL_INTERVAL_MS, Math.max(0, deadline - Date.now()));
476
516
  if (delay > 0) await (0, node_timers_promises.setTimeout)(delay, void 0, { signal });
477
517
  }
478
- throw new Error(`CDP endpoint on port ${port} did not become ready within ${timeout}ms`);
518
+ throw new BrowserConnectionError(`CDP endpoint on port ${port} did not become ready within ${timeout}ms`, {
519
+ port,
520
+ timeout
521
+ });
479
522
  }
480
523
  /**
481
- * Fetch the current CDP target list from a browser's `/json/list` endpoint.
524
+ * Fetches the current CDP target list from a browser's `/json/list` endpoint, as a `Result`
525
+ * carrying either the targets or a coded `BrowserConnectionError`.
526
+ *
527
+ * @remarks
528
+ * The endpoint is a network boundary, so an unreachable host, a non-2xx
529
+ * response, and a body that is not a JSON array each come back as a failed
530
+ * `Result` carrying a coded `BrowserConnectionError`. An entry missing a
531
+ * required string field is skipped rather than failing the whole list.
482
532
  *
483
533
  * @param port - Port the browser exposes its CDP endpoint on
484
534
  * @param timeout - Request timeout in milliseconds
485
535
  * @param host - Host the browser exposes its CDP endpoint on (default `127.0.0.1`)
486
- * @returns Normalized CDP targets
536
+ * @returns The normalized CDP targets, or the fault that stopped the request
537
+ *
538
+ * @example
539
+ * ```ts
540
+ * import { fetchCDPTargets } from '@orkestrel/browser/server'
541
+ *
542
+ * const result = await fetchCDPTargets(9222, 2000)
543
+ * if (result.success) log(result.value.length)
544
+ * else log(result.error.code)
545
+ * ```
487
546
  */
488
547
  async function fetchCDPTargets(port, timeout, host = BROWSER_DEFAULT_HOST) {
489
548
  const url = `${BROWSER_CDP_PROTOCOL}://${host}:${port}${BROWSER_CDP_LIST_PATH}`;
@@ -491,23 +550,41 @@ async function fetchCDPTargets(port, timeout, host = BROWSER_DEFAULT_HOST) {
491
550
  const timer = setTimeout(() => controller.abort(), timeout);
492
551
  try {
493
552
  const response = await fetch(url, { signal: controller.signal });
494
- if (!response.ok) return [];
553
+ if (!response.ok) return {
554
+ success: false,
555
+ error: new BrowserConnectionError("CDP target list request was refused", {
556
+ url,
557
+ status: response.status
558
+ })
559
+ };
495
560
  const list = await response.json();
496
- if (!(0, _orkestrel_contract.isArray)(list)) return [];
561
+ if (!(0, _orkestrel_contract.isArray)(list)) return {
562
+ success: false,
563
+ error: new BrowserConnectionError("CDP target list is not a JSON array", { url })
564
+ };
497
565
  const targets = [];
498
566
  for (const entry of list) {
499
567
  if (!(0, _orkestrel_contract.isRecord)(entry)) continue;
500
568
  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
569
  targets.push({
502
570
  id: entry["id"],
503
- type: entry["type"],
571
+ category: entry["type"],
504
572
  title: entry["title"],
505
573
  url: entry["url"]
506
574
  });
507
575
  }
508
- return targets;
509
- } catch {
510
- return [];
576
+ return {
577
+ success: true,
578
+ value: targets
579
+ };
580
+ } catch (error) {
581
+ return {
582
+ success: false,
583
+ error: new BrowserConnectionError("CDP target list is unreachable", {
584
+ url,
585
+ error
586
+ })
587
+ };
511
588
  } finally {
512
589
  clearTimeout(timer);
513
590
  }
@@ -515,27 +592,36 @@ async function fetchCDPTargets(port, timeout, host = BROWSER_DEFAULT_HOST) {
515
592
  //#endregion
516
593
  //#region src/server/transports/WebSocketCDPTransport.ts
517
594
  /**
518
- * `@orkestrel/websocket`-backed raw CDP text transport.
595
+ * Provides a raw CDP text transport backed by `@orkestrel/websocket`.
519
596
  *
520
597
  * @remarks
521
598
  * Performs and validates the RFC 6455 HTTP upgrade, then delegates frame
522
599
  * handling to `@orkestrel/websocket`. Concurrent and repeated `start()` /
523
600
  * `close()` calls share their active transition, and a later `start()` opens a
524
601
  * fresh socket after the prior one closes.
602
+ *
603
+ * @example
604
+ * ```ts
605
+ * import { WebSocketCDPTransport } from '@orkestrel/browser/server'
606
+ *
607
+ * const transport = new WebSocketCDPTransport({ url: 'ws://127.0.0.1:9222/devtools/browser/abc' })
608
+ * await transport.start()
609
+ * await transport.close()
610
+ * ```
525
611
  */
526
612
  var WebSocketCDPTransport = class {
527
613
  #emitter;
528
614
  #url;
529
615
  #timeout;
530
616
  #socket;
531
- #starting;
532
- #closing;
617
+ #starting = new _src_core.BrowserTransition();
618
+ #closing = new _src_core.BrowserTransition();
533
619
  #request;
534
620
  #resolve;
535
621
  #reject;
536
622
  #timer;
537
623
  #closeResolve;
538
- #onSocketClose = this.#handleSocketClose.bind(this);
624
+ #socketCloseHandler = this.#handleSocketClose.bind(this);
539
625
  constructor(options) {
540
626
  this.#emitter = new _orkestrel_emitter.Emitter({
541
627
  ...options.on !== void 0 ? { on: options.on } : {},
@@ -548,43 +634,27 @@ var WebSocketCDPTransport = class {
548
634
  return this.#emitter;
549
635
  }
550
636
  async start() {
551
- if (this.#closing !== void 0) await this.#closing;
637
+ const closing = this.#closing.pending;
638
+ if (closing !== void 0) await closing;
552
639
  if (this.#socket?.readyState === _orkestrel_websocket.WEBSOCKET_READY_OPEN) return;
553
- const active = this.#starting;
640
+ const active = this.#starting.pending;
554
641
  if (active !== void 0) {
555
642
  await active;
556
643
  return;
557
644
  }
558
645
  this.#socket?.destroy();
559
646
  this.#socket = void 0;
560
- const transition = this.#open();
561
- this.#starting = transition;
562
- try {
563
- await transition;
564
- } finally {
565
- if (this.#starting === transition) this.#starting = void 0;
566
- }
647
+ await this.#starting.execute(() => this.#start());
567
648
  }
568
649
  async send(data) {
569
650
  const socket = this.#socket;
570
- if (socket === void 0 || socket.readyState !== _orkestrel_websocket.WEBSOCKET_READY_OPEN) throw new Error("WebSocket CDP transport is not open");
651
+ if (socket === void 0 || socket.readyState !== _orkestrel_websocket.WEBSOCKET_READY_OPEN) throw new BrowserConnectionError("WebSocket CDP transport is not open", { url: this.#url });
571
652
  socket.send(data);
572
653
  }
573
654
  async close() {
574
- const active = this.#closing;
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
- }
655
+ await this.#closing.execute(() => this.#close());
586
656
  }
587
- async #open() {
657
+ async #start() {
588
658
  const parsed = (0, _orkestrel_contract.attempt)(() => new URL(this.#url));
589
659
  if (!parsed.success) throw new BrowserConnectionError(`WebSocket CDP URL is invalid: ${this.#url}`, {
590
660
  url: this.#url,
@@ -620,19 +690,19 @@ var WebSocketCDPTransport = class {
620
690
  this.#socket = socket;
621
691
  this.#bind(socket);
622
692
  }
623
- async #stop() {
693
+ async #close() {
624
694
  const request = this.#request;
625
695
  if (request !== void 0) {
626
696
  this.#rejectRequest(request, new BrowserConnectionError(`WebSocket CDP connection to ${this.#url} was closed before it finished connecting`, { url: this.#url }));
627
697
  request.destroy();
628
698
  }
629
- await this.#starting?.catch(() => void 0);
699
+ await this.#starting.pending?.catch(() => void 0);
630
700
  const socket = this.#socket;
631
701
  this.#socket = void 0;
632
702
  if (socket === void 0 || socket.readyState === _orkestrel_websocket.WEBSOCKET_READY_CLOSED) return;
633
703
  const closed = Promise.withResolvers();
634
704
  this.#closeResolve = closed.resolve;
635
- socket.emitter.on("close", this.#onSocketClose);
705
+ socket.emitter.on("close", this.#socketCloseHandler);
636
706
  try {
637
707
  socket.close();
638
708
  if (socket.readyState !== _orkestrel_websocket.WEBSOCKET_READY_CLOSED) await closed.promise;
@@ -640,7 +710,7 @@ var WebSocketCDPTransport = class {
640
710
  socket.destroy();
641
711
  if (socket.readyState !== _orkestrel_websocket.WEBSOCKET_READY_CLOSED) await closed.promise;
642
712
  } finally {
643
- socket.emitter.off("close", this.#onSocketClose);
713
+ socket.emitter.off("close", this.#socketCloseHandler);
644
714
  this.#closeResolve = void 0;
645
715
  }
646
716
  }
@@ -715,26 +785,61 @@ var WebSocketCDPTransport = class {
715
785
  }
716
786
  };
717
787
  //#endregion
788
+ //#region src/server/writers/FileBrowserWriter.ts
789
+ /**
790
+ * Persists captured browser bytes to the filesystem through `node:fs/promises`.
791
+ *
792
+ * @remarks
793
+ * Creates every missing parent directory of the target path before writing, so
794
+ * a caller may name a nested destination that does not exist yet.
795
+ *
796
+ * @example
797
+ * ```ts
798
+ * import { FileBrowserWriter } from '@orkestrel/browser/server'
799
+ *
800
+ * const writer = new FileBrowserWriter()
801
+ * await writer.write('shots/hero.png', new Uint8Array([137, 80, 78, 71]))
802
+ * ```
803
+ */
804
+ var FileBrowserWriter = class {
805
+ /**
806
+ * Writes the captured bytes to a path, creating its parent directories.
807
+ *
808
+ * @param path - Destination file path
809
+ * @param data - Captured bytes to persist
810
+ * @returns A promise resolving once the bytes are on disk
811
+ */
812
+ async write(path, data) {
813
+ await (0, node_fs_promises.mkdir)((0, node_path.dirname)(path), { recursive: true });
814
+ await (0, node_fs_promises.writeFile)(path, data);
815
+ }
816
+ };
817
+ //#endregion
718
818
  //#region src/server/factories.ts
719
819
  /**
720
- * Create a raw-CDP Browser façade.
820
+ * Creates a raw-CDP `BrowserInterface` façade with discovery, connection, and lifecycle
821
+ * management.
721
822
  *
722
823
  * @param options - Connection, launch, and viewport configuration
723
824
  * @returns A {@link BrowserInterface}
724
825
  *
725
- * @example
826
+ * @example Connect to a browser and drive a page
726
827
  * ```ts
727
- * import { createBrowser } from '@src/server'
828
+ * import { createBrowser } from '@orkestrel/browser/server'
728
829
  *
729
- * const browser = createBrowser()
730
- * await browser.connect()
830
+ * const browser = createBrowser({ headless: true })
831
+ * await browser.connect() // CDP endpoint discovery → connect, else launch
832
+ * const page = await browser.create({ url: 'https://example.com' })
833
+ * await page.click('#accept')
834
+ * const shot = await page.screenshot({ path: './out.png' })
835
+ * await browser.destroy()
731
836
  * ```
732
837
  */
733
838
  function createBrowser(options) {
734
839
  return new Browser(options);
735
840
  }
736
841
  /**
737
- * Create a Node `WebSocket`-backed CDP transport.
842
+ * Creates a Node `WebSocket`-backed `CDPTransportInterface` for the given CDP debugger URL.
738
843
  *
739
844
  * @param options - The CDP WebSocket debugger URL (and optional timeout)
740
845
  * @returns A {@link CDPTransportInterface}
@@ -743,20 +848,28 @@ function createCDPTransport(options) {
743
848
  return new WebSocketCDPTransport(options);
744
849
  }
745
850
  /**
746
- * Create a filesystem-backed screenshot writer.
851
+ * Creates a filesystem-backed `BrowserWriterInterface` that persists bytes through
852
+ * `node:fs/promises`.
747
853
  *
748
- * @returns A {@link ScreenshotWriterInterface} that persists bytes via `node:fs/promises`
854
+ * @returns A {@link BrowserWriterInterface} that persists bytes through `node:fs/promises`
749
855
  */
750
- function createScreenshotWriter() {
751
- return { async write(path, data) {
752
- await (0, node_fs_promises.mkdir)((0, node_path.dirname)(path), { recursive: true });
753
- await (0, node_fs_promises.writeFile)(path, data);
754
- } };
856
+ function createBrowserWriter() {
857
+ return new FileBrowserWriter();
755
858
  }
756
859
  //#endregion
757
860
  //#region src/server/Browser.ts
758
861
  /**
759
862
  * Discovers, launches, connects to, and owns Chromium-family browser sessions.
863
+ *
864
+ * @example
865
+ * ```ts
866
+ * import { Browser } from '@orkestrel/browser/server'
867
+ *
868
+ * const browser = new Browser({ headless: true })
869
+ * await browser.connect()
870
+ * const page = await browser.create({ url: 'https://example.com' })
871
+ * await browser.destroy()
872
+ * ```
760
873
  */
761
874
  var Browser = class {
762
875
  #emitter;
@@ -775,14 +888,14 @@ var Browser = class {
775
888
  #profile;
776
889
  #contexts = [];
777
890
  #destroyed = false;
778
- #connecting;
779
- #disconnecting;
891
+ #connecting = new _src_core.BrowserTransition();
892
+ #disconnecting = new _src_core.BrowserTransition();
780
893
  #exitCleanup;
781
894
  #shutdown;
782
895
  #transport;
783
- #onTransportClose = this.#handleTransportLoss.bind(this);
784
- #onTransportError = this.#handleTransportLoss.bind(this);
785
- #onProcessExit = this.#handleProcessExit.bind(this);
896
+ #transportCloseHandler = this.#handleTransportLoss.bind(this);
897
+ #transportErrorHandler = this.#handleTransportLoss.bind(this);
898
+ #processExitHandler = this.#handleProcessExit.bind(this);
786
899
  constructor(options) {
787
900
  this.#emitter = new _orkestrel_emitter.Emitter({
788
901
  ...options?.on !== void 0 ? { on: options.on } : {},
@@ -809,9 +922,6 @@ var Browser = class {
809
922
  get owned() {
810
923
  return this.#owned;
811
924
  }
812
- get connected() {
813
- return this.#status === "connected";
814
- }
815
925
  get pid() {
816
926
  return this.#servingPid ?? this.#process?.pid;
817
927
  }
@@ -822,19 +932,13 @@ var Browser = class {
822
932
  }
823
933
  async connect() {
824
934
  if (this.#destroyed) throw new BrowserDestroyedError();
825
- const active = this.#connecting;
935
+ const active = this.#connecting.pending;
826
936
  if (active !== void 0) {
827
937
  await active;
828
938
  return;
829
939
  }
830
940
  if (this.#status === "connected") return;
831
- const attempt = this.#establish();
832
- this.#connecting = attempt;
833
- try {
834
- await attempt;
835
- } finally {
836
- if (this.#connecting === attempt) this.#connecting = void 0;
837
- }
941
+ await this.#connecting.execute(() => this.#establish());
838
942
  }
839
943
  adopt() {
840
944
  if (this.#destroyed) throw new BrowserDestroyedError();
@@ -843,19 +947,13 @@ var Browser = class {
843
947
  }
844
948
  async disconnect() {
845
949
  if (this.#destroyed) return;
846
- const disconnecting = this.#disconnecting;
847
- const connecting = this.#connecting;
950
+ const disconnecting = this.#disconnecting.pending;
951
+ const connecting = this.#connecting.pending;
848
952
  if (disconnecting !== void 0) await disconnecting;
849
953
  if (connecting !== void 0) await connecting.catch(() => void 0);
850
954
  if (this.#destroyed) return;
851
955
  if (this.#status !== "connected" || this.#client === void 0) return;
852
- const attempt = this.#detach();
853
- this.#disconnecting = attempt;
854
- try {
855
- await attempt;
856
- } finally {
857
- if (this.#disconnecting === attempt) this.#disconnecting = void 0;
858
- }
956
+ await this.#disconnecting.execute(() => this.#detach());
859
957
  }
860
958
  context(index) {
861
959
  const i = index ?? 0;
@@ -879,7 +977,7 @@ var Browser = class {
879
977
  const result = await client.send("Target.createBrowserContext", params);
880
978
  if (!(0, _orkestrel_contract.isRecord)(result) || !(0, _orkestrel_contract.isString)(result["browserContextId"])) throw new BrowserConnectionError("Failed to create isolated browser context");
881
979
  const id = result["browserContextId"];
882
- const context = new _src_core.BrowserContext(client, id, options?.emulation?.viewport ?? this.#options.viewport, createScreenshotWriter(), options?.emulation, options?.downloads);
980
+ const context = new _src_core.BrowserContext(client, id, options?.emulation?.viewport ?? this.#options.viewport, createBrowserWriter(), options?.emulation, options?.downloads, options);
883
981
  try {
884
982
  if (options?.downloads !== void 0) await client.send("Browser.setDownloadBehavior", {
885
983
  behavior: options.downloads.named === true ? "allowAndName" : "allow",
@@ -900,7 +998,7 @@ var Browser = class {
900
998
  if (this.#status !== "connected" || client === void 0) throw new BrowserNotConnectedError();
901
999
  let context = this.#contexts[0];
902
1000
  if (context === void 0) {
903
- context = new _src_core.BrowserContext(client, void 0, this.#options.viewport, createScreenshotWriter());
1001
+ context = new _src_core.BrowserContext(client, void 0, this.#options.viewport, createBrowserWriter());
904
1002
  this.#registerContext(context);
905
1003
  }
906
1004
  const page = await context.create(options);
@@ -928,7 +1026,8 @@ var Browser = class {
928
1026
  return shutdown;
929
1027
  }
930
1028
  async #establish() {
931
- if (this.#disconnecting !== void 0) await this.#disconnecting;
1029
+ const disconnecting = this.#disconnecting.pending;
1030
+ if (disconnecting !== void 0) await disconnecting;
932
1031
  await this.#settleExit();
933
1032
  if (this.#destroyed) throw new BrowserDestroyedError();
934
1033
  if (this.#status === "connected") return;
@@ -946,7 +1045,7 @@ var Browser = class {
946
1045
  }
947
1046
  if (this.#options.cdp?.discover ?? true) {
948
1047
  const discovery = await this.#raceAbort(this.#discoverCDP(this.#signal()));
949
- if (discovery.found && discovery.endpoint !== void 0) {
1048
+ if (discovery.endpoint !== void 0) {
950
1049
  this.#engine = browserToEngine(discovery.browser);
951
1050
  await this.#connectCDP(discovery.endpoint);
952
1051
  return;
@@ -1064,24 +1163,24 @@ var Browser = class {
1064
1163
  #bindTransport(transport) {
1065
1164
  this.#unbindTransport();
1066
1165
  this.#transport = transport;
1067
- transport.emitter.on("close", this.#onTransportClose);
1068
- transport.emitter.on("error", this.#onTransportError);
1166
+ transport.emitter.on("close", this.#transportCloseHandler);
1167
+ transport.emitter.on("error", this.#transportErrorHandler);
1069
1168
  }
1070
1169
  #unbindTransport() {
1071
1170
  const transport = this.#transport;
1072
1171
  if (transport === void 0) return;
1073
- transport.emitter.off("close", this.#onTransportClose);
1074
- transport.emitter.off("error", this.#onTransportError);
1172
+ transport.emitter.off("close", this.#transportCloseHandler);
1173
+ transport.emitter.off("error", this.#transportErrorHandler);
1075
1174
  this.#transport = void 0;
1076
1175
  }
1077
1176
  #bindProcess(process) {
1078
1177
  this.#unbindProcess();
1079
1178
  if (this.#servingPid !== void 0) return;
1080
- process.once("exit", this.#onProcessExit);
1081
- if (process.exitCode !== null || process.signalCode !== null) queueMicrotask(this.#onProcessExit);
1179
+ process.once("exit", this.#processExitHandler);
1180
+ if (process.exitCode !== null || process.signalCode !== null) queueMicrotask(this.#processExitHandler);
1082
1181
  }
1083
1182
  #unbindProcess() {
1084
- this.#process?.off("exit", this.#onProcessExit);
1183
+ this.#process?.off("exit", this.#processExitHandler);
1085
1184
  }
1086
1185
  #timeout() {
1087
1186
  return this.#options.timeout ?? _src_core.BROWSER_DEFAULT_TIMEOUT_MS;
@@ -1094,13 +1193,9 @@ var Browser = class {
1094
1193
  if (!response.ok) return this.#notFound();
1095
1194
  const info = await response.json();
1096
1195
  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
1196
  return {
1100
- found: endpoint !== void 0,
1101
- endpoint,
1102
- browser,
1103
- connection: endpoint !== void 0 ? "cdp" : void 0
1197
+ endpoint: (0, _orkestrel_contract.isString)(info["webSocketDebuggerUrl"]) ? info["webSocketDebuggerUrl"] : void 0,
1198
+ browser: (0, _orkestrel_contract.isString)(info["Browser"]) ? info["Browser"] : void 0
1104
1199
  };
1105
1200
  } catch (error) {
1106
1201
  if (signal?.aborted === true) throw error;
@@ -1109,10 +1204,8 @@ var Browser = class {
1109
1204
  }
1110
1205
  #notFound() {
1111
1206
  return {
1112
- found: false,
1113
1207
  endpoint: void 0,
1114
- browser: void 0,
1115
- connection: void 0
1208
+ browser: void 0
1116
1209
  };
1117
1210
  }
1118
1211
  async #assertPortFree() {
@@ -1313,13 +1406,13 @@ var Browser = class {
1313
1406
  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
1407
  pages.push({
1315
1408
  id: target["targetId"],
1316
- type: target["type"],
1409
+ category: target["type"],
1317
1410
  title: target["title"],
1318
1411
  url: target["url"]
1319
1412
  });
1320
1413
  }
1321
1414
  if (pages.length === 0 || this.#contexts.length > 0) return;
1322
- const context = new _src_core.BrowserContext(client, void 0, this.#options.viewport, createScreenshotWriter());
1415
+ const context = new _src_core.BrowserContext(client, void 0, this.#options.viewport, createBrowserWriter());
1323
1416
  await context.sync(pages);
1324
1417
  this.#registerContext(context);
1325
1418
  }
@@ -1383,8 +1476,8 @@ var Browser = class {
1383
1476
  }
1384
1477
  }
1385
1478
  async #settle() {
1386
- await this.#connecting?.catch(() => void 0);
1387
- await this.#disconnecting?.catch(() => void 0);
1479
+ await this.#connecting.pending?.catch(() => void 0);
1480
+ await this.#disconnecting.pending?.catch(() => void 0);
1388
1481
  await this.#settleExit();
1389
1482
  }
1390
1483
  async #settleExit() {
@@ -1578,21 +1671,20 @@ exports.Browser = Browser;
1578
1671
  exports.BrowserConnectionError = BrowserConnectionError;
1579
1672
  exports.BrowserDestroyedError = BrowserDestroyedError;
1580
1673
  exports.BrowserNotConnectedError = BrowserNotConnectedError;
1674
+ exports.FileBrowserWriter = FileBrowserWriter;
1581
1675
  exports.WebSocketCDPTransport = WebSocketCDPTransport;
1582
1676
  exports.browserToEngine = browserToEngine;
1677
+ exports.buildInstallPaths = buildInstallPaths;
1678
+ exports.buildStoreBases = buildStoreBases;
1679
+ exports.buildWindowsRoots = buildWindowsRoots;
1583
1680
  exports.createBrowser = createBrowser;
1584
1681
  exports.createBrowserProfile = createBrowserProfile;
1682
+ exports.createBrowserWriter = createBrowserWriter;
1585
1683
  exports.createCDPTransport = createCDPTransport;
1586
- exports.createScreenshotWriter = createScreenshotWriter;
1587
- exports.defaultInstallPaths = defaultInstallPaths;
1588
- exports.defaultStoreBases = defaultStoreBases;
1589
1684
  exports.fetchCDPTargets = fetchCDPTargets;
1590
- exports.findAllEnvOverrides = findAllEnvOverrides;
1591
- exports.findAllInStore = findAllInStore;
1592
- exports.findAllInstallPaths = findAllInstallPaths;
1593
- exports.findEnvOverride = findEnvOverride;
1594
- exports.findInStore = findInStore;
1595
- exports.findInstallPath = findInstallPath;
1685
+ exports.findEnvOverrides = findEnvOverrides;
1686
+ exports.findInstallPaths = findInstallPaths;
1687
+ exports.findStorePaths = findStorePaths;
1596
1688
  exports.findSystemBrowser = findSystemBrowser;
1597
1689
  exports.findSystemBrowsers = findSystemBrowsers;
1598
1690
  exports.isBrowserConnectionError = isBrowserConnectionError;
@@ -1601,11 +1693,9 @@ exports.isBrowserNotConnectedError = isBrowserNotConnectedError;
1601
1693
  exports.launchBrowserProcess = launchBrowserProcess;
1602
1694
  exports.normalizeExecutablePath = normalizeExecutablePath;
1603
1695
  exports.parseBrowserEngine = parseBrowserEngine;
1604
- exports.probeAllPathNames = probeAllPathNames;
1605
1696
  exports.probePathNames = probePathNames;
1606
1697
  exports.readFirstLine = readFirstLine;
1607
1698
  exports.removeBrowserProfile = removeBrowserProfile;
1608
1699
  exports.waitForCDPReady = waitForCDPReady;
1609
- exports.windowsRoots = windowsRoots;
1610
1700
 
1611
1701
  //# sourceMappingURL=index.cjs.map