├── .github └── ISSUE_TEMPLATE │ └── general_issue.yaml ├── .gitignore ├── Dockerfile ├── LICENSE.md ├── README.md ├── data ├── capsolver.png └── sdo.gif ├── lib ├── cjs │ ├── index.js │ └── module │ │ ├── pageController.js │ │ └── turnstile.js └── esm │ ├── index.mjs │ └── module │ ├── pageController.mjs │ └── turnstile.mjs ├── package-lock.json ├── package.json ├── test ├── cjs │ └── test.js └── esm │ ├── package.json │ └── test.js └── typings.d.ts /.github/ISSUE_TEMPLATE/general_issue.yaml: -------------------------------------------------------------------------------- 1 | name: Report Issue 2 | description: Please use this to report any issue 3 | labels: [triage] 4 | assignees: 5 | - zfcsoftware 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Please take care to fill in all fields. Recreating the issue will speed up its resolution. Thank you for contributing to the betterment of the library by reporting issues. 11 | - type: textarea 12 | id: issue-detail 13 | attributes: 14 | label: Description 15 | description: Please describe the problem you are experiencing. You only need to provide information about the problem in this field. 16 | validations: 17 | required: true 18 | - type: textarea 19 | id: issue-recreate 20 | attributes: 21 | label: Full steps to reproduce the issue 22 | description: Please provide a full working code to reproduce the issue. Make sure that the code you provide is directly executable. This step is very important to resolve the issue. 23 | validations: 24 | required: true 25 | - type: dropdown 26 | id: issue-type 27 | attributes: 28 | label: Issue Type 29 | description: What type of issue would you like to report? 30 | multiple: true 31 | options: 32 | - Bug 33 | - Build/Install 34 | - Performance 35 | - Support 36 | - Feature Request 37 | - Documentation Request 38 | - Others 39 | - type: dropdown 40 | id: Operating-System 41 | attributes: 42 | label: Operating System 43 | description: What OS are you seeing the issue in? If you don't see your OS listed, please provide more details in the "Description" section above. 44 | multiple: true 45 | options: 46 | - Windows 10 47 | - Linux 48 | - Mac OS 49 | - Other 50 | - type: dropdown 51 | id: use-type 52 | attributes: 53 | label: Do you use Docker? 54 | description: Are you running it with Docker or on your local computer? 55 | multiple: false 56 | options: 57 | - Docker 58 | - I don't use Docker 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:latest 2 | 3 | RUN apt-get update && apt-get install -y \ 4 | wget \ 5 | gnupg \ 6 | ca-certificates \ 7 | apt-transport-https \ 8 | chromium \ 9 | chromium-driver \ 10 | xvfb \ 11 | && rm -rf /var/lib/apt/lists/* 12 | 13 | ENV CHROME_BIN=/usr/bin/chromium 14 | 15 | WORKDIR /app 16 | 17 | COPY package*.json ./ 18 | 19 | RUN npm update 20 | RUN npm install 21 | COPY . . 22 | 23 | # Command to run the application 24 | CMD ["npm", "run","cjs_test"] -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 - 2024 @zfcsoftware 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > [!WARNING] 2 | > This repo will no longer receive updates. Thank you to everyone who supported it. 3 | 4 |
5 |

6 | 7 | Logo 8 | 9 | 10 |

Puppeteer Real Browser

11 | 12 |

13 | This package prevents Puppeteer from being detected as a bot in services like Cloudflare and allows you to pass captchas without any problems. It behaves like a real browser. 14 |
15 |
16 | If you are only interested in Cloudflare WAF, please check this repo:
https://github.com/zfcsoftware/cf-clearance-scraper 17 |

18 |

19 | 20 |

21 |

23 | 24 |

25 | Contributors 26 | Forks 27 | Stargazers 28 | Issues 29 | License 30 |

31 | 32 | ## Sponsor 33 | 34 | [![Capsolver](data/capsolver.png)](https://www.capsolver.com/?utm_source=github&utm_medium=repo&utm_campaign=scraping&utm_term=puppeteer-real-browser) 35 | 36 | [![ScrapeDo](data/sdo.gif)](https://scrape.do/?utm_source=github&utm_medium=repo_prb) 37 | 38 | ## Installation 39 | 40 | If you are using a Linux operating system, xvfb must be installed for the library to work correctly. 41 | 42 | ```bash 43 | npm i puppeteer-real-browser 44 | ``` 45 | 46 | if you are using linux: 47 | 48 | ```bash 49 | sudo apt-get install xvfb 50 | ``` 51 | 52 | ## Include 53 | 54 | ### CommonJS 55 | 56 | ```js 57 | const { connect } = require("puppeteer-real-browser"); 58 | 59 | const start = async () => { 60 | const { page, browser } = await connect(); 61 | }; 62 | ``` 63 | 64 | ### Module 65 | 66 | ```js 67 | import { connect } from "puppeteer-real-browser"; 68 | 69 | const { page, browser } = await connect(); 70 | ``` 71 | 72 | ## Usage 73 | 74 | ```js 75 | const { connect } = require("puppeteer-real-browser"); 76 | 77 | async function test() { 78 | const { browser, page } = await connect({ 79 | headless: false, 80 | 81 | args: [], 82 | 83 | customConfig: {}, 84 | 85 | turnstile: true, 86 | 87 | connectOption: {}, 88 | 89 | disableXvfb: false, 90 | ignoreAllFlags: false, 91 | // proxy:{ 92 | // host:'', 93 | // port:'', 94 | // username:'', 95 | // password:'' 96 | // } 97 | }); 98 | await page.goto(""); 99 | } 100 | 101 | test(); 102 | ``` 103 | 104 | **headless**: The default value is false. Values such as “new”, true, “shell” can also be sent, but it works most stable when false is used. 105 | 106 | **args:** If there is an additional flag you want to add when starting Chromium, you can send it with this string. 107 | Supported flags: https://github.com/GoogleChrome/chrome-launcher/blob/main/docs/chrome-flags-for-tools.md 108 | 109 | **customConfig:** https://github.com/GoogleChrome/chrome-launcher The browser is initialized with this library. What you send with this object is added as a direct initialization argument. You should use the initialization values in this repo. You should set the userDataDir option here and if you want to specify a custom chrome path, you should set it with the chromePath value. 110 | 111 | **turnstile:** Cloudflare Turnstile automatically clicks on Captchas if set to true 112 | 113 | **connectOption:** The variables you send when connecting to chromium created with puppeteer.connect are added 114 | 115 | **disableXvfb:** In Linux, when headless is false, a virtual screen is created and the browser is run there. You can set this value to true if you want to see the browser. 116 | 117 | **ignoreAllFlags** If true, all initialization arguments are overridden. This includes the let's get started page that appears on the first load. 118 | 119 | ## How to Install Puppeteer-extra Plugins? 120 | 121 | Some plugins, such as puppeteer-extra-plugin-anonymize-ua, may cause you to be detected. You can use the plugin installation test in the library's test file to see if it will cause you to be detected. 122 | 123 | The following is an example of installing a plugin. You can install other plugins in the same way as this example. 124 | 125 | ```bash 126 | npm i puppeteer-extra-plugin-click-and-wait 127 | ``` 128 | 129 | ```js 130 | const test = require("node:test"); 131 | const assert = require("node:assert"); 132 | const { connect } = require("puppeteer-real-browser"); 133 | 134 | test("Puppeteer Extra Plugin", async () => { 135 | const { page, browser } = await connect({ 136 | args: ["--start-maximized"], 137 | turnstile: true, 138 | headless: false, 139 | // disableXvfb: true, 140 | customConfig: {}, 141 | connectOption: { 142 | defaultViewport: null, 143 | }, 144 | plugins: [require("puppeteer-extra-plugin-click-and-wait")()], 145 | }); 146 | await page.goto("https://google.com", { waitUntil: "domcontentloaded" }); 147 | await page.clickAndWaitForNavigation("body"); 148 | await browser.close(); 149 | }); 150 | ``` 151 | 152 | ## Docker 153 | 154 | You can use the Dockerfile file in the main directory to use this library with docker. It has been tested with docker on Ubuntu server operating systems. 155 | 156 | To run a test, you can follow these steps 157 | 158 | ```bash 159 | git clone https://github.com/zfcsoftware/puppeteer-real-browser 160 | ``` 161 | 162 | ```bash 163 | cd puppeteer-real-browser 164 | ``` 165 | 166 | ```bash 167 | docker build -t puppeteer-real-browser-project . 168 | ``` 169 | 170 | ```bash 171 | docker run puppeteer-real-browser-project 172 | ``` 173 | 174 | ## Support Us 175 | 176 | This library is completely open source and is constantly being updated. Please star this repo to support this project. Starring and supporting the project will ensure that it receives updates. If you want to support it further, you can consider sponsoring me (https://github.com/sponsors/zfcsoftware) 177 | 178 | ## Quick Questions and Answers 179 | 180 | ### I Cannot Access Functions in Window Object What Should I Do? 181 | 182 | This problem is probably caused by the runtime being closed by the rebrowser used. 183 | https://github.com/zfcsoftware/puppeteer-real-browser/tree/access-window 184 | I created a branch for this. You can access the value you want by adding javascript to the page source with puppeteer-intercept-and-modify-requests as done in success.js. If you know about the Chrome plugin, you can also use it. 185 | 186 | ### page.setViewport method is not working, what should I do? 187 | 188 | As with the initialization arguments in the test module, you can set the defaultViewport in connectOption. If you set null, it will take up as much space as the width of the Browser. 189 | 190 | ### Does the library have any known detection problems? 191 | 192 | using puppeteer-core patched with rebrowser. Tested with the challenging sites in the test file in headless false mode and passed with flying colors. The only known issue is that the mouse screeenX does not match the mouse position. This has been patched in the library. 193 | 194 | The ghost-cursor is included in the library. (https://github.com/zfcsoftware/puppeteer-real-browser/blob/2a5fba37a85c15625fb3c8d1f7cf8dcb109b9492/lib/cjs/module/pageController.js#L54) You can use ghost-cursor with page.realCursor. page.click It is recommended to use page.realClick instead of page.click. 195 | 196 | ### What Makes This Library Special? 197 | 198 | This library lets you launch and use Chrome in its most natural state. It tries to get the best results with minimal patching. Thanks to @nwebson who fixed the Runtime.enable issue from this point. If using rebrowser solves your problem, I don't recommend using real browser. 199 | 200 | Real browser does not give you full control over launching. It launches Chrome with Chrome launcher and connects to it with rebrowser. 201 | 202 | ### Why can't I pass Recaptcha v3? 203 | 204 | https://stackoverflow.com/questions/52546045/how-to-pass-recaptcha-v3 205 | 206 | Please see the answers in the link above. When there is no Google session, no matter how good your browser is, recaptcha identifies you as a bot. It is a common problem. 207 | 208 | ## License 209 | 210 | Distributed under the MIT License. See [LICENSE](https://github.com/zfcsoftware/puppeteer-real-browser/blob/main/LICENSE.md) for more information. 211 | 212 | ## Thank You 213 | 214 | **Contributions to the current version** 215 | 216 | - **rebrowser™** - [rebrowser™](https://github.com/rebrowser) - _Created a patch pack for Runtime, which left many traces behind. Since Runtime was not used, most problems were solved. TargetFilter, which was used in the past and caused many problems, was switched to this patch. The Puppeteer-core library was patched and added to this repo. A lot of good bot detection systems are not caught thanks to rebrowser. Please star the rebrowser repo. Thank you. (https://github.com/rebrowser/rebrowser-patches)_ 217 | 218 | - **Skill Issue™** - [TheFalloutOf76](https://github.com/TheFalloutOf76) - _He realized that mouse movements could not be simulated accurately and created a solution for this. His solution is used in this library. (https://github.com/TheFalloutOf76/CDP-bug-MouseEvent-.screenX-.screenY-patcher)_ 219 | 220 | ## Disclaimer of Liability 221 | 222 | No responsibility is accepted for the use of this software. This software is intended for educational and informational purposes only. Users should use this software at their own risk. The developer cannot be held liable for any damages that may result from the use of this software. 223 | 224 | This software is not intended to bypass Cloudflare Captcha or any other security measure. It must not be used for malicious purposes. Malicious use may result in legal consequences. 225 | 226 | This software is not officially endorsed or guaranteed. Users can visit the GitHub page to report bugs or contribute to the software, but they are not entitled to make any claims or request service fixes. 227 | 228 | By using this software, you agree to this disclaimer. 229 | -------------------------------------------------------------------------------- /data/capsolver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZFC-Digital/puppeteer-real-browser/4550aeabfed280ac1977d2768a6aa65bde69c367/data/capsolver.png -------------------------------------------------------------------------------- /data/sdo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZFC-Digital/puppeteer-real-browser/4550aeabfed280ac1977d2768a6aa65bde69c367/data/sdo.gif -------------------------------------------------------------------------------- /lib/cjs/index.js: -------------------------------------------------------------------------------- 1 | let puppeteer = require("rebrowser-puppeteer-core"); 2 | const { pageController } = require("./module/pageController.js"); 3 | let Xvfb 4 | try { 5 | Xvfb = require("xvfb"); 6 | } catch { 7 | // ignore 8 | } 9 | 10 | async function connect({ 11 | args = [], 12 | headless = false, 13 | customConfig = {}, 14 | proxy = {}, 15 | turnstile = false, 16 | connectOption = {}, 17 | disableXvfb = false, 18 | plugins = [], 19 | ignoreAllFlags = false, 20 | } = {}) { 21 | const { launch, Launcher } = await import("chrome-launcher"); 22 | 23 | let xvfbsession = null; 24 | if (headless == "auto") headless = false; 25 | 26 | if (process.platform === "linux" && disableXvfb === false) { 27 | try { 28 | xvfbsession = new Xvfb({ 29 | silent: true, 30 | xvfb_args: ["-screen", "0", "1920x1080x24", "-ac"], 31 | }); 32 | xvfbsession.startSync(); 33 | } catch (err) { 34 | console.log( 35 | "You are running on a Linux platform but do not have xvfb installed. The browser can be captured. Please install it with the following command\n\nsudo apt-get install xvfb\n\n" + 36 | err.message 37 | ); 38 | } 39 | } 40 | 41 | let chromeFlags; 42 | if (ignoreAllFlags === true) { 43 | chromeFlags = [ 44 | ...args, 45 | ...(headless !== false ? [`--headless=${headless}`] : []), 46 | ...(proxy && proxy.host && proxy.port 47 | ? [`--proxy-server=${proxy.host}:${proxy.port}`] 48 | : []), 49 | ]; 50 | } else { 51 | // Default flags: https://github.com/GoogleChrome/chrome-launcher/blob/main/src/flags.ts 52 | const flags = Launcher.defaultFlags(); 53 | // Add AutomationControlled to "disable-features" flag 54 | const indexDisableFeatures = flags.findIndex((flag) => flag.startsWith('--disable-features')); 55 | flags[indexDisableFeatures] = `${flags[indexDisableFeatures]},AutomationControlled`; 56 | // Remove "disable-component-update" flag 57 | const indexComponentUpdateFlag = flags.findIndex((flag) => flag.startsWith('--disable-component-update')); 58 | flags.splice(indexComponentUpdateFlag, 1); 59 | chromeFlags = [ 60 | ...flags, 61 | ...args, 62 | ...(headless !== false ? [`--headless=${headless}`] : []), 63 | ...(proxy && proxy.host && proxy.port 64 | ? [`--proxy-server=${proxy.host}:${proxy.port}`] 65 | : []), 66 | "--no-sandbox", 67 | "--disable-dev-shm-usage", 68 | ]; 69 | } 70 | const chrome = await launch({ 71 | ignoreDefaultFlags: true, 72 | chromeFlags, 73 | ...customConfig, 74 | }); 75 | 76 | if (plugins.length > 0) { 77 | const { addExtra } = await import("puppeteer-extra"); 78 | 79 | puppeteer = addExtra(puppeteer); 80 | 81 | for (const item of plugins) { 82 | puppeteer.use(item); 83 | } 84 | } 85 | 86 | const browser = await puppeteer.connect({ 87 | browserURL: `http://127.0.0.1:${chrome.port}`, 88 | ...connectOption, 89 | }); 90 | 91 | let [page] = await browser.pages(); 92 | 93 | let pageControllerConfig = { 94 | browser, 95 | page, 96 | proxy, 97 | turnstile, 98 | xvfbsession, 99 | pid: chrome.pid, 100 | plugins, 101 | }; 102 | 103 | page = await pageController({ 104 | ...pageControllerConfig, 105 | killProcess: true, 106 | chrome, 107 | }); 108 | 109 | browser.on("targetcreated", async (target) => { 110 | if (target.type() === "page") { 111 | let newPage = await target.page(); 112 | pageControllerConfig.page = newPage; 113 | newPage = await pageController(pageControllerConfig); 114 | } 115 | }); 116 | 117 | return { 118 | browser, 119 | page, 120 | }; 121 | } 122 | 123 | module.exports = { connect }; 124 | -------------------------------------------------------------------------------- /lib/cjs/module/pageController.js: -------------------------------------------------------------------------------- 1 | const { createCursor } = require('ghost-cursor'); 2 | const { checkTurnstile } = require('./turnstile.js'); 3 | const kill = require('tree-kill'); 4 | 5 | function getRandomInt(min, max) { 6 | min = Math.ceil(min); 7 | max = Math.floor(max); 8 | return Math.floor(Math.random() * (max - min + 1)) + min; 9 | } 10 | 11 | async function pageController({ browser, page, proxy, turnstile, xvfbsession, pid, plugins, killProcess = false, chrome }) { 12 | 13 | let solveStatus = turnstile 14 | 15 | page.on('close', () => { 16 | solveStatus = false 17 | }); 18 | 19 | 20 | browser.on('disconnected', async () => { 21 | solveStatus = false 22 | if (killProcess === true) { 23 | if (xvfbsession) try { xvfbsession.stopSync() } catch (err) { } 24 | if (chrome) try { chrome.kill() } catch (err) { console.log(err); } 25 | if (pid) try { kill(pid, 'SIGKILL', () => { }) } catch (err) { } 26 | } 27 | }); 28 | 29 | async function turnstileSolver() { 30 | while (solveStatus) { 31 | await checkTurnstile({ page }).catch(() => { }); 32 | await new Promise(r => setTimeout(r, 1000)); 33 | } 34 | return 35 | } 36 | 37 | turnstileSolver() 38 | 39 | if (proxy.username && proxy.password) await page.authenticate({ username: proxy.username, password: proxy.password }); 40 | 41 | if (plugins.length > 0) { 42 | for (const plugin of plugins) { 43 | plugin.onPageCreated(page) 44 | } 45 | } 46 | 47 | await page.evaluateOnNewDocument(() => { 48 | Object.defineProperty(MouseEvent.prototype, 'screenX', { 49 | get: function () { 50 | return this.clientX + window.screenX; 51 | } 52 | }); 53 | 54 | Object.defineProperty(MouseEvent.prototype, 'screenY', { 55 | get: function () { 56 | return this.clientY + window.screenY; 57 | } 58 | }); 59 | 60 | }); 61 | 62 | const cursor = createCursor(page); 63 | page.realCursor = cursor 64 | page.realClick = cursor.click 65 | 66 | return page 67 | } 68 | 69 | module.exports = { pageController } -------------------------------------------------------------------------------- /lib/cjs/module/turnstile.js: -------------------------------------------------------------------------------- 1 | const checkTurnstile = ({ page }) => { 2 | return new Promise(async (resolve, reject) => { 3 | var waitInterval = setTimeout(() => { clearInterval(waitInterval); resolve(false) }, 5000); 4 | try { 5 | const elements = await page.$$('[name="cf-turnstile-response"]'); 6 | if (elements.length <= 0) { 7 | 8 | const coordinates = await page.evaluate(() => { 9 | let coordinates = []; 10 | document.querySelectorAll('div').forEach(item => { 11 | try { 12 | let itemCoordinates = item.getBoundingClientRect() 13 | let itemCss = window.getComputedStyle(item) 14 | if (itemCss.margin == "0px" && itemCss.padding == "0px" && itemCoordinates.width > 290 && itemCoordinates.width <= 310 && !item.querySelector('*')) { 15 | coordinates.push({ x: itemCoordinates.x, y: item.getBoundingClientRect().y, w: item.getBoundingClientRect().width, h: item.getBoundingClientRect().height }) 16 | } 17 | } catch (err) { } 18 | }); 19 | 20 | if (coordinates.length <= 0) { 21 | document.querySelectorAll('div').forEach(item => { 22 | try { 23 | let itemCoordinates = item.getBoundingClientRect() 24 | if (itemCoordinates.width > 290 && itemCoordinates.width <= 310 && !item.querySelector('*')) { 25 | coordinates.push({ x: itemCoordinates.x, y: item.getBoundingClientRect().y, w: item.getBoundingClientRect().width, h: item.getBoundingClientRect().height }) 26 | } 27 | } catch (err) { } 28 | }); 29 | 30 | } 31 | 32 | return coordinates 33 | }) 34 | 35 | for (const item of coordinates) { 36 | try { 37 | let x = item.x + 30; 38 | let y = item.y + item.h / 2; 39 | await page.mouse.click(x, y); 40 | } catch (err) { } 41 | } 42 | return resolve(true) 43 | } 44 | 45 | for (const element of elements) { 46 | try { 47 | const parentElement = await element.evaluateHandle(el => el.parentElement); 48 | const box = await parentElement.boundingBox(); 49 | let x = box.x + 30; 50 | let y = box.y + box.height / 2; 51 | await page.mouse.click(x, y); 52 | } catch (err) { } 53 | } 54 | clearInterval(waitInterval) 55 | resolve(true) 56 | } catch (err) { 57 | clearInterval(waitInterval) 58 | resolve(false) 59 | } 60 | }) 61 | } 62 | 63 | module.exports = { checkTurnstile } -------------------------------------------------------------------------------- /lib/esm/index.mjs: -------------------------------------------------------------------------------- 1 | import { launch, Launcher } from "chrome-launcher"; 2 | import puppeteer from "rebrowser-puppeteer-core"; 3 | import { pageController } from "./module/pageController.mjs"; 4 | 5 | // process.env.REBROWSER_PATCHES_DEBUG=1 6 | export async function connect({ 7 | args = [], 8 | headless = false, 9 | customConfig = {}, 10 | proxy = {}, 11 | turnstile = false, 12 | connectOption = {}, 13 | disableXvfb = false, 14 | plugins = [], 15 | ignoreAllFlags = false, 16 | } = {}) { 17 | let xvfbsession = null; 18 | if (headless == "auto") headless = false; 19 | 20 | if (process.platform === "linux" && disableXvfb === false) { 21 | try { 22 | const { default: Xvfb } = await import("xvfb"); 23 | xvfbsession = new Xvfb({ 24 | silent: true, 25 | xvfb_args: ["-screen", "0", "1920x1080x24", "-ac"], 26 | }); 27 | xvfbsession.startSync(); 28 | } catch (err) { 29 | console.log( 30 | "You are running on a Linux platform but do not have xvfb installed. The browser can be captured. Please install it with the following command\n\nsudo apt-get install xvfb\n\n" + 31 | err.message 32 | ); 33 | } 34 | } 35 | 36 | let chromeFlags; 37 | if (ignoreAllFlags === true) { 38 | chromeFlags = [ 39 | ...args, 40 | ...(headless !== false ? [`--headless=${headless}`] : []), 41 | ...(proxy && proxy.host && proxy.port 42 | ? [`--proxy-server=${proxy.host}:${proxy.port}`] 43 | : []), 44 | ]; 45 | } else { 46 | // Default flags: https://github.com/GoogleChrome/chrome-launcher/blob/main/src/flags.ts 47 | const flags = Launcher.defaultFlags(); 48 | // Add AutomationControlled to "disable-features" flag 49 | const indexDisableFeatures = flags.findIndex((flag) => flag.startsWith('--disable-features')); 50 | flags[indexDisableFeatures] = `${flags[indexDisableFeatures]},AutomationControlled`; 51 | // Remove "disable-component-update" flag 52 | const indexComponentUpdateFlag = flags.findIndex((flag) => flag.startsWith('--disable-component-update')); 53 | flags.splice(indexComponentUpdateFlag, 1); 54 | chromeFlags = [ 55 | ...flags, 56 | ...args, 57 | ...(headless !== false ? [`--headless=${headless}`] : []), 58 | ...(proxy && proxy.host && proxy.port 59 | ? [`--proxy-server=${proxy.host}:${proxy.port}`] 60 | : []), 61 | "--no-sandbox", 62 | "--disable-dev-shm-usage", 63 | ]; 64 | } 65 | const chrome = await launch({ 66 | ignoreDefaultFlags: true, 67 | chromeFlags, 68 | ...customConfig, 69 | }); 70 | let pextra = null; 71 | if (plugins.length > 0) { 72 | const { addExtra } = await import("puppeteer-extra"); 73 | 74 | pextra = addExtra(puppeteer); 75 | 76 | for (const item of plugins) { 77 | pextra.use(item); 78 | } 79 | } 80 | 81 | const browser = await (pextra ? pextra : puppeteer).connect({ 82 | browserURL: `http://127.0.0.1:${chrome.port}`, 83 | ...connectOption, 84 | }); 85 | 86 | let [page] = await browser.pages(); 87 | 88 | let pageControllerConfig = { 89 | browser, 90 | page, 91 | proxy, 92 | turnstile, 93 | xvfbsession, 94 | pid: chrome.pid, 95 | plugins, 96 | }; 97 | 98 | page = await pageController({ 99 | ...pageControllerConfig, 100 | chrome, 101 | killProcess: true, 102 | }); 103 | 104 | browser.on("targetcreated", async (target) => { 105 | if (target.type() === "page") { 106 | let newPage = await target.page(); 107 | pageControllerConfig.page = newPage; 108 | newPage = await pageController(pageControllerConfig); 109 | } 110 | }); 111 | 112 | return { 113 | browser, 114 | page, 115 | }; 116 | } 117 | -------------------------------------------------------------------------------- /lib/esm/module/pageController.mjs: -------------------------------------------------------------------------------- 1 | import { createCursor } from 'ghost-cursor'; 2 | import { checkTurnstile } from './turnstile.mjs'; 3 | import kill from 'tree-kill'; 4 | 5 | function getRandomInt(min, max) { 6 | min = Math.ceil(min); 7 | max = Math.floor(max); 8 | return Math.floor(Math.random() * (max - min + 1)) + min; 9 | } 10 | 11 | export async function pageController({ browser, page, proxy, turnstile, xvfbsession, pid, plugins, killProcess = false, chrome }) { 12 | 13 | let solveStatus = turnstile 14 | 15 | page.on('close', () => { 16 | solveStatus = false 17 | }); 18 | 19 | 20 | browser.on('disconnected', async () => { 21 | solveStatus = false 22 | if (killProcess === true) { 23 | if (xvfbsession) try { xvfbsession.stopSync() } catch (err) { } 24 | if (chrome) try { chrome.kill() } catch (err) { console.log(err); } 25 | if (pid) try { kill(pid, 'SIGKILL', () => { }) } catch (err) { } 26 | } 27 | }); 28 | 29 | async function turnstileSolver() { 30 | while (solveStatus) { 31 | await checkTurnstile({ page }).catch(() => { }); 32 | await new Promise(r => setTimeout(r, 1000)); 33 | } 34 | return 35 | } 36 | 37 | turnstileSolver() 38 | 39 | if (proxy.username && proxy.password) await page.authenticate({ username: proxy.username, password: proxy.password }); 40 | 41 | if (plugins.length > 0) { 42 | for (const plugin of plugins) { 43 | plugin.onPageCreated(page) 44 | } 45 | } 46 | 47 | await page.evaluateOnNewDocument(() => { 48 | Object.defineProperty(MouseEvent.prototype, 'screenX', { 49 | get: function () { 50 | return this.clientX + window.screenX; 51 | } 52 | }); 53 | 54 | Object.defineProperty(MouseEvent.prototype, 'screenY', { 55 | get: function () { 56 | return this.clientY + window.screenY; 57 | } 58 | }); 59 | 60 | }); 61 | 62 | const cursor = createCursor(page); 63 | page.realCursor = cursor 64 | page.realClick = cursor.click 65 | return page 66 | } -------------------------------------------------------------------------------- /lib/esm/module/turnstile.mjs: -------------------------------------------------------------------------------- 1 | export const checkTurnstile = ({ page }) => { 2 | return new Promise(async (resolve, reject) => { 3 | var waitInterval = setTimeout(() => { clearInterval(waitInterval); resolve(false) }, 5000); 4 | 5 | try { 6 | const elements = await page.$$('[name="cf-turnstile-response"]'); 7 | if (elements.length <= 0) { 8 | 9 | const coordinates = await page.evaluate(() => { 10 | let coordinates = []; 11 | document.querySelectorAll('div').forEach(item => { 12 | try { 13 | let itemCoordinates = item.getBoundingClientRect() 14 | let itemCss = window.getComputedStyle(item) 15 | if (itemCss.margin == "0px" && itemCss.padding == "0px" && itemCoordinates.width > 290 && itemCoordinates.width <= 310 && !item.querySelector('*')) { 16 | coordinates.push({ x: itemCoordinates.x, y: item.getBoundingClientRect().y, w: item.getBoundingClientRect().width, h: item.getBoundingClientRect().height }) 17 | } 18 | } catch (err) { } 19 | }); 20 | 21 | if (coordinates.length <= 0) { 22 | document.querySelectorAll('div').forEach(item => { 23 | try { 24 | let itemCoordinates = item.getBoundingClientRect() 25 | if (itemCoordinates.width > 290 && itemCoordinates.width <= 310 && !item.querySelector('*')) { 26 | coordinates.push({ x: itemCoordinates.x, y: item.getBoundingClientRect().y, w: item.getBoundingClientRect().width, h: item.getBoundingClientRect().height }) 27 | } 28 | } catch (err) { } 29 | }); 30 | 31 | } 32 | 33 | return coordinates 34 | }) 35 | 36 | for (const item of coordinates) { 37 | try { 38 | let x = item.x + 30; 39 | let y = item.y + item.h / 2; 40 | await page.mouse.click(x, y); 41 | } catch (err) { } 42 | } 43 | return resolve(true) 44 | } 45 | 46 | for (const element of elements) { 47 | try { 48 | const parentElement = await element.evaluateHandle(el => el.parentElement); 49 | const box = await parentElement.boundingBox(); 50 | let x = box.x + 30; 51 | let y = box.y + box.height / 2; 52 | await page.mouse.click(x, y); 53 | } catch (err) { } 54 | } 55 | clearInterval(waitInterval) 56 | resolve(true) 57 | } catch (err) { 58 | clearInterval(waitInterval) 59 | resolve(false) 60 | } 61 | }) 62 | } -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "puppeteer-real-browser", 3 | "version": "1.3.22", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "puppeteer-real-browser", 9 | "version": "1.3.22", 10 | "license": "ISC", 11 | "dependencies": { 12 | "chrome-launcher": "^1.1.2", 13 | "ghost-cursor": "^1.3.0", 14 | "puppeteer-extra": "^3.3.6", 15 | "rebrowser-puppeteer-core": "^23.3.1", 16 | "tree-kill": "^1.2.2", 17 | "xvfb": "^0.4.0" 18 | } 19 | }, 20 | "node_modules/@tootallnate/quickjs-emscripten": { 21 | "version": "0.23.0", 22 | "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", 23 | "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", 24 | "license": "MIT" 25 | }, 26 | "node_modules/@types/bezier-js": { 27 | "version": "4.1.3", 28 | "resolved": "https://registry.npmjs.org/@types/bezier-js/-/bezier-js-4.1.3.tgz", 29 | "integrity": "sha512-FNVVCu5mx/rJCWBxLTcL7oOajmGtWtBTDjq6DSUWUI12GeePivrZZXz+UgE0D6VYsLEjvExRO03z4hVtu3pTEQ==", 30 | "license": "MIT" 31 | }, 32 | "node_modules/@types/debug": { 33 | "version": "4.1.12", 34 | "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", 35 | "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", 36 | "license": "MIT", 37 | "dependencies": { 38 | "@types/ms": "*" 39 | } 40 | }, 41 | "node_modules/@types/ms": { 42 | "version": "0.7.34", 43 | "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", 44 | "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", 45 | "license": "MIT" 46 | }, 47 | "node_modules/@types/node": { 48 | "version": "22.5.0", 49 | "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.0.tgz", 50 | "integrity": "sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg==", 51 | "license": "MIT", 52 | "dependencies": { 53 | "undici-types": "~6.19.2" 54 | } 55 | }, 56 | "node_modules/@types/yauzl": { 57 | "version": "2.10.3", 58 | "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", 59 | "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", 60 | "license": "MIT", 61 | "optional": true, 62 | "dependencies": { 63 | "@types/node": "*" 64 | } 65 | }, 66 | "node_modules/agent-base": { 67 | "version": "7.1.1", 68 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", 69 | "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", 70 | "license": "MIT", 71 | "dependencies": { 72 | "debug": "^4.3.4" 73 | }, 74 | "engines": { 75 | "node": ">= 14" 76 | } 77 | }, 78 | "node_modules/ansi-regex": { 79 | "version": "5.0.1", 80 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 81 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 82 | "license": "MIT", 83 | "engines": { 84 | "node": ">=8" 85 | } 86 | }, 87 | "node_modules/ansi-styles": { 88 | "version": "4.3.0", 89 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 90 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 91 | "license": "MIT", 92 | "dependencies": { 93 | "color-convert": "^2.0.1" 94 | }, 95 | "engines": { 96 | "node": ">=8" 97 | }, 98 | "funding": { 99 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 100 | } 101 | }, 102 | "node_modules/ast-types": { 103 | "version": "0.13.4", 104 | "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", 105 | "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", 106 | "license": "MIT", 107 | "dependencies": { 108 | "tslib": "^2.0.1" 109 | }, 110 | "engines": { 111 | "node": ">=4" 112 | } 113 | }, 114 | "node_modules/b4a": { 115 | "version": "1.6.6", 116 | "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", 117 | "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==", 118 | "license": "Apache-2.0" 119 | }, 120 | "node_modules/bare-events": { 121 | "version": "2.4.2", 122 | "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.4.2.tgz", 123 | "integrity": "sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==", 124 | "license": "Apache-2.0", 125 | "optional": true 126 | }, 127 | "node_modules/bare-fs": { 128 | "version": "2.3.3", 129 | "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.3.tgz", 130 | "integrity": "sha512-7RYKL+vZVCyAsMLi5SPu7QGauGGT8avnP/HO571ndEuV4MYdGXvLhtW67FuLPeEI8EiIY7zbbRR9x7x7HU0kgw==", 131 | "license": "Apache-2.0", 132 | "optional": true, 133 | "dependencies": { 134 | "bare-events": "^2.0.0", 135 | "bare-path": "^2.0.0", 136 | "bare-stream": "^2.0.0" 137 | } 138 | }, 139 | "node_modules/bare-os": { 140 | "version": "2.4.2", 141 | "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.2.tgz", 142 | "integrity": "sha512-HZoJwzC+rZ9lqEemTMiO0luOePoGYNBgsLLgegKR/cljiJvcDNhDZQkzC+NC5Oh0aHbdBNSOHpghwMuB5tqhjg==", 143 | "license": "Apache-2.0", 144 | "optional": true 145 | }, 146 | "node_modules/bare-path": { 147 | "version": "2.1.3", 148 | "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz", 149 | "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", 150 | "license": "Apache-2.0", 151 | "optional": true, 152 | "dependencies": { 153 | "bare-os": "^2.1.0" 154 | } 155 | }, 156 | "node_modules/bare-stream": { 157 | "version": "2.2.1", 158 | "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.2.1.tgz", 159 | "integrity": "sha512-YTB47kHwBW9zSG8LD77MIBAAQXjU2WjAkMHeeb7hUplVs6+IoM5I7uEVQNPMB7lj9r8I76UMdoMkGnCodHOLqg==", 160 | "license": "Apache-2.0", 161 | "optional": true, 162 | "dependencies": { 163 | "b4a": "^1.6.6", 164 | "streamx": "^2.18.0" 165 | } 166 | }, 167 | "node_modules/base64-js": { 168 | "version": "1.5.1", 169 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 170 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 171 | "funding": [ 172 | { 173 | "type": "github", 174 | "url": "https://github.com/sponsors/feross" 175 | }, 176 | { 177 | "type": "patreon", 178 | "url": "https://www.patreon.com/feross" 179 | }, 180 | { 181 | "type": "consulting", 182 | "url": "https://feross.org/support" 183 | } 184 | ], 185 | "license": "MIT" 186 | }, 187 | "node_modules/basic-ftp": { 188 | "version": "5.0.5", 189 | "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", 190 | "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", 191 | "license": "MIT", 192 | "engines": { 193 | "node": ">=10.0.0" 194 | } 195 | }, 196 | "node_modules/bezier-js": { 197 | "version": "6.1.4", 198 | "resolved": "https://registry.npmjs.org/bezier-js/-/bezier-js-6.1.4.tgz", 199 | "integrity": "sha512-PA0FW9ZpcHbojUCMu28z9Vg/fNkwTj5YhusSAjHHDfHDGLxJ6YUKrAN2vk1fP2MMOxVw4Oko16FMlRGVBGqLKg==", 200 | "license": "MIT", 201 | "funding": { 202 | "type": "individual", 203 | "url": "https://github.com/Pomax/bezierjs/blob/master/FUNDING.md" 204 | } 205 | }, 206 | "node_modules/buffer": { 207 | "version": "5.7.1", 208 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", 209 | "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", 210 | "funding": [ 211 | { 212 | "type": "github", 213 | "url": "https://github.com/sponsors/feross" 214 | }, 215 | { 216 | "type": "patreon", 217 | "url": "https://www.patreon.com/feross" 218 | }, 219 | { 220 | "type": "consulting", 221 | "url": "https://feross.org/support" 222 | } 223 | ], 224 | "license": "MIT", 225 | "dependencies": { 226 | "base64-js": "^1.3.1", 227 | "ieee754": "^1.1.13" 228 | } 229 | }, 230 | "node_modules/buffer-crc32": { 231 | "version": "0.2.13", 232 | "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", 233 | "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", 234 | "license": "MIT", 235 | "engines": { 236 | "node": "*" 237 | } 238 | }, 239 | "node_modules/chrome-launcher": { 240 | "version": "1.1.2", 241 | "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-1.1.2.tgz", 242 | "integrity": "sha512-YclTJey34KUm5jB1aEJCq807bSievi7Nb/TU4Gu504fUYi3jw3KCIaH6L7nFWQhdEgH3V+wCh+kKD1P5cXnfxw==", 243 | "license": "Apache-2.0", 244 | "dependencies": { 245 | "@types/node": "*", 246 | "escape-string-regexp": "^4.0.0", 247 | "is-wsl": "^2.2.0", 248 | "lighthouse-logger": "^2.0.1" 249 | }, 250 | "bin": { 251 | "print-chrome-path": "bin/print-chrome-path.js" 252 | }, 253 | "engines": { 254 | "node": ">=12.13.0" 255 | } 256 | }, 257 | "node_modules/chromium-bidi": { 258 | "version": "0.6.5", 259 | "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.6.5.tgz", 260 | "integrity": "sha512-RuLrmzYrxSb0s9SgpB+QN5jJucPduZQ/9SIe76MDxYJuecPW5mxMdacJ1f4EtgiV+R0p3sCkznTMvH0MPGFqjA==", 261 | "license": "Apache-2.0", 262 | "dependencies": { 263 | "mitt": "3.0.1", 264 | "urlpattern-polyfill": "10.0.0", 265 | "zod": "3.23.8" 266 | }, 267 | "peerDependencies": { 268 | "devtools-protocol": "*" 269 | } 270 | }, 271 | "node_modules/cliui": { 272 | "version": "8.0.1", 273 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", 274 | "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", 275 | "license": "ISC", 276 | "dependencies": { 277 | "string-width": "^4.2.0", 278 | "strip-ansi": "^6.0.1", 279 | "wrap-ansi": "^7.0.0" 280 | }, 281 | "engines": { 282 | "node": ">=12" 283 | } 284 | }, 285 | "node_modules/color-convert": { 286 | "version": "2.0.1", 287 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 288 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 289 | "license": "MIT", 290 | "dependencies": { 291 | "color-name": "~1.1.4" 292 | }, 293 | "engines": { 294 | "node": ">=7.0.0" 295 | } 296 | }, 297 | "node_modules/color-name": { 298 | "version": "1.1.4", 299 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 300 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 301 | "license": "MIT" 302 | }, 303 | "node_modules/data-uri-to-buffer": { 304 | "version": "6.0.2", 305 | "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", 306 | "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", 307 | "license": "MIT", 308 | "engines": { 309 | "node": ">= 14" 310 | } 311 | }, 312 | "node_modules/debug": { 313 | "version": "4.3.7", 314 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", 315 | "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", 316 | "license": "MIT", 317 | "dependencies": { 318 | "ms": "^2.1.3" 319 | }, 320 | "engines": { 321 | "node": ">=6.0" 322 | }, 323 | "peerDependenciesMeta": { 324 | "supports-color": { 325 | "optional": true 326 | } 327 | } 328 | }, 329 | "node_modules/deepmerge": { 330 | "version": "4.3.1", 331 | "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", 332 | "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", 333 | "license": "MIT", 334 | "engines": { 335 | "node": ">=0.10.0" 336 | } 337 | }, 338 | "node_modules/degenerator": { 339 | "version": "5.0.1", 340 | "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", 341 | "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", 342 | "license": "MIT", 343 | "dependencies": { 344 | "ast-types": "^0.13.4", 345 | "escodegen": "^2.1.0", 346 | "esprima": "^4.0.1" 347 | }, 348 | "engines": { 349 | "node": ">= 14" 350 | } 351 | }, 352 | "node_modules/devtools-protocol": { 353 | "version": "0.0.1330662", 354 | "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1330662.tgz", 355 | "integrity": "sha512-pzh6YQ8zZfz3iKlCvgzVCu22NdpZ8hNmwU6WnQjNVquh0A9iVosPtNLWDwaWVGyrntQlltPFztTMK5Cg6lfCuw==", 356 | "license": "BSD-3-Clause" 357 | }, 358 | "node_modules/emoji-regex": { 359 | "version": "8.0.0", 360 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 361 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 362 | "license": "MIT" 363 | }, 364 | "node_modules/end-of-stream": { 365 | "version": "1.4.4", 366 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", 367 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", 368 | "license": "MIT", 369 | "dependencies": { 370 | "once": "^1.4.0" 371 | } 372 | }, 373 | "node_modules/escalade": { 374 | "version": "3.2.0", 375 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", 376 | "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", 377 | "license": "MIT", 378 | "engines": { 379 | "node": ">=6" 380 | } 381 | }, 382 | "node_modules/escape-string-regexp": { 383 | "version": "4.0.0", 384 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 385 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 386 | "license": "MIT", 387 | "engines": { 388 | "node": ">=10" 389 | }, 390 | "funding": { 391 | "url": "https://github.com/sponsors/sindresorhus" 392 | } 393 | }, 394 | "node_modules/escodegen": { 395 | "version": "2.1.0", 396 | "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", 397 | "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", 398 | "license": "BSD-2-Clause", 399 | "dependencies": { 400 | "esprima": "^4.0.1", 401 | "estraverse": "^5.2.0", 402 | "esutils": "^2.0.2" 403 | }, 404 | "bin": { 405 | "escodegen": "bin/escodegen.js", 406 | "esgenerate": "bin/esgenerate.js" 407 | }, 408 | "engines": { 409 | "node": ">=6.0" 410 | }, 411 | "optionalDependencies": { 412 | "source-map": "~0.6.1" 413 | } 414 | }, 415 | "node_modules/esprima": { 416 | "version": "4.0.1", 417 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 418 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 419 | "license": "BSD-2-Clause", 420 | "bin": { 421 | "esparse": "bin/esparse.js", 422 | "esvalidate": "bin/esvalidate.js" 423 | }, 424 | "engines": { 425 | "node": ">=4" 426 | } 427 | }, 428 | "node_modules/estraverse": { 429 | "version": "5.3.0", 430 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", 431 | "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", 432 | "license": "BSD-2-Clause", 433 | "engines": { 434 | "node": ">=4.0" 435 | } 436 | }, 437 | "node_modules/esutils": { 438 | "version": "2.0.3", 439 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 440 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 441 | "license": "BSD-2-Clause", 442 | "engines": { 443 | "node": ">=0.10.0" 444 | } 445 | }, 446 | "node_modules/extract-zip": { 447 | "version": "2.0.1", 448 | "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", 449 | "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", 450 | "license": "BSD-2-Clause", 451 | "dependencies": { 452 | "debug": "^4.1.1", 453 | "get-stream": "^5.1.0", 454 | "yauzl": "^2.10.0" 455 | }, 456 | "bin": { 457 | "extract-zip": "cli.js" 458 | }, 459 | "engines": { 460 | "node": ">= 10.17.0" 461 | }, 462 | "optionalDependencies": { 463 | "@types/yauzl": "^2.9.1" 464 | } 465 | }, 466 | "node_modules/fast-fifo": { 467 | "version": "1.3.2", 468 | "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", 469 | "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", 470 | "license": "MIT" 471 | }, 472 | "node_modules/fd-slicer": { 473 | "version": "1.1.0", 474 | "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", 475 | "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", 476 | "license": "MIT", 477 | "dependencies": { 478 | "pend": "~1.2.0" 479 | } 480 | }, 481 | "node_modules/fs-extra": { 482 | "version": "11.2.0", 483 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", 484 | "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", 485 | "license": "MIT", 486 | "dependencies": { 487 | "graceful-fs": "^4.2.0", 488 | "jsonfile": "^6.0.1", 489 | "universalify": "^2.0.0" 490 | }, 491 | "engines": { 492 | "node": ">=14.14" 493 | } 494 | }, 495 | "node_modules/get-caller-file": { 496 | "version": "2.0.5", 497 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 498 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 499 | "license": "ISC", 500 | "engines": { 501 | "node": "6.* || 8.* || >= 10.*" 502 | } 503 | }, 504 | "node_modules/get-stream": { 505 | "version": "5.2.0", 506 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", 507 | "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", 508 | "license": "MIT", 509 | "dependencies": { 510 | "pump": "^3.0.0" 511 | }, 512 | "engines": { 513 | "node": ">=8" 514 | }, 515 | "funding": { 516 | "url": "https://github.com/sponsors/sindresorhus" 517 | } 518 | }, 519 | "node_modules/get-uri": { 520 | "version": "6.0.3", 521 | "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", 522 | "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", 523 | "license": "MIT", 524 | "dependencies": { 525 | "basic-ftp": "^5.0.2", 526 | "data-uri-to-buffer": "^6.0.2", 527 | "debug": "^4.3.4", 528 | "fs-extra": "^11.2.0" 529 | }, 530 | "engines": { 531 | "node": ">= 14" 532 | } 533 | }, 534 | "node_modules/ghost-cursor": { 535 | "version": "1.3.0", 536 | "resolved": "https://registry.npmjs.org/ghost-cursor/-/ghost-cursor-1.3.0.tgz", 537 | "integrity": "sha512-niTjgH8o2EhYG0vJfGB45JrLAt5CRoXg+8zWIcXtcgUgLds+i6YyFXkYuHpno9gKi93KpQvxY/uVYMV9CeeJ0w==", 538 | "license": "ISC", 539 | "dependencies": { 540 | "@types/bezier-js": "4", 541 | "bezier-js": "^6.1.3", 542 | "debug": "^4.3.4" 543 | } 544 | }, 545 | "node_modules/graceful-fs": { 546 | "version": "4.2.11", 547 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", 548 | "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", 549 | "license": "ISC" 550 | }, 551 | "node_modules/http-proxy-agent": { 552 | "version": "7.0.2", 553 | "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", 554 | "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", 555 | "license": "MIT", 556 | "dependencies": { 557 | "agent-base": "^7.1.0", 558 | "debug": "^4.3.4" 559 | }, 560 | "engines": { 561 | "node": ">= 14" 562 | } 563 | }, 564 | "node_modules/https-proxy-agent": { 565 | "version": "7.0.5", 566 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", 567 | "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", 568 | "license": "MIT", 569 | "dependencies": { 570 | "agent-base": "^7.0.2", 571 | "debug": "4" 572 | }, 573 | "engines": { 574 | "node": ">= 14" 575 | } 576 | }, 577 | "node_modules/ieee754": { 578 | "version": "1.2.1", 579 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 580 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 581 | "funding": [ 582 | { 583 | "type": "github", 584 | "url": "https://github.com/sponsors/feross" 585 | }, 586 | { 587 | "type": "patreon", 588 | "url": "https://www.patreon.com/feross" 589 | }, 590 | { 591 | "type": "consulting", 592 | "url": "https://feross.org/support" 593 | } 594 | ], 595 | "license": "BSD-3-Clause" 596 | }, 597 | "node_modules/ip-address": { 598 | "version": "9.0.5", 599 | "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", 600 | "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", 601 | "license": "MIT", 602 | "dependencies": { 603 | "jsbn": "1.1.0", 604 | "sprintf-js": "^1.1.3" 605 | }, 606 | "engines": { 607 | "node": ">= 12" 608 | } 609 | }, 610 | "node_modules/is-docker": { 611 | "version": "2.2.1", 612 | "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", 613 | "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", 614 | "license": "MIT", 615 | "bin": { 616 | "is-docker": "cli.js" 617 | }, 618 | "engines": { 619 | "node": ">=8" 620 | }, 621 | "funding": { 622 | "url": "https://github.com/sponsors/sindresorhus" 623 | } 624 | }, 625 | "node_modules/is-fullwidth-code-point": { 626 | "version": "3.0.0", 627 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 628 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 629 | "license": "MIT", 630 | "engines": { 631 | "node": ">=8" 632 | } 633 | }, 634 | "node_modules/is-wsl": { 635 | "version": "2.2.0", 636 | "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", 637 | "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", 638 | "license": "MIT", 639 | "dependencies": { 640 | "is-docker": "^2.0.0" 641 | }, 642 | "engines": { 643 | "node": ">=8" 644 | } 645 | }, 646 | "node_modules/jsbn": { 647 | "version": "1.1.0", 648 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", 649 | "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", 650 | "license": "MIT" 651 | }, 652 | "node_modules/jsonfile": { 653 | "version": "6.1.0", 654 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", 655 | "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", 656 | "license": "MIT", 657 | "dependencies": { 658 | "universalify": "^2.0.0" 659 | }, 660 | "optionalDependencies": { 661 | "graceful-fs": "^4.1.6" 662 | } 663 | }, 664 | "node_modules/lighthouse-logger": { 665 | "version": "2.0.1", 666 | "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-2.0.1.tgz", 667 | "integrity": "sha512-ioBrW3s2i97noEmnXxmUq7cjIcVRjT5HBpAYy8zE11CxU9HqlWHHeRxfeN1tn8F7OEMVPIC9x1f8t3Z7US9ehQ==", 668 | "license": "Apache-2.0", 669 | "dependencies": { 670 | "debug": "^2.6.9", 671 | "marky": "^1.2.2" 672 | } 673 | }, 674 | "node_modules/lighthouse-logger/node_modules/debug": { 675 | "version": "2.6.9", 676 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 677 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 678 | "license": "MIT", 679 | "dependencies": { 680 | "ms": "2.0.0" 681 | } 682 | }, 683 | "node_modules/lighthouse-logger/node_modules/ms": { 684 | "version": "2.0.0", 685 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 686 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", 687 | "license": "MIT" 688 | }, 689 | "node_modules/lru-cache": { 690 | "version": "7.18.3", 691 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", 692 | "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", 693 | "license": "ISC", 694 | "engines": { 695 | "node": ">=12" 696 | } 697 | }, 698 | "node_modules/marky": { 699 | "version": "1.2.5", 700 | "resolved": "https://registry.npmjs.org/marky/-/marky-1.2.5.tgz", 701 | "integrity": "sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q==", 702 | "license": "Apache-2.0" 703 | }, 704 | "node_modules/mitt": { 705 | "version": "3.0.1", 706 | "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", 707 | "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", 708 | "license": "MIT" 709 | }, 710 | "node_modules/ms": { 711 | "version": "2.1.3", 712 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 713 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 714 | "license": "MIT" 715 | }, 716 | "node_modules/nan": { 717 | "version": "2.20.0", 718 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz", 719 | "integrity": "sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==", 720 | "license": "MIT", 721 | "optional": true 722 | }, 723 | "node_modules/netmask": { 724 | "version": "2.0.2", 725 | "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", 726 | "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", 727 | "license": "MIT", 728 | "engines": { 729 | "node": ">= 0.4.0" 730 | } 731 | }, 732 | "node_modules/once": { 733 | "version": "1.4.0", 734 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 735 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 736 | "license": "ISC", 737 | "dependencies": { 738 | "wrappy": "1" 739 | } 740 | }, 741 | "node_modules/pac-proxy-agent": { 742 | "version": "7.0.2", 743 | "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz", 744 | "integrity": "sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==", 745 | "license": "MIT", 746 | "dependencies": { 747 | "@tootallnate/quickjs-emscripten": "^0.23.0", 748 | "agent-base": "^7.0.2", 749 | "debug": "^4.3.4", 750 | "get-uri": "^6.0.1", 751 | "http-proxy-agent": "^7.0.0", 752 | "https-proxy-agent": "^7.0.5", 753 | "pac-resolver": "^7.0.1", 754 | "socks-proxy-agent": "^8.0.4" 755 | }, 756 | "engines": { 757 | "node": ">= 14" 758 | } 759 | }, 760 | "node_modules/pac-resolver": { 761 | "version": "7.0.1", 762 | "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", 763 | "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", 764 | "license": "MIT", 765 | "dependencies": { 766 | "degenerator": "^5.0.0", 767 | "netmask": "^2.0.2" 768 | }, 769 | "engines": { 770 | "node": ">= 14" 771 | } 772 | }, 773 | "node_modules/pend": { 774 | "version": "1.2.0", 775 | "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", 776 | "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", 777 | "license": "MIT" 778 | }, 779 | "node_modules/progress": { 780 | "version": "2.0.3", 781 | "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", 782 | "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", 783 | "license": "MIT", 784 | "engines": { 785 | "node": ">=0.4.0" 786 | } 787 | }, 788 | "node_modules/proxy-agent": { 789 | "version": "6.4.0", 790 | "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", 791 | "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", 792 | "license": "MIT", 793 | "dependencies": { 794 | "agent-base": "^7.0.2", 795 | "debug": "^4.3.4", 796 | "http-proxy-agent": "^7.0.1", 797 | "https-proxy-agent": "^7.0.3", 798 | "lru-cache": "^7.14.1", 799 | "pac-proxy-agent": "^7.0.1", 800 | "proxy-from-env": "^1.1.0", 801 | "socks-proxy-agent": "^8.0.2" 802 | }, 803 | "engines": { 804 | "node": ">= 14" 805 | } 806 | }, 807 | "node_modules/proxy-from-env": { 808 | "version": "1.1.0", 809 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 810 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", 811 | "license": "MIT" 812 | }, 813 | "node_modules/pump": { 814 | "version": "3.0.0", 815 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", 816 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", 817 | "license": "MIT", 818 | "dependencies": { 819 | "end-of-stream": "^1.1.0", 820 | "once": "^1.3.1" 821 | } 822 | }, 823 | "node_modules/puppeteer-extra": { 824 | "version": "3.3.6", 825 | "resolved": "https://registry.npmjs.org/puppeteer-extra/-/puppeteer-extra-3.3.6.tgz", 826 | "integrity": "sha512-rsLBE/6mMxAjlLd06LuGacrukP2bqbzKCLzV1vrhHFavqQE/taQ2UXv3H5P0Ls7nsrASa+6x3bDbXHpqMwq+7A==", 827 | "license": "MIT", 828 | "dependencies": { 829 | "@types/debug": "^4.1.0", 830 | "debug": "^4.1.1", 831 | "deepmerge": "^4.2.2" 832 | }, 833 | "engines": { 834 | "node": ">=8" 835 | }, 836 | "peerDependencies": { 837 | "@types/puppeteer": "*", 838 | "puppeteer": "*", 839 | "puppeteer-core": "*" 840 | }, 841 | "peerDependenciesMeta": { 842 | "@types/puppeteer": { 843 | "optional": true 844 | }, 845 | "puppeteer": { 846 | "optional": true 847 | }, 848 | "puppeteer-core": { 849 | "optional": true 850 | } 851 | } 852 | }, 853 | "node_modules/queue-tick": { 854 | "version": "1.0.1", 855 | "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", 856 | "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", 857 | "license": "MIT" 858 | }, 859 | "node_modules/rebrowser-puppeteer-core": { 860 | "version": "23.3.1", 861 | "resolved": "https://registry.npmjs.org/rebrowser-puppeteer-core/-/rebrowser-puppeteer-core-23.3.1.tgz", 862 | "integrity": "sha512-+BB6leL2aSnpR/DYc0cqcdue3PCzPzT6t0b3IUFbhfQVjfr0WHXNpEd5/AECTg42O/3YdMkLLXNyfzmd+aiFsw==", 863 | "license": "Apache-2.0", 864 | "dependencies": { 865 | "@puppeteer/browsers": "2.4.0", 866 | "chromium-bidi": "0.6.5", 867 | "debug": "^4.3.7", 868 | "devtools-protocol": "0.0.1330662", 869 | "typed-query-selector": "^2.12.0", 870 | "ws": "^8.18.0" 871 | }, 872 | "engines": { 873 | "node": ">=18" 874 | } 875 | }, 876 | "node_modules/rebrowser-puppeteer-core/node_modules/@puppeteer/browsers": { 877 | "version": "2.4.0", 878 | "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.4.0.tgz", 879 | "integrity": "sha512-x8J1csfIygOwf6D6qUAZ0ASk3z63zPb7wkNeHRerCMh82qWKUrOgkuP005AJC8lDL6/evtXETGEJVcwykKT4/g==", 880 | "license": "Apache-2.0", 881 | "dependencies": { 882 | "debug": "^4.3.6", 883 | "extract-zip": "^2.0.1", 884 | "progress": "^2.0.3", 885 | "proxy-agent": "^6.4.0", 886 | "semver": "^7.6.3", 887 | "tar-fs": "^3.0.6", 888 | "unbzip2-stream": "^1.4.3", 889 | "yargs": "^17.7.2" 890 | }, 891 | "bin": { 892 | "browsers": "lib/cjs/main-cli.js" 893 | }, 894 | "engines": { 895 | "node": ">=18" 896 | } 897 | }, 898 | "node_modules/require-directory": { 899 | "version": "2.1.1", 900 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 901 | "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", 902 | "license": "MIT", 903 | "engines": { 904 | "node": ">=0.10.0" 905 | } 906 | }, 907 | "node_modules/semver": { 908 | "version": "7.6.3", 909 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", 910 | "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", 911 | "license": "ISC", 912 | "bin": { 913 | "semver": "bin/semver.js" 914 | }, 915 | "engines": { 916 | "node": ">=10" 917 | } 918 | }, 919 | "node_modules/sleep": { 920 | "version": "6.1.0", 921 | "resolved": "https://registry.npmjs.org/sleep/-/sleep-6.1.0.tgz", 922 | "integrity": "sha512-Z1x4JjJxsru75Tqn8F4tnOFeEu3HjtITTsumYUiuz54sGKdISgLCek9AUlXlVVrkhltRFhNUsJDJE76SFHTDIQ==", 923 | "hasInstallScript": true, 924 | "license": "MIT", 925 | "optional": true, 926 | "dependencies": { 927 | "nan": "^2.13.2" 928 | }, 929 | "engines": { 930 | "node": ">=0.8.0" 931 | } 932 | }, 933 | "node_modules/smart-buffer": { 934 | "version": "4.2.0", 935 | "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", 936 | "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", 937 | "license": "MIT", 938 | "engines": { 939 | "node": ">= 6.0.0", 940 | "npm": ">= 3.0.0" 941 | } 942 | }, 943 | "node_modules/socks": { 944 | "version": "2.8.3", 945 | "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", 946 | "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", 947 | "license": "MIT", 948 | "dependencies": { 949 | "ip-address": "^9.0.5", 950 | "smart-buffer": "^4.2.0" 951 | }, 952 | "engines": { 953 | "node": ">= 10.0.0", 954 | "npm": ">= 3.0.0" 955 | } 956 | }, 957 | "node_modules/socks-proxy-agent": { 958 | "version": "8.0.4", 959 | "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", 960 | "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", 961 | "license": "MIT", 962 | "dependencies": { 963 | "agent-base": "^7.1.1", 964 | "debug": "^4.3.4", 965 | "socks": "^2.8.3" 966 | }, 967 | "engines": { 968 | "node": ">= 14" 969 | } 970 | }, 971 | "node_modules/source-map": { 972 | "version": "0.6.1", 973 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 974 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 975 | "license": "BSD-3-Clause", 976 | "optional": true, 977 | "engines": { 978 | "node": ">=0.10.0" 979 | } 980 | }, 981 | "node_modules/sprintf-js": { 982 | "version": "1.1.3", 983 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", 984 | "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", 985 | "license": "BSD-3-Clause" 986 | }, 987 | "node_modules/streamx": { 988 | "version": "2.20.0", 989 | "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.0.tgz", 990 | "integrity": "sha512-ZGd1LhDeGFucr1CUCTBOS58ZhEendd0ttpGT3usTvosS4ntIwKN9LJFp+OeCSprsCPL14BXVRZlHGRY1V9PVzQ==", 991 | "license": "MIT", 992 | "dependencies": { 993 | "fast-fifo": "^1.3.2", 994 | "queue-tick": "^1.0.1", 995 | "text-decoder": "^1.1.0" 996 | }, 997 | "optionalDependencies": { 998 | "bare-events": "^2.2.0" 999 | } 1000 | }, 1001 | "node_modules/string-width": { 1002 | "version": "4.2.3", 1003 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 1004 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 1005 | "license": "MIT", 1006 | "dependencies": { 1007 | "emoji-regex": "^8.0.0", 1008 | "is-fullwidth-code-point": "^3.0.0", 1009 | "strip-ansi": "^6.0.1" 1010 | }, 1011 | "engines": { 1012 | "node": ">=8" 1013 | } 1014 | }, 1015 | "node_modules/strip-ansi": { 1016 | "version": "6.0.1", 1017 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1018 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1019 | "license": "MIT", 1020 | "dependencies": { 1021 | "ansi-regex": "^5.0.1" 1022 | }, 1023 | "engines": { 1024 | "node": ">=8" 1025 | } 1026 | }, 1027 | "node_modules/tar-fs": { 1028 | "version": "3.0.6", 1029 | "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz", 1030 | "integrity": "sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==", 1031 | "license": "MIT", 1032 | "dependencies": { 1033 | "pump": "^3.0.0", 1034 | "tar-stream": "^3.1.5" 1035 | }, 1036 | "optionalDependencies": { 1037 | "bare-fs": "^2.1.1", 1038 | "bare-path": "^2.1.0" 1039 | } 1040 | }, 1041 | "node_modules/tar-stream": { 1042 | "version": "3.1.7", 1043 | "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", 1044 | "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", 1045 | "license": "MIT", 1046 | "dependencies": { 1047 | "b4a": "^1.6.4", 1048 | "fast-fifo": "^1.2.0", 1049 | "streamx": "^2.15.0" 1050 | } 1051 | }, 1052 | "node_modules/text-decoder": { 1053 | "version": "1.1.1", 1054 | "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.1.1.tgz", 1055 | "integrity": "sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA==", 1056 | "license": "Apache-2.0", 1057 | "dependencies": { 1058 | "b4a": "^1.6.4" 1059 | } 1060 | }, 1061 | "node_modules/through": { 1062 | "version": "2.3.8", 1063 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 1064 | "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", 1065 | "license": "MIT" 1066 | }, 1067 | "node_modules/tree-kill": { 1068 | "version": "1.2.2", 1069 | "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", 1070 | "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", 1071 | "license": "MIT", 1072 | "bin": { 1073 | "tree-kill": "cli.js" 1074 | } 1075 | }, 1076 | "node_modules/tslib": { 1077 | "version": "2.7.0", 1078 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", 1079 | "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", 1080 | "license": "0BSD" 1081 | }, 1082 | "node_modules/typed-query-selector": { 1083 | "version": "2.12.0", 1084 | "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz", 1085 | "integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==", 1086 | "license": "MIT" 1087 | }, 1088 | "node_modules/unbzip2-stream": { 1089 | "version": "1.4.3", 1090 | "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", 1091 | "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", 1092 | "license": "MIT", 1093 | "dependencies": { 1094 | "buffer": "^5.2.1", 1095 | "through": "^2.3.8" 1096 | } 1097 | }, 1098 | "node_modules/undici-types": { 1099 | "version": "6.19.8", 1100 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", 1101 | "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", 1102 | "license": "MIT" 1103 | }, 1104 | "node_modules/universalify": { 1105 | "version": "2.0.1", 1106 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", 1107 | "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", 1108 | "license": "MIT", 1109 | "engines": { 1110 | "node": ">= 10.0.0" 1111 | } 1112 | }, 1113 | "node_modules/urlpattern-polyfill": { 1114 | "version": "10.0.0", 1115 | "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", 1116 | "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", 1117 | "license": "MIT" 1118 | }, 1119 | "node_modules/wrap-ansi": { 1120 | "version": "7.0.0", 1121 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 1122 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 1123 | "license": "MIT", 1124 | "dependencies": { 1125 | "ansi-styles": "^4.0.0", 1126 | "string-width": "^4.1.0", 1127 | "strip-ansi": "^6.0.0" 1128 | }, 1129 | "engines": { 1130 | "node": ">=10" 1131 | }, 1132 | "funding": { 1133 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 1134 | } 1135 | }, 1136 | "node_modules/wrappy": { 1137 | "version": "1.0.2", 1138 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1139 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 1140 | "license": "ISC" 1141 | }, 1142 | "node_modules/ws": { 1143 | "version": "8.18.0", 1144 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", 1145 | "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", 1146 | "license": "MIT", 1147 | "engines": { 1148 | "node": ">=10.0.0" 1149 | }, 1150 | "peerDependencies": { 1151 | "bufferutil": "^4.0.1", 1152 | "utf-8-validate": ">=5.0.2" 1153 | }, 1154 | "peerDependenciesMeta": { 1155 | "bufferutil": { 1156 | "optional": true 1157 | }, 1158 | "utf-8-validate": { 1159 | "optional": true 1160 | } 1161 | } 1162 | }, 1163 | "node_modules/xvfb": { 1164 | "version": "0.4.0", 1165 | "resolved": "https://registry.npmjs.org/xvfb/-/xvfb-0.4.0.tgz", 1166 | "integrity": "sha512-g55AbjcBL4Bztfn7kiUrR0ne8mMUsFODDJ+HFGf5OuHJqKKccpExX2Qgn7VF2eImw1eoh6+riXHser1J4agrFA==", 1167 | "license": "MIT", 1168 | "optionalDependencies": { 1169 | "sleep": "6.1.0" 1170 | } 1171 | }, 1172 | "node_modules/y18n": { 1173 | "version": "5.0.8", 1174 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 1175 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 1176 | "license": "ISC", 1177 | "engines": { 1178 | "node": ">=10" 1179 | } 1180 | }, 1181 | "node_modules/yargs": { 1182 | "version": "17.7.2", 1183 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", 1184 | "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", 1185 | "license": "MIT", 1186 | "dependencies": { 1187 | "cliui": "^8.0.1", 1188 | "escalade": "^3.1.1", 1189 | "get-caller-file": "^2.0.5", 1190 | "require-directory": "^2.1.1", 1191 | "string-width": "^4.2.3", 1192 | "y18n": "^5.0.5", 1193 | "yargs-parser": "^21.1.1" 1194 | }, 1195 | "engines": { 1196 | "node": ">=12" 1197 | } 1198 | }, 1199 | "node_modules/yargs-parser": { 1200 | "version": "21.1.1", 1201 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", 1202 | "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", 1203 | "license": "ISC", 1204 | "engines": { 1205 | "node": ">=12" 1206 | } 1207 | }, 1208 | "node_modules/yauzl": { 1209 | "version": "2.10.0", 1210 | "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", 1211 | "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", 1212 | "license": "MIT", 1213 | "dependencies": { 1214 | "buffer-crc32": "~0.2.3", 1215 | "fd-slicer": "~1.1.0" 1216 | } 1217 | }, 1218 | "node_modules/zod": { 1219 | "version": "3.23.8", 1220 | "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", 1221 | "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", 1222 | "license": "MIT", 1223 | "funding": { 1224 | "url": "https://github.com/sponsors/colinhacks" 1225 | } 1226 | } 1227 | } 1228 | } 1229 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "puppeteer-real-browser", 3 | "version": "1.4.2", 4 | "description": "This package is designed to bypass puppeteer's bot-detecting captchas such as Cloudflare. It acts like a real browser and can be managed with puppeteer.", 5 | "main": "lib/cjs/index.js", 6 | "module": "lib/esm/index.mjs", 7 | "exports": { 8 | ".": { 9 | "require": "./lib/cjs/index.js", 10 | "import": "./lib/esm/index.mjs", 11 | "types": "./typings.d.ts" 12 | } 13 | }, 14 | "typings": "typings.d.ts", 15 | "scripts": { 16 | "esm_test": "node ./test/esm/test.js", 17 | "cjs_test": "node ./test/cjs/test.js" 18 | }, 19 | "keywords": [ 20 | "cf-bypass", 21 | "cloudflare-bypass", 22 | "puppeteer-fingerprint", 23 | "puppeteer-cloudflare", 24 | "puppeteer-real-browser", 25 | "undetectable-puppeteer", 26 | "undetect", 27 | "undetectable", 28 | "puppeteer-undetectable", 29 | "puppeteer-undetect", 30 | "puppeteer-undetectable-bypass", 31 | "puppeteer-undetect-bypass", 32 | "puppeteer-undetectable-cloudflare", 33 | "puppeteer-undetect-cloudflare", 34 | "puppeteer-undetectable-cf", 35 | "puppeteer-undetect-cf", 36 | "puppeteer-undetectable-cf-bypass" 37 | ], 38 | "author": "zfc-software", 39 | "license": "ISC", 40 | "dependencies": { 41 | "chrome-launcher": "^1.1.2", 42 | "ghost-cursor": "^1.3.0", 43 | "puppeteer-extra": "^3.3.6", 44 | "rebrowser-puppeteer-core": "^23.3.1", 45 | "tree-kill": "^1.2.2", 46 | "xvfb": "^0.4.0" 47 | }, 48 | "repository": { 49 | "type": "git", 50 | "url": "https://github.com/zfcsoftware/puppeteer-real-browser" 51 | }, 52 | "readme": "README.md", 53 | "homepage": "https://github.com/zfcsoftware/puppeteer-real-browser" 54 | } 55 | -------------------------------------------------------------------------------- /test/cjs/test.js: -------------------------------------------------------------------------------- 1 | const test = require('node:test'); 2 | const assert = require('node:assert'); 3 | const { connect } = require('../../lib/cjs/index.js'); 4 | 5 | 6 | const realBrowserOption = { 7 | args: ["--start-maximized"], 8 | turnstile: true, 9 | headless: false, 10 | // disableXvfb: true, 11 | customConfig: {}, 12 | connectOption: { 13 | defaultViewport: null 14 | }, 15 | plugins: [] 16 | } 17 | 18 | // test('Puppeteer Extra Plugin', async () => { 19 | // /* 20 | // Run with: 21 | // npm i puppeteer-extra-plugin-click-and-wait 22 | // */ 23 | // realBrowserOption.plugins = [ 24 | // require('puppeteer-extra-plugin-click-and-wait')() 25 | // ] 26 | // const { page, browser } = await connect(realBrowserOption) 27 | // await page.goto("https://google.com", { waitUntil: "domcontentloaded" }) 28 | // await page.clickAndWaitForNavigation('body') 29 | // await browser.close() 30 | // }) 31 | 32 | test('DrissionPage Detector', async () => { 33 | const { page, browser } = await connect(realBrowserOption) 34 | await page.goto("https://web.archive.org/web/20240913054632/https://drissionpage.pages.dev/"); 35 | await page.realClick("#detector") 36 | let result = await page.evaluate(() => { return document.querySelector('#isBot span').textContent.includes("not") ? true : false }) 37 | await browser.close() 38 | assert.strictEqual(result, true, "DrissionPage Detector test failed!") 39 | }) 40 | 41 | test('Brotector, a webdriver detector', async () => { 42 | const { page, browser } = await connect(realBrowserOption) 43 | await page.goto("https://kaliiiiiiiiii.github.io/brotector/"); 44 | await new Promise(r => setTimeout(r, 3000)); 45 | let result = await page.evaluate(() => { return document.querySelector('#table-keys').getAttribute('bgcolor') }) 46 | await browser.close() 47 | assert.strictEqual(result === "darkgreen", true, "Brotector, a webdriver detector test failed!") 48 | }) 49 | 50 | test('Cloudflare WAF', async () => { 51 | const { page, browser } = await connect(realBrowserOption) 52 | await page.goto("https://nopecha.com/demo/cloudflare"); 53 | let verify = null 54 | let startDate = Date.now() 55 | while (!verify && (Date.now() - startDate) < 30000) { 56 | verify = await page.evaluate(() => { return document.querySelector('.link_row') ? true : null }).catch(() => null) 57 | await new Promise(r => setTimeout(r, 1000)); 58 | } 59 | await browser.close() 60 | assert.strictEqual(verify === true, true, "Cloudflare WAF test failed!") 61 | }) 62 | 63 | 64 | test('Cloudflare Turnstile', async () => { 65 | const { page, browser } = await connect(realBrowserOption) 66 | await page.goto("https://turnstile.zeroclover.io/"); 67 | await page.waitForSelector('[type="submit"]') 68 | let token = null 69 | let startDate = Date.now() 70 | while (!token && (Date.now() - startDate) < 30000) { 71 | token = await page.evaluate(() => { 72 | try { 73 | let item = document.querySelector('[name="cf-turnstile-response"]').value 74 | return item && item.length > 20 ? item : null 75 | } catch (e) { 76 | return null 77 | } 78 | }) 79 | await new Promise(r => setTimeout(r, 1000)); 80 | } 81 | await browser.close() 82 | // if (token !== null) console.log('Cloudflare Turnstile Token: ' + token); 83 | assert.strictEqual(token !== null, true, "Cloudflare turnstile test failed!") 84 | }) 85 | 86 | 87 | test('Fingerprint JS Bot Detector', async () => { 88 | const { page, browser } = await connect(realBrowserOption) 89 | await page.goto("https://fingerprint.com/products/bot-detection/"); 90 | await new Promise(r => setTimeout(r, 5000)); 91 | const detect = await page.evaluate(() => { 92 | return document.querySelector('.HeroSection-module--botSubTitle--2711e').textContent.includes("not") ? true : false 93 | }) 94 | await browser.close() 95 | assert.strictEqual(detect, true, "Fingerprint JS Bot Detector test failed!") 96 | }) 97 | 98 | 99 | // If you fail this test, your ip address probably has a high spam score. Please use a mobile or clean ip address. 100 | test('Datadome Bot Detector', async (t) => { 101 | const { page, browser } = await connect(realBrowserOption) 102 | await page.goto("https://antoinevastel.com/bots/datadome"); 103 | const check = await page.waitForSelector('nav #navbarCollapse').catch(() => null) 104 | await browser.close() 105 | assert.strictEqual(check ? true : false, true, "Datadome Bot Detector test failed! [This may also be because your ip address has a high spam score. Please try with a clean ip address.]") 106 | }) 107 | 108 | // If this test fails, please first check if you can access https://antcpt.com/score_detector/ 109 | test('Recaptcha V3 Score (hard)', async () => { 110 | const { page, browser } = await connect(realBrowserOption) 111 | await page.goto("https://antcpt.com/score_detector/"); 112 | await page.realClick("button") 113 | await new Promise(r => setTimeout(r, 5000)); 114 | const score = await page.evaluate(() => { 115 | return document.querySelector('big').textContent.replace(/[^0-9.]/g, '') 116 | }) 117 | await browser.close() 118 | // if (Number(score) >= 0.7) console.log('Recaptcha V3 Score: ' + score); 119 | assert.strictEqual(Number(score) >= 0.7, true, "(please first check if you can access https://antcpt.com/score_detector/.) Recaptcha V3 Score (hard) should be >=0.7. Score Result: " + score) 120 | }) -------------------------------------------------------------------------------- /test/esm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "type": "module", 4 | "version": "1.0.0", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "description": "" 13 | } 14 | -------------------------------------------------------------------------------- /test/esm/test.js: -------------------------------------------------------------------------------- 1 | import test from 'node:test'; 2 | import assert from 'node:assert'; 3 | import { connect } from '../../lib/esm/index.mjs'; 4 | 5 | const realBrowserOption = { 6 | args: ["--start-maximized"], 7 | turnstile: true, 8 | headless: false, 9 | // disableXvfb: true, 10 | // ignoreAllFlags:true, 11 | customConfig: {}, 12 | connectOption: { 13 | defaultViewport: null 14 | }, 15 | plugins: [] 16 | } 17 | 18 | 19 | // test('Puppeteer Extra Plugin', async () => { 20 | // /* 21 | // Run with: 22 | // npm i puppeteer-extra-plugin-click-and-wait 23 | // */ 24 | // const clickAndWait = await (await import('puppeteer-extra-plugin-click-and-wait')).default 25 | // realBrowserOption.plugins = [ 26 | // clickAndWait() 27 | // ] 28 | // const { page, browser } = await connect(realBrowserOption) 29 | // await page.goto("https://google.com", { waitUntil: "domcontentloaded" }) 30 | // await page.clickAndWaitForNavigation('body') 31 | // await browser.close() 32 | // }) 33 | 34 | test('DrissionPage Detector', async () => { 35 | const { page, browser } = await connect(realBrowserOption) 36 | await page.goto("https://web.archive.org/web/20240913054632/https://drissionpage.pages.dev/"); 37 | await page.realClick("#detector") 38 | let result = await page.evaluate(() => { return document.querySelector('#isBot span').textContent.includes("not") ? true : false }) 39 | await browser.close() 40 | assert.strictEqual(result, true, "DrissionPage Detector test failed!") 41 | }) 42 | 43 | test('Brotector, a webdriver detector', async () => { 44 | const { page, browser } = await connect(realBrowserOption) 45 | await page.goto("https://kaliiiiiiiiii.github.io/brotector/"); 46 | await new Promise(r => setTimeout(r, 3000)); 47 | let result = await page.evaluate(() => { return document.querySelector('#table-keys').getAttribute('bgcolor') }) 48 | await browser.close() 49 | assert.strictEqual(result === "darkgreen", true, "Brotector, a webdriver detector test failed!") 50 | }) 51 | 52 | test('Cloudflare WAF', async () => { 53 | const { page, browser } = await connect(realBrowserOption) 54 | await page.goto("https://nopecha.com/demo/cloudflare"); 55 | let verify = null 56 | let startDate = Date.now() 57 | while (!verify && (Date.now() - startDate) < 30000) { 58 | verify = await page.evaluate(() => { return document.querySelector('.link_row') ? true : null }).catch(() => null) 59 | await new Promise(r => setTimeout(r, 1000)); 60 | } 61 | await browser.close() 62 | assert.strictEqual(verify === true, true, "Cloudflare WAF test failed!") 63 | }) 64 | 65 | 66 | test('Cloudflare Turnstile', async () => { 67 | const { page, browser } = await connect(realBrowserOption) 68 | await page.goto("https://turnstile.zeroclover.io/"); 69 | await page.waitForSelector('[type="submit"]') 70 | let token = null 71 | let startDate = Date.now() 72 | while (!token && (Date.now() - startDate) < 30000) { 73 | token = await page.evaluate(() => { 74 | try { 75 | let item = document.querySelector('[name="cf-turnstile-response"]').value 76 | return item && item.length > 20 ? item : null 77 | } catch (e) { 78 | return null 79 | } 80 | }) 81 | await new Promise(r => setTimeout(r, 1000)); 82 | } 83 | await browser.close() 84 | // if (token !== null) console.log('Cloudflare Turnstile Token: ' + token); 85 | assert.strictEqual(token !== null, true, "Cloudflare turnstile test failed!") 86 | }) 87 | 88 | 89 | 90 | test('Fingerprint JS Bot Detector', async () => { 91 | const { page, browser } = await connect(realBrowserOption) 92 | await page.goto("https://fingerprint.com/products/bot-detection/"); 93 | await new Promise(r => setTimeout(r, 5000)); 94 | const detect = await page.evaluate(() => { 95 | return document.querySelector('.HeroSection-module--botSubTitle--2711e').textContent.includes("not") ? true : false 96 | }) 97 | await browser.close() 98 | assert.strictEqual(detect, true, "Fingerprint JS Bot Detector test failed!") 99 | }) 100 | 101 | 102 | // If you fail this test, your ip address probably has a high spam score. Please use a mobile or clean ip address. 103 | test('Datadome Bot Detector', async (t) => { 104 | const { page, browser } = await connect(realBrowserOption) 105 | await page.goto("https://antoinevastel.com/bots/datadome"); 106 | const check = await page.waitForSelector('nav #navbarCollapse').catch(() => null) 107 | await browser.close() 108 | assert.strictEqual(check ? true : false, true, "Datadome Bot Detector test failed! [This may also be because your ip address has a high spam score. Please try with a clean ip address.]") 109 | }) 110 | 111 | // If this test fails, please first check if you can access https://antcpt.com/score_detector/ 112 | test('Recaptcha V3 Score (hard)', async () => { 113 | const { page, browser } = await connect(realBrowserOption) 114 | await page.goto("https://antcpt.com/score_detector/"); 115 | await page.realClick("button") 116 | await new Promise(r => setTimeout(r, 5000)); 117 | const score = await page.evaluate(() => { 118 | return document.querySelector('big').textContent.replace(/[^0-9.]/g, '') 119 | }) 120 | await browser.close() 121 | // if (Number(score) >= 0.7) console.log('Recaptcha V3 Score: ' + score); 122 | assert.strictEqual(Number(score) >= 0.7, true, "(please first check if you can access https://antcpt.com/score_detector/.) Recaptcha V3 Score (hard) should be >=0.7. Score Result: " + score) 123 | }) -------------------------------------------------------------------------------- /typings.d.ts: -------------------------------------------------------------------------------- 1 | declare module "puppeteer-real-browser" { 2 | import type { Browser, Page } from "rebrowser-puppeteer-core"; 3 | import type { GhostCursor } from "ghost-cursor"; 4 | 5 | export function connect(options: Options): Promise; 6 | 7 | interface PageWithCursor extends Page { 8 | realClick: GhostCursor["click"]; 9 | realCursor: GhostCursor; 10 | } 11 | 12 | type ConnectResult = { 13 | browser: Browser; 14 | page: PageWithCursor; 15 | }; 16 | 17 | interface Options { 18 | args?: string[]; 19 | headless?: boolean; 20 | customConfig?: import("chrome-launcher").Options; 21 | proxy?: ProxyOptions; 22 | turnstile?: boolean; 23 | connectOption?: import("rebrowser-puppeteer-core").ConnectOptions; 24 | disableXvfb?: boolean; 25 | plugins?: import("puppeteer-extra").PuppeteerExtraPlugin[]; 26 | ignoreAllFlags?: boolean; 27 | } 28 | 29 | interface ProxyOptions { 30 | host: string; 31 | port: number; 32 | username?: string; 33 | password?: string; 34 | } 35 | } 36 | --------------------------------------------------------------------------------