arn-browser 0.1.12 → 0.1.13
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/package.json +2 -2
- package/src/index.d.ts +3 -1
- package/src/index.js +3 -1
- package/src/utility/playwright/human-cursor/HumanCursor.js +2 -2
- package/src/utility/playwright/human-cursor/index.d.ts +1 -1
- package/src/utility/playwright/{playwright-helper.d.ts → pwHelper.d.ts} +1 -1
- package/src/utility/playwright/{playwright-helper.js → pwHelper.js} +4 -4
- package/src/utility/playwright/pwLaunch.d.ts +1 -1
- package/src/utility/playwright/pwLaunch.js +33 -1
- package/src/utility/playwright/routes/pwRoute.d.ts +1 -1
- package/src/utility/puppeteer/ppHelper.d.ts +26 -0
- package/src/utility/puppeteer/ppHelper.js +48 -0
- package/src/utility/puppeteer/ppLaunch.js +32 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arn-browser",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"description": "A lightweight, browser autmation helper.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"https-proxy-agent": "^7.0.6",
|
|
23
23
|
"node-cache": "^5.1.2",
|
|
24
24
|
"node-fetch": "^3.3.2",
|
|
25
|
-
"playwright": "
|
|
25
|
+
"playwright-core": "1.42.1",
|
|
26
26
|
"proxy-chain": "^2.6.0",
|
|
27
27
|
"puppeteer-core": "^24.38.0",
|
|
28
28
|
"randomstring": "^1.3.1",
|
package/src/index.d.ts
CHANGED
|
@@ -4,7 +4,8 @@ import { ppLaunch } from "./utility/puppeteer/ppLaunch";
|
|
|
4
4
|
import { ppRoute, ppCacheLogs } from "./utility/puppeteer/routes/ppRoute";
|
|
5
5
|
import { pwLaunch } from "./utility/playwright/pwLaunch";
|
|
6
6
|
import { pwRoute, pwCacheLogs } from "./utility/playwright/routes/pwRoute";
|
|
7
|
-
import { retryNavigation, retryClick, checkPageConditions } from "./utility/playwright/
|
|
7
|
+
import { retryNavigation, retryClick, checkPageConditions } from "./utility/playwright/pwHelper";
|
|
8
|
+
import { retryNavigation as ppRetryNavigation } from "./utility/puppeteer/ppHelper";
|
|
8
9
|
import { startProxyServer, fetchPublicIP, fetchProxyDetails } from "./utility/proxy-utility/proxy-chain";
|
|
9
10
|
import {
|
|
10
11
|
fetchAwsProxy, getInstanceStatus, getPublicIpAddress,
|
|
@@ -20,6 +21,7 @@ export declare const ppBrowser: {
|
|
|
20
21
|
launch: typeof ppLaunch;
|
|
21
22
|
route: typeof ppRoute;
|
|
22
23
|
cacheLogs: typeof ppCacheLogs;
|
|
24
|
+
ppGoto: typeof ppRetryNavigation;
|
|
23
25
|
};
|
|
24
26
|
|
|
25
27
|
export declare const pwBrowser: {
|
package/src/index.js
CHANGED
|
@@ -5,17 +5,19 @@
|
|
|
5
5
|
// --- Puppeteer ---
|
|
6
6
|
import { ppLaunch } from "./utility/puppeteer/ppLaunch.js";
|
|
7
7
|
import { ppRoute, ppCacheLogs } from "./utility/puppeteer/routes/ppRoute.js";
|
|
8
|
+
import { retryNavigation as ppRetryNavigation } from "./utility/puppeteer/ppHelper.js";
|
|
8
9
|
|
|
9
10
|
export const ppBrowser = {
|
|
10
11
|
launch: ppLaunch,
|
|
11
12
|
route: ppRoute,
|
|
12
13
|
cacheLogs: ppCacheLogs,
|
|
14
|
+
ppGoto: ppRetryNavigation,
|
|
13
15
|
};
|
|
14
16
|
|
|
15
17
|
// --- Playwright ---
|
|
16
18
|
import { pwLaunch } from "./utility/playwright/pwLaunch.js";
|
|
17
19
|
import { pwRoute, pwCacheLogs } from "./utility/playwright/routes/pwRoute.js";
|
|
18
|
-
import { retryNavigation, retryClick, checkPageConditions } from "./utility/playwright/
|
|
20
|
+
import { retryNavigation, retryClick, checkPageConditions } from "./utility/playwright/pwHelper.js";
|
|
19
21
|
|
|
20
22
|
export const pwBrowser = {
|
|
21
23
|
launch: pwLaunch,
|
|
@@ -273,9 +273,9 @@ function createHumanLocator(cursor, locator) {
|
|
|
273
273
|
* Create a HumanCursor that acts as a drop-in replacement for Page
|
|
274
274
|
* All page methods work, but click/fill/type/check/hover use human cursor
|
|
275
275
|
*
|
|
276
|
-
* @param {import('playwright').Page} page - Playwright Page object
|
|
276
|
+
* @param {import('playwright-core').Page} page - Playwright Page object
|
|
277
277
|
* @param {Object} options - Configuration options
|
|
278
|
-
* @returns {import('playwright').Page & HumanCursorMethods}
|
|
278
|
+
* @returns {import('playwright-core').Page & HumanCursorMethods}
|
|
279
279
|
*/
|
|
280
280
|
export function createCursor(page, options = {}) {
|
|
281
281
|
const config = {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* TypeScript declarations for human-cursor-playwright
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import type { Page, Locator, Response, PageScreenshotOptions, WaitForURLOptions } from 'playwright';
|
|
5
|
+
import type { Page, Locator, Response, PageScreenshotOptions, WaitForURLOptions } from 'playwright-core';
|
|
6
6
|
|
|
7
7
|
// ============= Tweening Functions =============
|
|
8
8
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* If navigation fails, it temporarily goes to "about:blank" before retrying.
|
|
4
4
|
*
|
|
5
5
|
* @param {Object} options
|
|
6
|
-
* @param {import('playwright').Page} options.page - Playwright Page object
|
|
6
|
+
* @param {import('playwright-core').Page} options.page - Playwright Page object
|
|
7
7
|
* @param {string} options.url - Target URL
|
|
8
8
|
* @param {number} [options.maxRetries=5] - Maximum number of attempts
|
|
9
9
|
* @param {string|null} [options.referer=null] - Referer header
|
|
@@ -54,7 +54,7 @@ export const retryNavigation = async ({
|
|
|
54
54
|
* EXPECTATION: The element should disappear (become hidden) after clicking.
|
|
55
55
|
*
|
|
56
56
|
* @param {Object} options
|
|
57
|
-
* @param {import('playwright').Locator} options.locator - The element to click
|
|
57
|
+
* @param {import('playwright-core').Locator} options.locator - The element to click
|
|
58
58
|
* @param {number} [options.maxRetries=3] - Max retries
|
|
59
59
|
* @param {number} [options.timeout=15000] - Timeout for visibility checks
|
|
60
60
|
*/
|
|
@@ -80,8 +80,8 @@ export const retryClick = async ({ locator, maxRetries = 3, timeout = 15000 }) =
|
|
|
80
80
|
* Races multiple conditions (URL or Element) to see which happens first.
|
|
81
81
|
* Modifies the input object by deleting the matched key.
|
|
82
82
|
*
|
|
83
|
-
* @param {import('playwright').Page} page
|
|
84
|
-
* @param {Object.<string, string|import('playwright').Locator|null>} checksToPerform - Map of names to URL strings or Locators
|
|
83
|
+
* @param {import('playwright-core').Page} page
|
|
84
|
+
* @param {Object.<string, string|import('playwright-core').Locator|null>} checksToPerform - Map of names to URL strings or Locators
|
|
85
85
|
* @param {number} timeout - Timeout in ms
|
|
86
86
|
* @returns {Promise<string|null>} The key of the matched condition
|
|
87
87
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Browser, BrowserContext, Page } from "playwright";
|
|
1
|
+
import { Browser, BrowserContext, Page } from "playwright-core";
|
|
2
2
|
import type { FingerprintGeneratorOptions } from "fingerprint-generator";
|
|
3
3
|
import type { HumanPage, CreateCursorOptions } from "./human-cursor/index";
|
|
4
4
|
type Screen = FingerprintGeneratorOptions["screen"];
|
|
@@ -9,7 +9,7 @@ import fs from "fs";
|
|
|
9
9
|
import path from "path";
|
|
10
10
|
import os from "os";
|
|
11
11
|
import crypto from "node:crypto";
|
|
12
|
-
import { chromium, firefox } from "playwright";
|
|
12
|
+
import { chromium, firefox } from "playwright-core";
|
|
13
13
|
import { setTimeout as sleep } from "timers/promises";
|
|
14
14
|
|
|
15
15
|
// Fingerprint management
|
|
@@ -966,11 +966,27 @@ async function launchExistingMultiloginProfile(profileId, humanize_options = nul
|
|
|
966
966
|
page = createCursor(page, humanize_options);
|
|
967
967
|
}
|
|
968
968
|
|
|
969
|
+
let closing = false;
|
|
969
970
|
const closeBrowser = async () => {
|
|
971
|
+
if (closing) return false;
|
|
972
|
+
closing = true;
|
|
973
|
+
if (signalHandler) {
|
|
974
|
+
process.off("SIGINT", signalHandler);
|
|
975
|
+
process.off("SIGTERM", signalHandler);
|
|
976
|
+
process.off("SIGHUP", signalHandler);
|
|
977
|
+
}
|
|
970
978
|
if (browser) await browser.close().catch(() => { });
|
|
971
979
|
return await stopMultiloginProfile(profileId);
|
|
972
980
|
};
|
|
973
981
|
|
|
982
|
+
let signalHandler = async () => {
|
|
983
|
+
await closeBrowser();
|
|
984
|
+
process.exit(0);
|
|
985
|
+
};
|
|
986
|
+
process.once("SIGINT", signalHandler);
|
|
987
|
+
process.once("SIGTERM", signalHandler);
|
|
988
|
+
process.once("SIGHUP", signalHandler);
|
|
989
|
+
|
|
974
990
|
return { browser, context, page, isBrowserRunning: () => !!browser, closeBrowser, launchError: null };
|
|
975
991
|
} catch (error) {
|
|
976
992
|
console.error("Multilogin Launch Error:", error.message);
|
|
@@ -1040,11 +1056,27 @@ async function launchQuickMultiloginProfile({ os_type, proxy, canvas_noise, medi
|
|
|
1040
1056
|
page = createCursor(page, humanize_options);
|
|
1041
1057
|
}
|
|
1042
1058
|
|
|
1059
|
+
let closing = false;
|
|
1043
1060
|
const closeBrowser = async () => {
|
|
1061
|
+
if (closing) return false;
|
|
1062
|
+
closing = true;
|
|
1063
|
+
if (signalHandler) {
|
|
1064
|
+
process.off("SIGINT", signalHandler);
|
|
1065
|
+
process.off("SIGTERM", signalHandler);
|
|
1066
|
+
process.off("SIGHUP", signalHandler);
|
|
1067
|
+
}
|
|
1044
1068
|
if (browser) await browser.close().catch(() => { });
|
|
1045
1069
|
return await stopMultiloginProfile(profileId);
|
|
1046
1070
|
};
|
|
1047
1071
|
|
|
1072
|
+
let signalHandler = async () => {
|
|
1073
|
+
await closeBrowser();
|
|
1074
|
+
process.exit(0);
|
|
1075
|
+
};
|
|
1076
|
+
process.once("SIGINT", signalHandler);
|
|
1077
|
+
process.once("SIGTERM", signalHandler);
|
|
1078
|
+
process.once("SIGHUP", signalHandler);
|
|
1079
|
+
|
|
1048
1080
|
return { browser, context, page, isBrowserRunning: () => !!browser, closeBrowser, launchError: null };
|
|
1049
1081
|
} catch (error) {
|
|
1050
1082
|
console.error("Quick Profile Error:", error);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Page } from "puppeteer-core";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Options for the retryNavigation function.
|
|
5
|
+
*/
|
|
6
|
+
export interface RetryNavigationOptions {
|
|
7
|
+
/** The Puppeteer Page object. */
|
|
8
|
+
page: Page;
|
|
9
|
+
/** The target URL to navigate to. */
|
|
10
|
+
url: string;
|
|
11
|
+
/** Maximum number of retry attempts. Default: 5 */
|
|
12
|
+
maxRetries?: number;
|
|
13
|
+
/** Custom Referer header. Default: null */
|
|
14
|
+
referer?: string | null;
|
|
15
|
+
/** Base timeout in milliseconds. Default: 30000 */
|
|
16
|
+
timeout?: number;
|
|
17
|
+
/** When to consider operation succeeded. Default: "load" */
|
|
18
|
+
waitUntil?: "load" | "domcontentloaded" | "networkidle0" | "networkidle2";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Navigates to a URL with retry logic and incremental timeouts.
|
|
23
|
+
* If navigation fails, it temporarily goes to "about:blank" before retrying.
|
|
24
|
+
* @returns True if navigation succeeded, throws error otherwise.
|
|
25
|
+
*/
|
|
26
|
+
export function retryNavigation(options: RetryNavigationOptions): Promise<boolean>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navigates to a URL with retry logic and incremental timeouts.
|
|
3
|
+
* If navigation fails, it temporarily goes to "about:blank" before retrying.
|
|
4
|
+
*
|
|
5
|
+
* @param {Object} options
|
|
6
|
+
* @param {import('puppeteer-core').Page} options.page - Puppeteer Page object
|
|
7
|
+
* @param {string} options.url - Target URL
|
|
8
|
+
* @param {number} [options.maxRetries=5] - Maximum number of attempts
|
|
9
|
+
* @param {string|null} [options.referer=null] - Referer header
|
|
10
|
+
* @param {number} [options.timeout=30000] - Base timeout in ms
|
|
11
|
+
* @param {"load"|"domcontentloaded"|"networkidle0"|"networkidle2"} [options.waitUntil="load"] - Wait condition
|
|
12
|
+
* @returns {Promise<boolean>} True if successful
|
|
13
|
+
*/
|
|
14
|
+
export const retryNavigation = async ({
|
|
15
|
+
page,
|
|
16
|
+
url,
|
|
17
|
+
maxRetries = 5,
|
|
18
|
+
referer = null,
|
|
19
|
+
timeout = 30000,
|
|
20
|
+
waitUntil = "load",
|
|
21
|
+
}) => {
|
|
22
|
+
for (let retryCount = 0; retryCount < maxRetries; retryCount++) {
|
|
23
|
+
// Apply incremental timeout only if the initial timeout is <= 30,000
|
|
24
|
+
const currentTimeout = timeout <= 30000 ? timeout + retryCount * 15000 : timeout;
|
|
25
|
+
try {
|
|
26
|
+
const gotoOptions = {
|
|
27
|
+
waitUntil: waitUntil,
|
|
28
|
+
timeout: currentTimeout,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
if (referer) {
|
|
32
|
+
gotoOptions.referer = referer;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
await page.goto(url, gotoOptions);
|
|
36
|
+
|
|
37
|
+
return true;
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.log(`Navigation attempt ${retryCount + 1} failed.`, url);
|
|
40
|
+
await page.goto("about:blank", { waitUntil: "load", timeout: timeout });
|
|
41
|
+
if (retryCount === maxRetries - 1) {
|
|
42
|
+
console.log("All retry attempts failed");
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
};
|
|
@@ -612,13 +612,29 @@ async function launchExistingMultiloginProfile(profileId) {
|
|
|
612
612
|
const pages = await browser.pages();
|
|
613
613
|
const page = pages[0] ?? (await browser.newPage());
|
|
614
614
|
|
|
615
|
+
let closing = false;
|
|
615
616
|
const closeBrowser = async () => {
|
|
617
|
+
if (closing) return false;
|
|
618
|
+
closing = true;
|
|
619
|
+
if (signalHandler) {
|
|
620
|
+
process.off("SIGINT", signalHandler);
|
|
621
|
+
process.off("SIGTERM", signalHandler);
|
|
622
|
+
process.off("SIGHUP", signalHandler);
|
|
623
|
+
}
|
|
616
624
|
try {
|
|
617
625
|
if (browser?.connected) await browser.close();
|
|
618
626
|
} catch { }
|
|
619
627
|
return await stopMultiloginProfile(profileId);
|
|
620
628
|
};
|
|
621
629
|
|
|
630
|
+
let signalHandler = async () => {
|
|
631
|
+
await closeBrowser();
|
|
632
|
+
process.exit(0);
|
|
633
|
+
};
|
|
634
|
+
process.once("SIGINT", signalHandler);
|
|
635
|
+
process.once("SIGTERM", signalHandler);
|
|
636
|
+
process.once("SIGHUP", signalHandler);
|
|
637
|
+
|
|
622
638
|
return { browser, context: browser, page, isBrowserRunning: () => !!browser?.connected, closeBrowser, launchError: null };
|
|
623
639
|
} catch (error) {
|
|
624
640
|
console.error("Multilogin Launch Error:", error.message);
|
|
@@ -685,13 +701,29 @@ async function launchQuickMultiloginProfile({ os_type, proxy, canvas_noise, medi
|
|
|
685
701
|
const pages = await browser.pages();
|
|
686
702
|
const page = pages[0] ?? (await browser.newPage());
|
|
687
703
|
|
|
704
|
+
let closing = false;
|
|
688
705
|
const closeBrowser = async () => {
|
|
706
|
+
if (closing) return false;
|
|
707
|
+
closing = true;
|
|
708
|
+
if (signalHandler) {
|
|
709
|
+
process.off("SIGINT", signalHandler);
|
|
710
|
+
process.off("SIGTERM", signalHandler);
|
|
711
|
+
process.off("SIGHUP", signalHandler);
|
|
712
|
+
}
|
|
689
713
|
try {
|
|
690
714
|
if (browser?.connected) await browser.close();
|
|
691
715
|
} catch { }
|
|
692
716
|
return await stopMultiloginProfile(profileId);
|
|
693
717
|
};
|
|
694
718
|
|
|
719
|
+
let signalHandler = async () => {
|
|
720
|
+
await closeBrowser();
|
|
721
|
+
process.exit(0);
|
|
722
|
+
};
|
|
723
|
+
process.once("SIGINT", signalHandler);
|
|
724
|
+
process.once("SIGTERM", signalHandler);
|
|
725
|
+
process.once("SIGHUP", signalHandler);
|
|
726
|
+
|
|
695
727
|
return { browser, context: browser, page, isBrowserRunning: () => !!browser?.connected, closeBrowser, launchError: null };
|
|
696
728
|
} catch (error) {
|
|
697
729
|
console.error("Quick Profile Error:", error);
|