@orkestrel/browser 0.0.5 → 0.0.6
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 +12 -3
- package/dist/src/core/index.cjs +6556 -629
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +2906 -309
- package/dist/src/core/index.d.ts +2906 -309
- package/dist/src/core/index.js +6406 -631
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +144 -31
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +43 -0
- package/dist/src/server/index.d.ts +43 -0
- package/dist/src/server/index.js +144 -34
- package/dist/src/server/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BrowserContextInterface } from '../core/index.ts';
|
|
2
|
+
import { BrowserContextOptions } from '../core/index.ts';
|
|
2
3
|
import { BrowserError } from '../core/index.ts';
|
|
3
4
|
import { BrowserPageInterface } from '../core/index.ts';
|
|
4
5
|
import { BrowserPageOptions } from '../core/index.ts';
|
|
@@ -14,6 +15,9 @@ import { EmitterHooks } from '@orkestrel/emitter';
|
|
|
14
15
|
import { EmitterInterface } from '@orkestrel/emitter';
|
|
15
16
|
import { ScreenshotWriterInterface } from '../core/index.ts';
|
|
16
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Discovers, launches, connects to, and owns Chromium-family browser sessions.
|
|
20
|
+
*/
|
|
17
21
|
export declare class Browser implements BrowserInterface {
|
|
18
22
|
#private;
|
|
19
23
|
constructor(options?: BrowserOptions);
|
|
@@ -30,6 +34,7 @@ export declare class Browser implements BrowserInterface {
|
|
|
30
34
|
disconnect(): Promise<void>;
|
|
31
35
|
context(index?: number): BrowserContextInterface | undefined;
|
|
32
36
|
contexts(): readonly BrowserContextInterface[];
|
|
37
|
+
isolate(options?: BrowserContextOptions): Promise<BrowserContextInterface>;
|
|
33
38
|
create(options?: BrowserPageOptions): Promise<BrowserPageInterface>;
|
|
34
39
|
destroy(): Promise<void>;
|
|
35
40
|
close(): Promise<void>;
|
|
@@ -80,6 +85,9 @@ export declare const BROWSER_PORT_PROBE_TIMEOUT_MS = 200;
|
|
|
80
85
|
/** Machine-readable error-context cause for an owned browser process exiting. */
|
|
81
86
|
export declare const BROWSER_PROCESS_EXIT_CAUSE = "process-exit";
|
|
82
87
|
|
|
88
|
+
/** Prefix for isolated browser profiles created beneath the operating-system temp directory. */
|
|
89
|
+
export declare const BROWSER_PROFILE_PREFIX = "orkestrel-browser-";
|
|
90
|
+
|
|
83
91
|
/** Per-OS default Playwright browser cache directory, relative to the home directory (win32 uses `LOCALAPPDATA` directly). */
|
|
84
92
|
export declare const BROWSER_STORE_CACHE_DIRS: Readonly<Record<string, string>>;
|
|
85
93
|
|
|
@@ -190,6 +198,7 @@ export declare type BrowserEventMap = {
|
|
|
190
198
|
readonly disconnect: readonly [];
|
|
191
199
|
readonly launch: readonly [engine: BrowserEngine];
|
|
192
200
|
readonly page: readonly [page: BrowserPageInterface];
|
|
201
|
+
readonly context: readonly [context: BrowserContextInterface];
|
|
193
202
|
readonly error: readonly [error: unknown];
|
|
194
203
|
readonly destroy: readonly [];
|
|
195
204
|
};
|
|
@@ -266,6 +275,7 @@ export declare interface BrowserInterface {
|
|
|
266
275
|
disconnect(): Promise<void>;
|
|
267
276
|
context(index?: number): BrowserContextInterface | undefined;
|
|
268
277
|
contexts(): readonly BrowserContextInterface[];
|
|
278
|
+
isolate(options?: BrowserContextOptions): Promise<BrowserContextInterface>;
|
|
269
279
|
create(options?: BrowserPageOptions): Promise<BrowserPageInterface>;
|
|
270
280
|
destroy(): Promise<void>;
|
|
271
281
|
close(): Promise<void>;
|
|
@@ -315,6 +325,19 @@ export declare interface BrowserOptions {
|
|
|
315
325
|
readonly browsers?: SystemBrowserOptions;
|
|
316
326
|
}
|
|
317
327
|
|
|
328
|
+
/**
|
|
329
|
+
* Resolved browser profile directory used for a Chromium-family launch.
|
|
330
|
+
*
|
|
331
|
+
* @remarks
|
|
332
|
+
* `temporary` is true only for an isolated profile created by
|
|
333
|
+
* {@link createBrowserProfile}; caller-supplied persistent profiles are
|
|
334
|
+
* never removed by the library.
|
|
335
|
+
*/
|
|
336
|
+
export declare interface BrowserProfileResult {
|
|
337
|
+
readonly path: string;
|
|
338
|
+
readonly temporary: boolean;
|
|
339
|
+
}
|
|
340
|
+
|
|
318
341
|
/** Lifecycle status of a browser wrapper. */
|
|
319
342
|
export declare type BrowserStatus = 'idle' | 'connecting' | 'connected' | 'disconnected' | 'error';
|
|
320
343
|
|
|
@@ -337,6 +360,14 @@ export declare function browserToEngine(browser?: string): BrowserEngine;
|
|
|
337
360
|
*/
|
|
338
361
|
export declare function createBrowser(options?: BrowserOptions): BrowserInterface;
|
|
339
362
|
|
|
363
|
+
/**
|
|
364
|
+
* Resolve a persistent caller profile or create an isolated temporary one.
|
|
365
|
+
*
|
|
366
|
+
* @param path - Optional caller-owned persistent user-data directory
|
|
367
|
+
* @returns The resolved profile and whether the library owns its lifecycle
|
|
368
|
+
*/
|
|
369
|
+
export declare function createBrowserProfile(path?: string): Promise<BrowserProfileResult>;
|
|
370
|
+
|
|
340
371
|
/**
|
|
341
372
|
* Create a Node `WebSocket`-backed CDP transport.
|
|
342
373
|
*
|
|
@@ -473,6 +504,18 @@ export declare function probeAllPathNames(names: readonly string[], platform: st
|
|
|
473
504
|
/** Probe PATH (`which`/`where`) for the first resolvable command name. */
|
|
474
505
|
export declare function probePathNames(names: readonly string[], platform: string): string | undefined;
|
|
475
506
|
|
|
507
|
+
/**
|
|
508
|
+
* Remove a library-owned isolated browser profile.
|
|
509
|
+
*
|
|
510
|
+
* @remarks
|
|
511
|
+
* The path must remain a direct child of the operating-system temp directory
|
|
512
|
+
* with {@link BROWSER_PROFILE_PREFIX}; this defensive check prevents a
|
|
513
|
+
* malformed profile value from widening recursive deletion.
|
|
514
|
+
*
|
|
515
|
+
* @param profile - Resolved profile lifecycle result
|
|
516
|
+
*/
|
|
517
|
+
export declare function removeBrowserProfile(profile: BrowserProfileResult): Promise<void>;
|
|
518
|
+
|
|
476
519
|
/**
|
|
477
520
|
* One discovered browser executable on this machine.
|
|
478
521
|
*
|
package/dist/src/server/index.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import { BROWSER_DEFAULT_TIMEOUT_MS, BROWSER_WAIT_POLL_INTERVAL_MS, BrowserContext, BrowserError, CDPClient } from "../core/index.js";
|
|
1
|
+
import { BROWSER_DEFAULT_TIMEOUT_MS, BROWSER_WAIT_POLL_INTERVAL_MS, BrowserContext, BrowserError, CDPClient, validateBrowserContextOptions } from "../core/index.js";
|
|
2
|
+
import { attempt, isArray, isError, isInstance, isInteger, isRecord, isString } from "@orkestrel/contract";
|
|
2
3
|
import { existsSync, globSync } from "node:fs";
|
|
3
|
-
import {
|
|
4
|
+
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { basename, dirname, join, posix, resolve, win32 } from "node:path";
|
|
4
7
|
import { spawn, spawnSync } from "node:child_process";
|
|
5
8
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
6
|
-
import { isRecord, isString } from "@orkestrel/contract";
|
|
7
9
|
import { randomBytes } from "node:crypto";
|
|
8
10
|
import { request } from "node:http";
|
|
9
11
|
import { request as request$1 } from "node:https";
|
|
10
12
|
import { Emitter } from "@orkestrel/emitter";
|
|
11
13
|
import { WEBSOCKET_READY_CLOSED, WEBSOCKET_READY_OPEN, WEBSOCKET_VERSION, computeWebSocketAccept, createNodeWebSocket } from "@orkestrel/websocket";
|
|
12
14
|
import { addAbortListener, once } from "node:events";
|
|
13
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
14
15
|
//#region src/server/constants.ts
|
|
15
16
|
/** Default CDP port probed for an existing browser and used for launches. */
|
|
16
17
|
var BROWSER_DEFAULT_CDP_PORT = 9222;
|
|
@@ -26,6 +27,8 @@ var BROWSER_CDP_LIST_PATH = "/json/list";
|
|
|
26
27
|
var BROWSER_LAUNCH_ARGS = Object.freeze(["--no-first-run", "--no-default-browser-check"]);
|
|
27
28
|
/** Flag enabling headless mode on a launched browser process. */
|
|
28
29
|
var BROWSER_HEADLESS_ARG = "--headless=new";
|
|
30
|
+
/** Prefix for isolated browser profiles created beneath the operating-system temp directory. */
|
|
31
|
+
var BROWSER_PROFILE_PREFIX = "orkestrel-browser-";
|
|
29
32
|
/** Grace period after SIGTERM before a launched process is escalated to SIGKILL during teardown. */
|
|
30
33
|
var BROWSER_KILL_GRACE_MS = 3e3;
|
|
31
34
|
/** 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. */
|
|
@@ -157,7 +160,7 @@ var BrowserDestroyedError = class extends BrowserError {
|
|
|
157
160
|
* @returns True when value is a BrowserConnectionError instance
|
|
158
161
|
*/
|
|
159
162
|
function isBrowserConnectionError(value) {
|
|
160
|
-
return value
|
|
163
|
+
return isInstance(value, BrowserConnectionError);
|
|
161
164
|
}
|
|
162
165
|
/**
|
|
163
166
|
* Narrow an unknown value to BrowserNotConnectedError.
|
|
@@ -166,7 +169,7 @@ function isBrowserConnectionError(value) {
|
|
|
166
169
|
* @returns True when value is a BrowserNotConnectedError instance
|
|
167
170
|
*/
|
|
168
171
|
function isBrowserNotConnectedError(value) {
|
|
169
|
-
return value
|
|
172
|
+
return isInstance(value, BrowserNotConnectedError);
|
|
170
173
|
}
|
|
171
174
|
/**
|
|
172
175
|
* Narrow an unknown value to BrowserDestroyedError.
|
|
@@ -175,7 +178,7 @@ function isBrowserNotConnectedError(value) {
|
|
|
175
178
|
* @returns True when value is a BrowserDestroyedError instance
|
|
176
179
|
*/
|
|
177
180
|
function isBrowserDestroyedError(value) {
|
|
178
|
-
return value
|
|
181
|
+
return isInstance(value, BrowserDestroyedError);
|
|
179
182
|
}
|
|
180
183
|
//#endregion
|
|
181
184
|
//#region src/server/helpers.ts
|
|
@@ -263,6 +266,44 @@ function browserToEngine(browser) {
|
|
|
263
266
|
if (browser?.includes("Chrome/") === true) return "chrome";
|
|
264
267
|
return "chromium";
|
|
265
268
|
}
|
|
269
|
+
/**
|
|
270
|
+
* Resolve a persistent caller profile or create an isolated temporary one.
|
|
271
|
+
*
|
|
272
|
+
* @param path - Optional caller-owned persistent user-data directory
|
|
273
|
+
* @returns The resolved profile and whether the library owns its lifecycle
|
|
274
|
+
*/
|
|
275
|
+
async function createBrowserProfile(path) {
|
|
276
|
+
if (path !== void 0) return {
|
|
277
|
+
path,
|
|
278
|
+
temporary: false
|
|
279
|
+
};
|
|
280
|
+
return {
|
|
281
|
+
path: await mkdtemp(join(tmpdir(), BROWSER_PROFILE_PREFIX)),
|
|
282
|
+
temporary: true
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Remove a library-owned isolated browser profile.
|
|
287
|
+
*
|
|
288
|
+
* @remarks
|
|
289
|
+
* The path must remain a direct child of the operating-system temp directory
|
|
290
|
+
* with {@link BROWSER_PROFILE_PREFIX}; this defensive check prevents a
|
|
291
|
+
* malformed profile value from widening recursive deletion.
|
|
292
|
+
*
|
|
293
|
+
* @param profile - Resolved profile lifecycle result
|
|
294
|
+
*/
|
|
295
|
+
async function removeBrowserProfile(profile) {
|
|
296
|
+
if (!profile.temporary) return;
|
|
297
|
+
const path = resolve(profile.path);
|
|
298
|
+
const temporary = resolve(tmpdir());
|
|
299
|
+
if (dirname(path) !== temporary || !basename(path).startsWith("orkestrel-browser-")) throw new Error("Refusing to remove an unsafe browser profile path");
|
|
300
|
+
await rm(path, {
|
|
301
|
+
recursive: true,
|
|
302
|
+
force: true,
|
|
303
|
+
maxRetries: 3,
|
|
304
|
+
retryDelay: 100
|
|
305
|
+
});
|
|
306
|
+
}
|
|
266
307
|
/** Check the env-override keys (`PLAYWRIGHT_EXECUTABLE_PATH`, `CHROME_PATH`) in order for an existing file. */
|
|
267
308
|
function findEnvOverride(env) {
|
|
268
309
|
return findAllEnvOverrides(env)[0];
|
|
@@ -415,7 +456,7 @@ async function fetchCDPTargets(port, timeout, host = BROWSER_DEFAULT_HOST) {
|
|
|
415
456
|
const response = await fetch(url, { signal: controller.signal });
|
|
416
457
|
if (!response.ok) return [];
|
|
417
458
|
const list = await response.json();
|
|
418
|
-
if (!
|
|
459
|
+
if (!isArray(list)) return [];
|
|
419
460
|
const targets = [];
|
|
420
461
|
for (const entry of list) {
|
|
421
462
|
if (!isRecord(entry)) continue;
|
|
@@ -479,12 +520,12 @@ var WebSocketCDPTransport = class {
|
|
|
479
520
|
}
|
|
480
521
|
this.#socket?.destroy();
|
|
481
522
|
this.#socket = void 0;
|
|
482
|
-
const
|
|
483
|
-
this.#starting =
|
|
523
|
+
const transition = this.#open();
|
|
524
|
+
this.#starting = transition;
|
|
484
525
|
try {
|
|
485
|
-
await
|
|
526
|
+
await transition;
|
|
486
527
|
} finally {
|
|
487
|
-
if (this.#starting ===
|
|
528
|
+
if (this.#starting === transition) this.#starting = void 0;
|
|
488
529
|
}
|
|
489
530
|
}
|
|
490
531
|
async send(data) {
|
|
@@ -498,16 +539,21 @@ var WebSocketCDPTransport = class {
|
|
|
498
539
|
await active;
|
|
499
540
|
return;
|
|
500
541
|
}
|
|
501
|
-
const
|
|
502
|
-
this.#closing =
|
|
542
|
+
const transition = this.#stop();
|
|
543
|
+
this.#closing = transition;
|
|
503
544
|
try {
|
|
504
|
-
await
|
|
545
|
+
await transition;
|
|
505
546
|
} finally {
|
|
506
|
-
if (this.#closing ===
|
|
547
|
+
if (this.#closing === transition) this.#closing = void 0;
|
|
507
548
|
}
|
|
508
549
|
}
|
|
509
550
|
async #open() {
|
|
510
|
-
const
|
|
551
|
+
const parsed = attempt(() => new URL(this.#url));
|
|
552
|
+
if (!parsed.success) throw new BrowserConnectionError(`WebSocket CDP URL is invalid: ${this.#url}`, {
|
|
553
|
+
url: this.#url,
|
|
554
|
+
error: parsed.error
|
|
555
|
+
});
|
|
556
|
+
const url = parsed.value;
|
|
511
557
|
if (url.protocol !== "ws:" && url.protocol !== "wss:") throw new BrowserConnectionError(`WebSocket CDP connection requires a ws: or wss: URL: ${this.#url}`, { url: this.#url });
|
|
512
558
|
const key = randomBytes(16).toString("base64");
|
|
513
559
|
const deferred = Promise.withResolvers();
|
|
@@ -669,6 +715,9 @@ function createScreenshotWriter() {
|
|
|
669
715
|
}
|
|
670
716
|
//#endregion
|
|
671
717
|
//#region src/server/Browser.ts
|
|
718
|
+
/**
|
|
719
|
+
* Discovers, launches, connects to, and owns Chromium-family browser sessions.
|
|
720
|
+
*/
|
|
672
721
|
var Browser = class {
|
|
673
722
|
#emitter;
|
|
674
723
|
#options;
|
|
@@ -682,6 +731,7 @@ var Browser = class {
|
|
|
682
731
|
#endpoint;
|
|
683
732
|
#client;
|
|
684
733
|
#process;
|
|
734
|
+
#profile;
|
|
685
735
|
#contexts = [];
|
|
686
736
|
#destroyed = false;
|
|
687
737
|
#connecting;
|
|
@@ -779,6 +829,35 @@ var Browser = class {
|
|
|
779
829
|
contexts() {
|
|
780
830
|
return [...this.#contexts];
|
|
781
831
|
}
|
|
832
|
+
async isolate(options) {
|
|
833
|
+
if (this.#destroyed) throw new BrowserDestroyedError();
|
|
834
|
+
const client = this.#client;
|
|
835
|
+
if (this.#status !== "connected" || client === void 0) throw new BrowserNotConnectedError();
|
|
836
|
+
validateBrowserContextOptions(options);
|
|
837
|
+
const params = { disposeOnDetach: false };
|
|
838
|
+
if (options?.proxy !== void 0) {
|
|
839
|
+
params["proxyServer"] = options.proxy.server;
|
|
840
|
+
if (options.proxy.bypass !== void 0) params["proxyBypassList"] = options.proxy.bypass.join(",");
|
|
841
|
+
}
|
|
842
|
+
if (options?.origins !== void 0) params["originsWithUniversalNetworkAccess"] = [...options.origins];
|
|
843
|
+
const result = await client.send("Target.createBrowserContext", params);
|
|
844
|
+
if (!isRecord(result) || !isString(result["browserContextId"])) throw new BrowserConnectionError("Failed to create isolated browser context");
|
|
845
|
+
const id = result["browserContextId"];
|
|
846
|
+
const context = new BrowserContext(client, id, options?.emulation?.viewport ?? this.#options.viewport, createScreenshotWriter(), options?.emulation, options?.downloads);
|
|
847
|
+
try {
|
|
848
|
+
if (options?.downloads !== void 0) await client.send("Browser.setDownloadBehavior", {
|
|
849
|
+
behavior: options.downloads.named === true ? "allowAndName" : "allow",
|
|
850
|
+
browserContextId: id,
|
|
851
|
+
downloadPath: options.downloads.path,
|
|
852
|
+
eventsEnabled: true
|
|
853
|
+
});
|
|
854
|
+
} catch (error) {
|
|
855
|
+
await context.close().catch(() => void 0);
|
|
856
|
+
throw error;
|
|
857
|
+
}
|
|
858
|
+
this.#registerContext(context);
|
|
859
|
+
return context;
|
|
860
|
+
}
|
|
782
861
|
async create(options) {
|
|
783
862
|
if (this.#destroyed) throw new BrowserDestroyedError();
|
|
784
863
|
const client = this.#client;
|
|
@@ -786,7 +865,7 @@ var Browser = class {
|
|
|
786
865
|
let context = this.#contexts[0];
|
|
787
866
|
if (context === void 0) {
|
|
788
867
|
context = new BrowserContext(client, void 0, this.#options.viewport, createScreenshotWriter());
|
|
789
|
-
this.#
|
|
868
|
+
this.#registerContext(context);
|
|
790
869
|
}
|
|
791
870
|
const page = await context.create(options);
|
|
792
871
|
this.#emitter.emit("page", page);
|
|
@@ -839,8 +918,8 @@ var Browser = class {
|
|
|
839
918
|
if (this.#destroyed) throw new BrowserDestroyedError();
|
|
840
919
|
this.#status = "error";
|
|
841
920
|
this.#emitter.emit("error", error);
|
|
842
|
-
if (error
|
|
843
|
-
throw new BrowserConnectionError(error
|
|
921
|
+
if (isBrowserConnectionError(error)) throw error;
|
|
922
|
+
throw new BrowserConnectionError(isError(error) ? error.message : String(error), { executable: this.#options.executable });
|
|
844
923
|
}
|
|
845
924
|
}
|
|
846
925
|
async #detach() {
|
|
@@ -889,6 +968,7 @@ var Browser = class {
|
|
|
889
968
|
this.#unbindTransport();
|
|
890
969
|
this.#unbindProcess();
|
|
891
970
|
this.#process = void 0;
|
|
971
|
+
this.#releaseProfile().catch((error) => this.#emitter.emit("error", error));
|
|
892
972
|
this.#client = void 0;
|
|
893
973
|
this.#contexts = [];
|
|
894
974
|
this.#connection = void 0;
|
|
@@ -1063,8 +1143,16 @@ var Browser = class {
|
|
|
1063
1143
|
}
|
|
1064
1144
|
if (executable === void 0) throw new BrowserConnectionError("No Chromium browser found. Install Chrome, Edge, or Chromium.", requestedEngine === void 0 ? void 0 : { engine: requestedEngine });
|
|
1065
1145
|
this.#engine = resolvedEngine ?? "chromium";
|
|
1066
|
-
|
|
1067
|
-
const
|
|
1146
|
+
await this.#releaseProfile();
|
|
1147
|
+
const profile = await createBrowserProfile(this.#options.profile);
|
|
1148
|
+
this.#profile = profile;
|
|
1149
|
+
let process;
|
|
1150
|
+
try {
|
|
1151
|
+
process = launchBrowserProcess(executable, this.#cdpPort, this.#options.headless ?? true, profile.path, this.#options.args);
|
|
1152
|
+
} catch (error) {
|
|
1153
|
+
await this.#releaseProfile();
|
|
1154
|
+
throw error;
|
|
1155
|
+
}
|
|
1068
1156
|
this.#process = process;
|
|
1069
1157
|
let client;
|
|
1070
1158
|
try {
|
|
@@ -1078,7 +1166,7 @@ var Browser = class {
|
|
|
1078
1166
|
timeout: this.#timeout()
|
|
1079
1167
|
});
|
|
1080
1168
|
await this.#raceAbort(client.connect());
|
|
1081
|
-
const connection = profile === void 0 ? "launch" : "persistent";
|
|
1169
|
+
const connection = this.#options.profile === void 0 ? "launch" : "persistent";
|
|
1082
1170
|
this.#client = client;
|
|
1083
1171
|
this.#endpoint = endpoint;
|
|
1084
1172
|
this.#connection = connection;
|
|
@@ -1094,6 +1182,7 @@ var Browser = class {
|
|
|
1094
1182
|
this.#unbindProcess();
|
|
1095
1183
|
await this.#closeClient(client);
|
|
1096
1184
|
await this.#terminate(process);
|
|
1185
|
+
await this.#releaseProfile();
|
|
1097
1186
|
this.#process = void 0;
|
|
1098
1187
|
this.#client = void 0;
|
|
1099
1188
|
this.#endpoint = void 0;
|
|
@@ -1110,16 +1199,16 @@ var Browser = class {
|
|
|
1110
1199
|
const signal = AbortSignal.any([controller.signal, this.#signal()]);
|
|
1111
1200
|
const ready = waitForCDPReady(this.#cdpPort, this.#timeout(), this.#cdpHost, signal);
|
|
1112
1201
|
const exited = once(process, "exit", { signal }).then((values) => {
|
|
1113
|
-
const code =
|
|
1114
|
-
const exitSignal =
|
|
1202
|
+
const code = isInteger(values[0]) ? values[0] : null;
|
|
1203
|
+
const exitSignal = isString(values[1]) ? values[1] : null;
|
|
1115
1204
|
throw new BrowserConnectionError(this.#formatLaunchExit(code, exitSignal), context);
|
|
1116
1205
|
});
|
|
1117
1206
|
try {
|
|
1118
1207
|
return await Promise.race([ready, exited]);
|
|
1119
1208
|
} catch (error) {
|
|
1120
|
-
if (error
|
|
1209
|
+
if (isBrowserConnectionError(error)) throw error;
|
|
1121
1210
|
if (this.#signal().aborted) throw new BrowserConnectionError("Connection aborted", context);
|
|
1122
|
-
throw new BrowserConnectionError(error
|
|
1211
|
+
throw new BrowserConnectionError(isError(error) ? error.message : String(error), context);
|
|
1123
1212
|
} finally {
|
|
1124
1213
|
controller.abort();
|
|
1125
1214
|
ready.catch(() => void 0);
|
|
@@ -1144,7 +1233,7 @@ var Browser = class {
|
|
|
1144
1233
|
} catch {
|
|
1145
1234
|
return;
|
|
1146
1235
|
}
|
|
1147
|
-
if (!isRecord(result) || !
|
|
1236
|
+
if (!isRecord(result) || !isArray(result["targetInfos"])) return;
|
|
1148
1237
|
const pages = [];
|
|
1149
1238
|
for (const target of result["targetInfos"]) {
|
|
1150
1239
|
if (!isRecord(target) || !isString(target["targetId"]) || target["type"] !== "page" || !isString(target["title"]) || !isString(target["url"])) continue;
|
|
@@ -1158,7 +1247,7 @@ var Browser = class {
|
|
|
1158
1247
|
if (pages.length === 0 || this.#contexts.length > 0) return;
|
|
1159
1248
|
const context = new BrowserContext(client, void 0, this.#options.viewport, createScreenshotWriter());
|
|
1160
1249
|
await context.sync(pages);
|
|
1161
|
-
this.#
|
|
1250
|
+
this.#registerContext(context);
|
|
1162
1251
|
}
|
|
1163
1252
|
async #destroyResources() {
|
|
1164
1253
|
try {
|
|
@@ -1176,7 +1265,11 @@ var Browser = class {
|
|
|
1176
1265
|
await this.#closeClient(client);
|
|
1177
1266
|
this.#client = void 0;
|
|
1178
1267
|
} finally {
|
|
1179
|
-
|
|
1268
|
+
try {
|
|
1269
|
+
await this.#releaseProfile();
|
|
1270
|
+
} finally {
|
|
1271
|
+
this.#finish();
|
|
1272
|
+
}
|
|
1180
1273
|
}
|
|
1181
1274
|
}
|
|
1182
1275
|
async #closeResources() {
|
|
@@ -1196,7 +1289,11 @@ var Browser = class {
|
|
|
1196
1289
|
await this.#closeClient(client);
|
|
1197
1290
|
this.#client = void 0;
|
|
1198
1291
|
} finally {
|
|
1199
|
-
|
|
1292
|
+
try {
|
|
1293
|
+
await this.#releaseProfile();
|
|
1294
|
+
} finally {
|
|
1295
|
+
this.#finish();
|
|
1296
|
+
}
|
|
1200
1297
|
}
|
|
1201
1298
|
}
|
|
1202
1299
|
async #settle() {
|
|
@@ -1228,12 +1325,20 @@ var Browser = class {
|
|
|
1228
1325
|
if (temporary) await this.#closeClient(remote);
|
|
1229
1326
|
}
|
|
1230
1327
|
async #closeContexts() {
|
|
1231
|
-
for (const context of this.#contexts) try {
|
|
1328
|
+
for (const context of [...this.#contexts]) try {
|
|
1232
1329
|
await context.close();
|
|
1233
1330
|
} catch {}
|
|
1234
1331
|
}
|
|
1235
1332
|
async #destroyContexts() {
|
|
1236
|
-
await this.#destroyContextList(this.#contexts);
|
|
1333
|
+
await this.#destroyContextList([...this.#contexts]);
|
|
1334
|
+
}
|
|
1335
|
+
#registerContext(context) {
|
|
1336
|
+
this.#contexts.push(context);
|
|
1337
|
+
context.emitter.on("close", () => {
|
|
1338
|
+
const index = this.#contexts.indexOf(context);
|
|
1339
|
+
if (index >= 0) this.#contexts.splice(index, 1);
|
|
1340
|
+
});
|
|
1341
|
+
this.#emitter.emit("context", context);
|
|
1237
1342
|
}
|
|
1238
1343
|
async #destroyContextList(contexts) {
|
|
1239
1344
|
for (const context of contexts) try {
|
|
@@ -1246,6 +1351,11 @@ var Browser = class {
|
|
|
1246
1351
|
await client.close();
|
|
1247
1352
|
} catch {}
|
|
1248
1353
|
}
|
|
1354
|
+
async #releaseProfile() {
|
|
1355
|
+
const profile = this.#profile;
|
|
1356
|
+
this.#profile = void 0;
|
|
1357
|
+
if (profile !== void 0) await removeBrowserProfile(profile);
|
|
1358
|
+
}
|
|
1249
1359
|
async #waitForProcess(process) {
|
|
1250
1360
|
if (process.exitCode !== null || process.signalCode !== null) return;
|
|
1251
1361
|
await once(process, "exit");
|
|
@@ -1291,6 +1401,6 @@ var Browser = class {
|
|
|
1291
1401
|
}
|
|
1292
1402
|
};
|
|
1293
1403
|
//#endregion
|
|
1294
|
-
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_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, createBrowser, createCDPTransport, createScreenshotWriter, defaultInstallPaths, defaultStoreBases, fetchCDPTargets, findAllEnvOverrides, findAllInStore, findAllInstallPaths, findEnvOverride, findInStore, findInstallPath, findSystemBrowser, findSystemBrowsers, isBrowserConnectionError, isBrowserDestroyedError, isBrowserNotConnectedError, launchBrowserProcess, normalizeExecutablePath, parseBrowserEngine, probeAllPathNames, probePathNames, waitForCDPReady, windowsRoots };
|
|
1404
|
+
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, createBrowser, createBrowserProfile, createCDPTransport, createScreenshotWriter, defaultInstallPaths, defaultStoreBases, fetchCDPTargets, findAllEnvOverrides, findAllInStore, findAllInstallPaths, findEnvOverride, findInStore, findInstallPath, findSystemBrowser, findSystemBrowsers, isBrowserConnectionError, isBrowserDestroyedError, isBrowserNotConnectedError, launchBrowserProcess, normalizeExecutablePath, parseBrowserEngine, probeAllPathNames, probePathNames, removeBrowserProfile, waitForCDPReady, windowsRoots };
|
|
1295
1405
|
|
|
1296
1406
|
//# sourceMappingURL=index.js.map
|