@crawlee/browser-pool 4.0.0-beta.211 → 4.0.0-beta.212
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/abstract-classes/browser-plugin.d.ts +1 -1
- package/abstract-classes/browser-plugin.js +2 -2
- package/browser-pool.d.ts +0 -12
- package/browser-pool.js +31 -28
- package/package.json +5 -5
- package/playwright/playwright-plugin.d.ts +0 -1
- package/playwright/playwright-plugin.js +3 -3
- package/remote-browser-pool.d.ts +0 -8
- package/remote-browser-pool.js +4 -4
|
@@ -81,6 +81,7 @@ export interface CreateLaunchContextOptions<Library extends CommonLibrary, Libra
|
|
|
81
81
|
* feed them to {@link BrowserPool} for use.
|
|
82
82
|
*/
|
|
83
83
|
export declare abstract class BrowserPlugin<Library extends CommonLibrary = CommonLibrary, LibraryOptions extends Dictionary | undefined = Parameters<Library['launch']>[0], LaunchResult extends CommonBrowser = UnwrapPromise<ReturnType<Library['launch']>>, NewPageOptions = Parameters<LaunchResult['newPage']>[0], NewPageResult = UnwrapPromise<ReturnType<LaunchResult['newPage']>>> {
|
|
84
|
+
#private;
|
|
84
85
|
readonly name: string;
|
|
85
86
|
/**
|
|
86
87
|
* Kept `protected` rather than `#private` because the concrete plugins in this package are separate
|
|
@@ -136,7 +137,6 @@ export declare abstract class BrowserPlugin<Library extends CommonLibrary = Comm
|
|
|
136
137
|
* Launches the browser using provided launch context.
|
|
137
138
|
*/
|
|
138
139
|
launch(launchContext?: LaunchContext<Library, LibraryOptions, LaunchResult, NewPageOptions, NewPageResult>): Promise<LaunchResult>;
|
|
139
|
-
private mergeArgsToHideWebdriver;
|
|
140
140
|
protected throwAugmentedLaunchError(cause: unknown, executablePath: string | undefined, dockerImage: string, moduleInstallCommand: string): never;
|
|
141
141
|
protected abstract addProxyToLaunchOptions(launchContext: LaunchContext<Library, LibraryOptions, LaunchResult, NewPageOptions, NewPageResult>): Promise<void>;
|
|
142
142
|
protected abstract isChromiumBasedBrowser(launchContext: LaunchContext<Library, LibraryOptions, LaunchResult, NewPageOptions, NewPageResult>): boolean;
|
|
@@ -125,7 +125,7 @@ export class BrowserPlugin {
|
|
|
125
125
|
}
|
|
126
126
|
if (!launchContext.isRemote && this.isChromiumBasedBrowser(launchContext)) {
|
|
127
127
|
// This will set the args for chromium based browsers to hide the webdriver.
|
|
128
|
-
launchOptions.args = this
|
|
128
|
+
launchOptions.args = this.#mergeArgsToHideWebdriver(launchOptions.args);
|
|
129
129
|
// When User-Agent is not set, and we're using Chromium in headless mode,
|
|
130
130
|
// it is better to use DEFAULT_USER_AGENT to reduce chance of detection,
|
|
131
131
|
// as otherwise 'HeadlessChrome' is present in User-Agent string.
|
|
@@ -139,7 +139,7 @@ export class BrowserPlugin {
|
|
|
139
139
|
}
|
|
140
140
|
return this._launch(launchContext);
|
|
141
141
|
}
|
|
142
|
-
mergeArgsToHideWebdriver(originalArgs) {
|
|
142
|
+
#mergeArgsToHideWebdriver(originalArgs) {
|
|
143
143
|
if (!originalArgs?.length) {
|
|
144
144
|
return ['--disable-blink-features=AutomationControlled'];
|
|
145
145
|
}
|
package/browser-pool.d.ts
CHANGED
|
@@ -337,7 +337,6 @@ export declare class BrowserPool<Options extends BrowserPoolOptions = BrowserPoo
|
|
|
337
337
|
* until it's closed.
|
|
338
338
|
*/
|
|
339
339
|
getPageId(page: PageReturn): string | undefined;
|
|
340
|
-
private createPageForBrowser;
|
|
341
340
|
/**
|
|
342
341
|
* Removes a browser controller from the pool. The underlying
|
|
343
342
|
* browser will be closed after all its pages are closed.
|
|
@@ -408,17 +407,7 @@ export declare class BrowserPool<Options extends BrowserPoolOptions = BrowserPoo
|
|
|
408
407
|
* re-armed and its listeners are dropped, so it cannot be used again.
|
|
409
408
|
*/
|
|
410
409
|
destroy(): Promise<void>;
|
|
411
|
-
private getAllBrowserControllers;
|
|
412
|
-
private launchBrowser;
|
|
413
|
-
/**
|
|
414
|
-
* Picks plugins round robin.
|
|
415
|
-
*/
|
|
416
|
-
private pickBrowserPlugin;
|
|
417
|
-
private pickBrowserWithFreeCapacity;
|
|
418
410
|
private closeInactiveRetiredBrowsers;
|
|
419
|
-
private overridePageClose;
|
|
420
|
-
private executeHooks;
|
|
421
|
-
private closeRetiredBrowserWithNoPages;
|
|
422
411
|
/**
|
|
423
412
|
* Returns `true` if the pool can accept a new browser launch without exceeding `maxOpenBrowsers`.
|
|
424
413
|
* Counts starting, active, and retired browsers.
|
|
@@ -437,7 +426,6 @@ export declare class BrowserPool<Options extends BrowserPoolOptions = BrowserPoo
|
|
|
437
426
|
* @internal
|
|
438
427
|
*/
|
|
439
428
|
hasActiveBrowserWithFreeCapacity(): boolean;
|
|
440
|
-
private initializeFingerprinting;
|
|
441
429
|
}
|
|
442
430
|
export interface BrowserPoolNewPageOptions<PageOptions, BP extends BrowserPlugin> extends NewPageOptions {
|
|
443
431
|
/**
|
package/browser-pool.js
CHANGED
|
@@ -99,14 +99,16 @@ export class BrowserPool extends TypedEmitter {
|
|
|
99
99
|
#prePageCloseHooks;
|
|
100
100
|
#postPageCloseHooks;
|
|
101
101
|
#pageCounter = 0;
|
|
102
|
-
//
|
|
102
|
+
// oxlint-disable-next-line crawlee/prefer-private-fields -- tests observe page tracking and controller retirement directly
|
|
103
103
|
pages = new Map();
|
|
104
104
|
#pageIds = new WeakMap();
|
|
105
105
|
#startingBrowserControllers = new Set();
|
|
106
|
+
// oxlint-disable-next-line crawlee/prefer-private-fields -- accessed by tests
|
|
106
107
|
activeBrowserControllers = new Set();
|
|
108
|
+
// oxlint-disable-next-line crawlee/prefer-private-fields -- accessed by tests
|
|
107
109
|
retiredBrowserControllers = new Set();
|
|
108
110
|
#pageToBrowserController = new WeakMap();
|
|
109
|
-
//
|
|
111
|
+
// oxlint-disable-next-line crawlee/prefer-private-fields -- tests replace this interval through bracket access
|
|
110
112
|
browserKillerInterval;
|
|
111
113
|
#browserRetireInterval;
|
|
112
114
|
#limiter = pLimit(1);
|
|
@@ -151,7 +153,7 @@ export class BrowserPool extends TypedEmitter {
|
|
|
151
153
|
this.#postPageCloseHooks = postPageCloseHooks;
|
|
152
154
|
// fingerprinting
|
|
153
155
|
if (useFingerprints) {
|
|
154
|
-
this
|
|
156
|
+
this.#initializeFingerprinting();
|
|
155
157
|
// The fingerprint pre-launch hook goes last because of the fingerprint cache.
|
|
156
158
|
// It is usual to generate proxy per browser and we want to know the proxyUrl for the caching.
|
|
157
159
|
this.#preLaunchHooks = [...this.#preLaunchHooks, createFingerprintPreLaunchHook(this)];
|
|
@@ -180,7 +182,7 @@ export class BrowserPool extends TypedEmitter {
|
|
|
180
182
|
* crawler's responsibility.
|
|
181
183
|
*/
|
|
182
184
|
async newPage(options = {}) {
|
|
183
|
-
const { id = nanoid(), pageOptions, browserPlugin = this
|
|
185
|
+
const { id = nanoid(), pageOptions, browserPlugin = this.#pickBrowserPlugin(), session, proxyUrl = session?.proxyInfo?.url, ignoreTlsErrors = session?.proxyInfo?.ignoreTlsErrors, } = options;
|
|
184
186
|
if (this.pages.has(id)) {
|
|
185
187
|
throw new Error(`Page with ID: ${id} already exists.`);
|
|
186
188
|
}
|
|
@@ -199,15 +201,15 @@ export class BrowserPool extends TypedEmitter {
|
|
|
199
201
|
// TODO(crawlee@v4): bump p-limit to v5 and drop this AsyncResource.bind wrapper.
|
|
200
202
|
// Limiter is necessary - https://github.com/apify/crawlee/issues/1126
|
|
201
203
|
return this.#limiter(AsyncResource.bind(async () => {
|
|
202
|
-
let browserController = this
|
|
204
|
+
let browserController = this.#pickBrowserWithFreeCapacity(browserPlugin, { proxyUrl });
|
|
203
205
|
if (!browserController)
|
|
204
|
-
browserController = await this
|
|
206
|
+
browserController = await this.#launchBrowser(id, {
|
|
205
207
|
browserPlugin,
|
|
206
208
|
proxyUrl,
|
|
207
209
|
ignoreTlsErrors,
|
|
208
210
|
});
|
|
209
211
|
tryCancel();
|
|
210
|
-
return await this
|
|
212
|
+
return await this.#createPageForBrowser(id, browserController, pageOptions, proxyUrl, ignoreTlsErrors);
|
|
211
213
|
}));
|
|
212
214
|
}
|
|
213
215
|
/**
|
|
@@ -216,13 +218,13 @@ export class BrowserPool extends TypedEmitter {
|
|
|
216
218
|
* configure the new browser.
|
|
217
219
|
*/
|
|
218
220
|
async newPageInNewBrowser(options = {}) {
|
|
219
|
-
const { id = nanoid(), pageOptions, launchOptions, browserPlugin = this
|
|
221
|
+
const { id = nanoid(), pageOptions, launchOptions, browserPlugin = this.#pickBrowserPlugin() } = options;
|
|
220
222
|
if (this.pages.has(id)) {
|
|
221
223
|
throw new Error(`Page with ID: ${id} already exists.`);
|
|
222
224
|
}
|
|
223
|
-
const browserController = await this
|
|
225
|
+
const browserController = await this.#launchBrowser(id, { launchOptions, browserPlugin });
|
|
224
226
|
tryCancel();
|
|
225
|
-
return await this
|
|
227
|
+
return await this.#createPageForBrowser(id, browserController, pageOptions);
|
|
226
228
|
}
|
|
227
229
|
/**
|
|
228
230
|
* Opens new pages with all available plugins and returns an array
|
|
@@ -288,7 +290,7 @@ export class BrowserPool extends TypedEmitter {
|
|
|
288
290
|
getPageId(page) {
|
|
289
291
|
return this.#pageIds.get(page);
|
|
290
292
|
}
|
|
291
|
-
async createPageForBrowser(pageId, browserController, pageOptions = {}, proxyUrl, ignoreTlsErrors) {
|
|
293
|
+
async #createPageForBrowser(pageId, browserController, pageOptions = {}, proxyUrl, ignoreTlsErrors) {
|
|
292
294
|
// This is needed for concurrent newPage calls to wait for the browser launch.
|
|
293
295
|
// It's not ideal though, we need to come up with a better API.
|
|
294
296
|
await browserController.waitForActive();
|
|
@@ -303,7 +305,7 @@ export class BrowserPool extends TypedEmitter {
|
|
|
303
305
|
});
|
|
304
306
|
}
|
|
305
307
|
}
|
|
306
|
-
await this
|
|
308
|
+
await this.#executeHooks(this.#prePageCreateHooks, pageId, browserController, finalPageOptions);
|
|
307
309
|
tryCancel();
|
|
308
310
|
let page;
|
|
309
311
|
try {
|
|
@@ -316,13 +318,13 @@ export class BrowserPool extends TypedEmitter {
|
|
|
316
318
|
if (browserController.totalPages >= this.#retireBrowserAfterPageCount) {
|
|
317
319
|
this.retireBrowserController(browserController);
|
|
318
320
|
}
|
|
319
|
-
this
|
|
321
|
+
this.#overridePageClose(page);
|
|
320
322
|
}
|
|
321
323
|
catch (err) {
|
|
322
324
|
this.retireBrowserController(browserController);
|
|
323
325
|
throw new Error(`browserController.newPage() failed: ${browserController.id}\nCause:${err.message}.`);
|
|
324
326
|
}
|
|
325
|
-
await this
|
|
327
|
+
await this.#executeHooks(this.#postPageCreateHooks, page, browserController);
|
|
326
328
|
tryCancel();
|
|
327
329
|
this.emit(BROWSER_POOL_EVENTS.PAGE_CREATED, page);
|
|
328
330
|
return page;
|
|
@@ -422,7 +424,7 @@ export class BrowserPool extends TypedEmitter {
|
|
|
422
424
|
* @return {Promise<void>}
|
|
423
425
|
*/
|
|
424
426
|
async closeAllBrowsers() {
|
|
425
|
-
const controllers = this
|
|
427
|
+
const controllers = this.#getAllBrowserControllers();
|
|
426
428
|
const promises = [...controllers]
|
|
427
429
|
.filter((controller) => controller.isActive)
|
|
428
430
|
.map(async (controller) => controller.close());
|
|
@@ -453,14 +455,14 @@ export class BrowserPool extends TypedEmitter {
|
|
|
453
455
|
await this.releaseAllBrowsers();
|
|
454
456
|
this.removeAllListeners();
|
|
455
457
|
}
|
|
456
|
-
getAllBrowserControllers() {
|
|
458
|
+
#getAllBrowserControllers() {
|
|
457
459
|
return new Set([
|
|
458
460
|
...this.#startingBrowserControllers,
|
|
459
461
|
...this.activeBrowserControllers,
|
|
460
462
|
...this.retiredBrowserControllers,
|
|
461
463
|
]);
|
|
462
464
|
}
|
|
463
|
-
async launchBrowser(pageId, options) {
|
|
465
|
+
async #launchBrowser(pageId, options) {
|
|
464
466
|
const { browserPlugin, launchOptions, proxyUrl, ignoreTlsErrors } = options;
|
|
465
467
|
const browserController = browserPlugin.createController();
|
|
466
468
|
this.#startingBrowserControllers.add(browserController);
|
|
@@ -481,7 +483,7 @@ export class BrowserPool extends TypedEmitter {
|
|
|
481
483
|
try {
|
|
482
484
|
// If the hooks or the launch fails, we need to delete the controller,
|
|
483
485
|
// because otherwise it would be stuck in limbo without a browser.
|
|
484
|
-
await this
|
|
486
|
+
await this.#executeHooks(this.#preLaunchHooks, pageId, launchContext);
|
|
485
487
|
tryCancel();
|
|
486
488
|
const browser = await browserPlugin.launch(launchContext);
|
|
487
489
|
tryCancel();
|
|
@@ -496,7 +498,7 @@ export class BrowserPool extends TypedEmitter {
|
|
|
496
498
|
try {
|
|
497
499
|
// If the launch fails on the post-launch hooks, we need to clean up
|
|
498
500
|
// both the controller and the browser before throwing.
|
|
499
|
-
await this
|
|
501
|
+
await this.#executeHooks(this.#postLaunchHooks, pageId, browserController);
|
|
500
502
|
}
|
|
501
503
|
catch (err) {
|
|
502
504
|
this.#startingBrowserControllers.delete(browserController);
|
|
@@ -517,12 +519,12 @@ export class BrowserPool extends TypedEmitter {
|
|
|
517
519
|
/**
|
|
518
520
|
* Picks plugins round robin.
|
|
519
521
|
*/
|
|
520
|
-
pickBrowserPlugin() {
|
|
522
|
+
#pickBrowserPlugin() {
|
|
521
523
|
const pluginIndex = this.#pageCounter % this.browserPlugins.length;
|
|
522
524
|
this.#pageCounter++;
|
|
523
525
|
return this.browserPlugins[pluginIndex];
|
|
524
526
|
}
|
|
525
|
-
pickBrowserWithFreeCapacity(browserPlugin, options) {
|
|
527
|
+
#pickBrowserWithFreeCapacity(browserPlugin, options) {
|
|
526
528
|
return [...this.activeBrowserControllers].find((controller) => {
|
|
527
529
|
const hasCapacity = controller.activePages < this.#maxOpenPagesPerBrowser;
|
|
528
530
|
const isCorrectPlugin = controller.browserPlugin === browserPlugin;
|
|
@@ -534,6 +536,7 @@ export class BrowserPool extends TypedEmitter {
|
|
|
534
536
|
(!options?.proxyUrl && !controller.proxyUrl)));
|
|
535
537
|
});
|
|
536
538
|
}
|
|
539
|
+
// oxlint-disable-next-line crawlee/prefer-private-fields -- accessed by tests
|
|
537
540
|
async closeInactiveRetiredBrowsers() {
|
|
538
541
|
const closedBrowserIds = [];
|
|
539
542
|
for (const controller of this.retiredBrowserControllers) {
|
|
@@ -555,7 +558,7 @@ export class BrowserPool extends TypedEmitter {
|
|
|
555
558
|
});
|
|
556
559
|
}
|
|
557
560
|
}
|
|
558
|
-
overridePageClose(page) {
|
|
561
|
+
#overridePageClose(page) {
|
|
559
562
|
const originalPageClose = page.close;
|
|
560
563
|
const browserController = this.#pageToBrowserController.get(page);
|
|
561
564
|
const pageId = this.getPageId(page);
|
|
@@ -569,14 +572,14 @@ export class BrowserPool extends TypedEmitter {
|
|
|
569
572
|
// so a timeout here would cancel the task of whoever awaited `page.close()`.
|
|
570
573
|
let pageClosed = false;
|
|
571
574
|
const closing = (async () => {
|
|
572
|
-
await this
|
|
575
|
+
await this.#executeHooks(this.#prePageCloseHooks, page, browserController);
|
|
573
576
|
await originalPageClose.apply(page, args).catch((err) => {
|
|
574
577
|
this.#log.debug(`Could not close page.\nCause:${err.message}`, { id: browserController.id });
|
|
575
578
|
});
|
|
576
579
|
// Whether the page itself is gone. Tracked separately from the sequence finishing,
|
|
577
580
|
// so that a slow hook does not get the browser retired.
|
|
578
581
|
pageClosed = true;
|
|
579
|
-
await this
|
|
582
|
+
await this.#executeHooks(this.#postPageCloseHooks, pageId, browserController);
|
|
580
583
|
})();
|
|
581
584
|
let timeout;
|
|
582
585
|
const finished = await Promise.race([
|
|
@@ -600,16 +603,16 @@ export class BrowserPool extends TypedEmitter {
|
|
|
600
603
|
}
|
|
601
604
|
browserController.registerPageClosed(page);
|
|
602
605
|
this.pages.delete(pageId);
|
|
603
|
-
this
|
|
606
|
+
this.#closeRetiredBrowserWithNoPages(browserController);
|
|
604
607
|
this.emit(BROWSER_POOL_EVENTS.PAGE_CLOSED, page);
|
|
605
608
|
};
|
|
606
609
|
}
|
|
607
|
-
async executeHooks(hooks, ...args) {
|
|
610
|
+
async #executeHooks(hooks, ...args) {
|
|
608
611
|
for (const hook of hooks) {
|
|
609
612
|
await hook(...args);
|
|
610
613
|
}
|
|
611
614
|
}
|
|
612
|
-
closeRetiredBrowserWithNoPages(browserController) {
|
|
615
|
+
#closeRetiredBrowserWithNoPages(browserController) {
|
|
613
616
|
if (browserController.activePages === 0 && this.retiredBrowserControllers.has(browserController)) {
|
|
614
617
|
// Run this with a delay, otherwise page.close()
|
|
615
618
|
// might fail with "Protocol error (Target.closeTarget): Target closed."
|
|
@@ -650,7 +653,7 @@ export class BrowserPool extends TypedEmitter {
|
|
|
650
653
|
}
|
|
651
654
|
return false;
|
|
652
655
|
}
|
|
653
|
-
initializeFingerprinting() {
|
|
656
|
+
#initializeFingerprinting() {
|
|
654
657
|
const { useFingerprintCache = true, fingerprintCacheSize = 10_000 } = this.fingerprintOptions;
|
|
655
658
|
this.fingerprintGenerator = new FingerprintGenerator(this.fingerprintOptions.fingerprintGeneratorOptions);
|
|
656
659
|
this.fingerprintInjector = new FingerprintInjector();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crawlee/browser-pool",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.212",
|
|
4
4
|
"description": "Rotate multiple browsers using popular automation libraries such as Playwright or Puppeteer.",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=22.0.0"
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@apify/timeout": "^1.0.1",
|
|
34
|
-
"@crawlee/core": "4.0.0-beta.
|
|
35
|
-
"@crawlee/types": "4.0.0-beta.
|
|
36
|
-
"@crawlee/utils": "4.0.0-beta.
|
|
34
|
+
"@crawlee/core": "4.0.0-beta.212",
|
|
35
|
+
"@crawlee/types": "4.0.0-beta.212",
|
|
36
|
+
"@crawlee/utils": "4.0.0-beta.212",
|
|
37
37
|
"fingerprint-generator": "^2.1.68",
|
|
38
38
|
"fingerprint-injector": "^2.1.68",
|
|
39
39
|
"lodash.merge": "^4.6.2",
|
|
@@ -64,5 +64,5 @@
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
},
|
|
67
|
-
"gitHead": "
|
|
67
|
+
"gitHead": "171a63a5d7f2a6d74cbce58479204ac20d30b87e"
|
|
68
68
|
}
|
|
@@ -15,7 +15,6 @@ export declare class PlaywrightPlugin extends BrowserPlugin<BrowserType, SafePar
|
|
|
15
15
|
*/
|
|
16
16
|
useRemoteConnection(connection: RemoteConnection, parameters?: RemoteConnectionParameters): void;
|
|
17
17
|
protected _launch(launchContext: LaunchContext<BrowserType>): Promise<PlaywrightBrowser>;
|
|
18
|
-
private throwOnFailedLaunch;
|
|
19
18
|
createController(): PlaywrightController;
|
|
20
19
|
protected addProxyToLaunchOptions(launchContext: LaunchContext<BrowserType>): Promise<void>;
|
|
21
20
|
protected isChromiumBasedBrowser(): boolean;
|
|
@@ -55,7 +55,7 @@ export class PlaywrightPlugin extends BrowserPlugin {
|
|
|
55
55
|
try {
|
|
56
56
|
if (useIncognitoPages) {
|
|
57
57
|
browser = await this.library.launch(launchOptions).catch((error) => {
|
|
58
|
-
return this
|
|
58
|
+
return this.#throwOnFailedLaunch(launchContext, error);
|
|
59
59
|
});
|
|
60
60
|
if (anonymizedProxyUrl) {
|
|
61
61
|
browser.on('disconnected', async () => {
|
|
@@ -67,7 +67,7 @@ export class PlaywrightPlugin extends BrowserPlugin {
|
|
|
67
67
|
const browserContext = await this.library
|
|
68
68
|
.launchPersistentContext(userDataDir, launchOptions)
|
|
69
69
|
.catch((error) => {
|
|
70
|
-
return this
|
|
70
|
+
return this.#throwOnFailedLaunch(launchContext, error);
|
|
71
71
|
});
|
|
72
72
|
browserContext.once('close', () => {
|
|
73
73
|
if (userDataDir.includes('apify-playwright-firefox-taac-')) {
|
|
@@ -104,7 +104,7 @@ export class PlaywrightPlugin extends BrowserPlugin {
|
|
|
104
104
|
}
|
|
105
105
|
return browser;
|
|
106
106
|
}
|
|
107
|
-
throwOnFailedLaunch(launchContext, cause) {
|
|
107
|
+
#throwOnFailedLaunch(launchContext, cause) {
|
|
108
108
|
this.throwAugmentedLaunchError(cause, launchContext.launchOptions?.executablePath, '`apify/actor-node-playwright-*` (with a correct browser name)', 'Try installing the required dependencies by running `npx playwright install --with-deps` (https://playwright.dev/docs/browsers).');
|
|
109
109
|
}
|
|
110
110
|
createController() {
|
package/remote-browser-pool.d.ts
CHANGED
|
@@ -167,12 +167,4 @@ export declare class RemoteBrowserPool<Page = unknown> implements IBrowserPool<P
|
|
|
167
167
|
[Symbol.asyncDispose](): Promise<void>;
|
|
168
168
|
/** Closes all browsers, releases any still-open remote sessions, and tears down the wrapped pool. */
|
|
169
169
|
destroy(): Promise<void>;
|
|
170
|
-
/** Resolves once the wrapped pool can serve another page without exceeding `maxOpenBrowsers`. */
|
|
171
|
-
private waitForFreeSlot;
|
|
172
|
-
/**
|
|
173
|
-
* Resolves on the next browser-retired / page-closed event, or after `slotPollIntervalMillis`. All
|
|
174
|
-
* concurrently-waiting `newPage` calls share a single promise (and a single pair of event listeners)
|
|
175
|
-
* per tick, so a fleet of saturated callers doesn't fan out into N listener pairs on the pool.
|
|
176
|
-
*/
|
|
177
|
-
private nextCapacityChange;
|
|
178
170
|
}
|
package/remote-browser-pool.js
CHANGED
|
@@ -148,7 +148,7 @@ export class RemoteBrowserPool {
|
|
|
148
148
|
* allows it (either a new browser slot is free, or an active browser still has page capacity).
|
|
149
149
|
*/
|
|
150
150
|
async newPage(options) {
|
|
151
|
-
await this
|
|
151
|
+
await this.#waitForFreeSlot();
|
|
152
152
|
return this.#pool.newPage(options);
|
|
153
153
|
}
|
|
154
154
|
async closePage(page, options) {
|
|
@@ -177,9 +177,9 @@ export class RemoteBrowserPool {
|
|
|
177
177
|
await this.#registry.releaseAll();
|
|
178
178
|
}
|
|
179
179
|
/** Resolves once the wrapped pool can serve another page without exceeding `maxOpenBrowsers`. */
|
|
180
|
-
async waitForFreeSlot() {
|
|
180
|
+
async #waitForFreeSlot() {
|
|
181
181
|
while (!this.browserPool.hasFreeBrowserSlot() && !this.browserPool.hasActiveBrowserWithFreeCapacity()) {
|
|
182
|
-
await this
|
|
182
|
+
await this.#nextCapacityChange();
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
185
|
/**
|
|
@@ -187,7 +187,7 @@ export class RemoteBrowserPool {
|
|
|
187
187
|
* concurrently-waiting `newPage` calls share a single promise (and a single pair of event listeners)
|
|
188
188
|
* per tick, so a fleet of saturated callers doesn't fan out into N listener pairs on the pool.
|
|
189
189
|
*/
|
|
190
|
-
nextCapacityChange() {
|
|
190
|
+
#nextCapacityChange() {
|
|
191
191
|
this.#capacityChange ??= new Promise((resolve) => {
|
|
192
192
|
const done = () => {
|
|
193
193
|
clearTimeout(timer);
|